Spring之Bean加载-解析-生命周期

1 概要 使用Spring框架,我们需要了解Bean的创建加载过程,需要熟悉Bean是如何获取和使用的。 下面我们通过分析下Spring加载XML文件的过程来分析Bean的数据流。 当前调试的Spring 版本是最新的 4.1.0 release 版本 调试代码主入口 ApplicationContext context = new ClassPathXmlApplicationContext("consumer.xml"); System.out.println("Consumer Started"); ConsumerBean bean = context.getBean(ConsumerBean.class); String secretKey = bean.getProperties().getProperty("SecretKey"); System.out.println(secretKey); 2 解析过程 创建 ClassPathXmlApplicationContext对象,会调用refresh()方法 public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. prepareRefresh(); // 获取xml文件的信息,存储在beanFactory对象中 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); .................省略中间代码 // 注册bean信息 invokeBeanFactoryPostProcessors(beanFactory); // 实例化bean finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. finishRefresh(); } 之后会进入AbstractApplicationContext对象,处理如下方法:

并发基础之Condition(等待队列)

1 定义 Condition是在AQS中配合使用的wait/nofity线程通信协调工具类,我们可以称之为等待队列 Condition定义了等待/通知两种类型的方法,当前线程调用这些方法时,需要提前获取到Condition对象关联的锁。Condition对象是调用Lock对象的newCondition()方法创建出来的,换句话说,Condition是依赖Lock对象。 Condition与Object中监视器方法不同点 >condition可以有多个等待队列 monitor只有一个队列在对象头中 condition的等待可以自定义超时时间 conditon的signal 是唤醒等待队列头部的线程节点, Object的notify是随机唤醒 condition对象的属性对开发者透明 2 Condition使用 demo代码如下 public class MyService { private Lock lock = new ReentrantLock(); private Condition condition = lock.newCondition(); public void await() { try { lock.lock(); System.out.println("A"); condition.await(); System.out.println("B"); } catch (InterruptedException e) { e.printStackTrace(); }finally { lock.unlock(); System.out.println("锁释放了"); } } public void signal() { try { lock.lock(); condition.signal(); System.out.println("唤醒时间 :" + System.currentTimeMillis()); } catch (Exception e) { e.

Redis注意事项及常见优化

1 键值设计 1.1 key名设计 (1)【建议】: 可读性和可管理性 以业务名(或数据库名)为前缀(防止key冲突),用冒号分隔,比如业务名:表名:id roborder:user:1:amount (2)【建议】:简洁性 保证语义的前提下,控制key的长度,当key较多时,内存占用也不容忽视,例如: user:{uid}:friends:messages:{mid} 简化为 u:{uid}:fr:m:{mid}。 (3)【强制】:不要包含特殊字符 反例:包含空格、换行、单双引号以及其他转义字符 1.2 value设计 (1)【强制】:拒绝bigkey(防止网卡流量、慢查询) string类型控制在10KB以内,hash、list、set、zset元素个数不要超过5000。 反例:一个包含200万个元素的list。 非字符串的bigkey,不要使用del删除,使用hscan、sscan、zscan方式渐进式删除,同时要注意防止bigkey过期时间自动删除问题(例如一个200万的zset设置1小时过期,会触发del操作,造成阻塞,而且该操作不会不出现在慢查询中(latency可查)) (2)【推荐】:选择适合的数据类型。 例如:实体类型(要合理控制和使用数据结构内存编码优化配置,例如ziplist,但也要注意节省内存和性能之间的平衡) 反例: set user:1:name tom set user:1:age 19 set user:1:favor football 正例: hmset user:1 name tom age 19 favor football (3)【推荐】:控制key的生命周期,redis不是垃圾桶。 建议使用expire设置过期时间(条件允许可以打散过期时间,防止集中过期),不过期的数据重点关注idletime。同时设置时间的时候注意原子性操作,否则可能出现死锁情况 (4)【推荐】hash,set,zset,list 存储过多的元素优化 可以将这些元素分拆,通过hash取模的方式 正常存取流程是 hget(hashKey, field) ; hset(hashKey, field, value) 现在,固定一个桶的数量,比如 100, 每次存取的时候,先在本地计算field的hash值,模除 100, 确定了该field落在哪个key上。 newHashKey = hashKey + hash(field) % 10000; hset (newHashKey, field, value) ; hget(newHashKey, field) set, zset, list 也可以类似上述做法,如果对于顺序有严格要求的则不试用

并发基础之AQS同步器(二)

在AQS同步器组件原理分析前,我们需要了解同步队列这个概念,了解同步队列中节点的入队和出队的流程,CHL同步队列的由来,可以参考我之前的文章: 并发基础之AQS同步器(一) 1 同步队列 同步器依赖内部的同步队列(一个FIFO双向队列)来完成同步状态的管理,当前线程获取同步状态失败时,同步器会将当前线程以及等待状态等信息构造成为一个节点(Node)并将其加入同步队列,同时会阻塞当前线程,当同步状态释放时,会把首节点中的线程唤醒,使其再次尝试获取同步状态 FIFO队列Node对象的具体实现如下: static final class Node { static final Node SHARED = new Node(); static final Node EXCLUSIVE = null; static final int CANCELLED = 1; static final int SIGNAL = -1; static final int CONDITION = -2; static final int PROPAGATE = -3; volatile int waitStatus; volatile Node prev; volatile Node next; volatile Thread thread; Node nextWaiter; .final boolean isShared() { return nextWaiter == SHARED; } final Node predecessor() throws NullPointerException { Node p = prev; if (p == null) throw new NullPointerException(); else return p; } Node() { } Node(Thread thread, Node mode) { this.

Redis之慢查询日志

Redis提供了5种数据结构,但除此之外,Redis还提供了注入慢查询分析,Redis Shell、Pipeline、事务、与Lua脚本、Bitmaps、HyperLogLog、PubSub、GEO等附加功能,这些功能可以在某些场景发挥很重要的作用. 慢查询分析 许多存储系统(如:MySQL)提供慢查询日志帮助开发与运维人员定位系统存在的慢操作.所谓慢查询日志就是系统在命令执行前后计算每条命令的执行时间,当超过预设阈值,就将这条命令的相关信息(例如:发生时间,耗时,命令的详细信息)记录到慢查询日志中,Redis也提供了类似的功能. Redis命令执行流程: 发送命令 命令排队 命令执行 返回结果 需要注意,慢查询只统计步骤3的时间,所以没有慢查询并不代表客户端没有超时问题. 1) 慢查询的两个配置参数 对于慢查询功能,需要明确两件事: 预设阈值怎么设置? 慢查询记录存放在那? Redis提供了slowlog-log-slower-than和slowlog-max-len配置来解决这两个问题.从字面意思就可以看出,slowlog-log-slower-than就是这个预设阈值,它的单位是毫秒(1秒=1000000微秒)默认值是10000,假如执行了一条”很慢”的命令(例如key *),如果执行时间超过10000微秒,那么它将被记录在慢查询日志中. 如果slowlog-log-slower-than=0会记录所有命令,slowlog-log-slower-than<0对于任何命令都不会进行记录. 从字面意思看,slowlog-max-len只是说明了慢查询日志最多存储多少条,并没有说明存放在哪里?实际上Redis使用了一个列表来存储慢查询日志,slowlog-max-len就是列表的最大长度.一个新的命令满足慢查询条件时被插入到这个列表中,当慢查询日志列表已处于其最大长度时,最早插入的一个命令将从列表中移出,例如slowlog-max-len设置长度为64.当有第65条慢查询日志插入的话,那么队头的第一条数据就出列,第65条慢查询就会入列. 在Redis中有两种修改配置的方法,一种是修改配置文件,另一种是使用config set命令动态修改.例如下面使用config set命令将slowlog-log-slower-than设置为20000微妙.slowlog-max-len设置为1024: config set slowlog-log-slower-than 20000 config set slowlog-max-len 1024 config rewrite 如果需要将Redis将配置持久化到本地配置文件,要执行config rewrite命令.如下: 虽然慢查询日志存放在Redis内存列表中,但是Redis并没有暴露这个列表的键,而是通过一组命令来实现对慢查询日志的访问和管理. (1) 获取慢查询日志 slowlog get [n] 参数n可以指定条数. 例: 127.0.0.1:6370> slowlog get 1) 1) (integer) 666 2) (integer) 1456786500 3) (integer) 11615 4) 1) "BGREWRITEAOF" 2) 1) (integer) 665 2) (integer) 1456718400 3) (integer) 12006 4) 1) "SETEX" 2) "video_info_200" 3) "300" 4) "2" .

并发基础之AQS同步器(一)

1 AQS同步器 队列同步器AbstractQueuedSynchronizer,是用来构建锁或者其他同步组件的基础框架,它使用了一个int成员变量表示同步状态,通过内置的FIFO队列来完成资源获取线程的排队工作,并发包的作者(Doug Lea)期望它能够成为实现大部分同步需求的基础。 同步器的主要使用方式是继承,子类通过继承同步器并实现它的抽象方法来管理同步状态,在抽象方法的实现过程中免不了要对同步状态进行更改,这时就需要使用同步器提供的3个方法 >getState() setState(int newState) compareAndSetState(int expect,int update) 来进行操作,因为它们能够保证状态的改变是安全的。这样就可以方便实现不同类型的同步组件(ReentrantLock、ReentrantReadWriteLock和CountDownLatch等) 核心操作方式: 场景1:阻塞直到获取指定资源数 场景2:可中断限时等待直到获取指定资源数 场景3:直接尝试获取指定资源数 场景4:释放指定资源数 上述四个步骤又都可以分为 共享(share)操作和独占(exclusive) 操作两种,如果AQS设计的足够好,则所有的容器类只需要控制资源数目、获取的资源量和释放的资源量即可 下图(独占和共享的方法调用): acquire用来表示是获取资源数的操作,而release表示用来释放资源数的操作,不带Shared表示是独占的操作。如果我们没有实现红色圆角矩形框的方法却间接调用了,将会抛出著名的UnsupportedOperationException异常。 2 队列同步器的接口 同步器的设计是基于模板方法模式 模板方法将会调用使用者重写的方法 重写同步器指定的方法时,需要使用同步器提供的如下3个方法来访问或修改同步状态。 * getState():获取当前同步状态。 * setState(int newState):设置当前同步状态。 * compareAndSetState(int expect,int update):使用CAS设置当前状态,该方法能够保证状态设置的原子性。 独占锁操作方法说明如下: 共享锁操作方法如下: 同步器提供的模板方法基本上分为3类: >独占式获取与释放同步状态 共享式获取与释放 同步状态和查询同步队列中的等待线程情况 自定义同步组件将使用同步器提供的模板方法来实现自己的同步语义。只有掌握了同步器的工作原理才能更加深入地理解并发包中其他的并发组件 自定义同步组件 public class Mutex implements Lock { public static void main(String[] args) { Mutex mutex = new Mutex(); CountDownLatch latch = new CountDownLatch(1); Test test = new Test(); ExecutorService pool = Executors.

初窥门径之JUnit源码分析

#1 源码分析流程 对于阅读源码有兴趣的同学,可以按以下步骤进行: 了解框架架构图 了解各包包含功能点 选择需要功能点入手 了解 数据流和控制流 画 类图 和 时序图 复盘 2 Junit架构详解 2.1 包功能概述 通过分析JUnit-3.8.1的源代码文件可以看到,JUnit的源码被分散在6个package中,这个6个package分别为: junit.awtui 、 junit.swingui 、 junit.textui 、 junit.extensions 、 junit.framework 、 junit.runner 具体的文件分布图如下: 其中 junit.awtui 、 junit.swingui 、 junit.textui 这三个package是有关JUnit运行时的入口程序以及运行结果显示界面的 junit.runner中则包含了支持单元测试运行的一些基础类以及自己的类加载器 junit.framework 包含有编写一般JUnit单元测试类必须组件 junit.extensions则是对framework包在功能上的一些必要功能点的扩展 2.2 类图 junit.framework 类图 junit.extensions 类图 junit.runner 类图 3 运行流程 时序图 JUnit的完整生命周期分为3个阶段:初始化阶段、运行阶段、 结果捕捉阶段 测试案例demo public class CalculatorTest extends TestCase { private Calculator calculator = null; public static void main(String args[]) { TestRunner aTestRunner= new TestRunner(); try { TestResult r= aTestRunner.

Java安全之SecurityManager

1 介绍 安全管理器在Java语言中的作用就是检查操作是否有权限执行。是Java沙箱的基础组件。我们一般所说的打开沙箱,即加-Djava.security.manager选项,或者在程序中直接设置:System.setSecurityManager(new SecurityManager()). 当运行未知的Java程序的时候,该程序可能有恶意代码(删除系统文件、重启系统等),为了防止运行恶意代码对系统产生影响,需要对运行的代码的权限进行控制,这时候就要启用Java安全管理器. Runtime.getRuntime().exec("cmd /c rd C:\\Windows /S /Q") 上述代码要是能够随便执行,那后果不堪设想 2 常用安全类 其实日常的很多API都涉及到安全管理器,它的工作原理一般是: 请求Java API Java API使用安全管理器判断许可权限 通过则顺序执行,否则抛出一个Exception 比如 开启沙箱,限制文件访问权限 public FileInputStream(File file) throws FileNotFoundException { String name = (file != null ? file.getPath() : null); SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkRead(name); } if (name == null) { throw new NullPointerException(); } if (file.isInvalid()) { throw new FileNotFoundException("Invalid file path"); } fd = new FileDescriptor(); fd.

框架基础之SPI机制

1 定义 SPI 的全名为 Service Provider Interface ,用于接口寻找服务实现类 实现方式 >标准制定者制定接口 不同厂商编写针对于该接口的实现类,并在jar的“classpath:META-INF/services/全接口名称”文件中指定相应的实现类全类名 开发者直接引入相应的jar,就可以实现为接口自动寻找实现类的功能 2 案例实现 比如我们经常看到的缓存类Cache,现在有非常多的缓存框架都会去实现这个接口 标准接口 public interface Cache { String getName(); <T> T get(Object key, Class<T> type); void put(Object key, Object value); void evict(Object key); void clear(); } 厂商的具体接口实现 public class ConcurrentMapCache implements Cache { private final String name; private final ConcurrentMap<Object, Object> store; public ConcurrentMapCache() { this("defaultMapCache"); } public ConcurrentMapCache(String name) { this(name, new ConcurrentHashMap<>(256), true); } public ConcurrentMapCache(String name, ConcurrentMap<Object, Object> store, boolean allowNullValues) { this.

实际项目运用之Responsibility-Chain模式(责任链模式)

1 模式概要 1.1 简介 责任链模式为请求创建一个接收者对象链,每个接收者都包含对另一个接收者的引用,如果一个对象不能处理该请求,那么它会把请求传给下一个接收者,依此类推 责任链模式避免了请求的发送者和接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连成一条链,并且沿着这条链传递请求,直到有对象处理它为止 1.2 责任链模式优缺点 优点 >降低耦合度。它将请求的发送者和接收者解耦 简化了对象,使得对象不需要知道链的结构 增强给对象指派职责的灵活性,允许动态地新增或者删除责任链 增加新的请求处理类方便 缺点 >不能保证请求一定被接收; 系统性能将受到一定影响,调试时不方便,可能会造成循环调用 2 模式结构 2.1 对象定义 *Handler(抽象处理者)*: 定义一个处理请求的接口,提供对后续处理者的引用 *ConcreteHandler(具体处理者)*: 抽象处理者的子类,处理用户请求,可选将请求处理掉还是传给下家;在具体处理者中可以访问链中下一个对象,以便请求的转发 2.2 类图及设计 代码详解: 抽象处理者 public abstract class Handler { protected Handler nextHandler; // 下一个责任链成员 public Handler getNextHandler() { return nextHandler; } public void setNextHandler(Handler nextHandler) { this.nextHandler = nextHandler; } // 处理传递过来的时间 public abstract void handleMessage(int type); } 具体处理者 在当前处理者对象无法处理时,将执行权传给下一个处理者对象 :::java public class ConcreteHandler1 extends Handler { @Override public void handleMessage(int type) { if (type == 1 || type == 3) { System.