Spring现在已经是一个庞大的框架集合。Spring 核心库的官方文档是时刻都值得参考的。
分为以下几部分:
Spring当中最核心的两个类
Spring的配置文件读取是通过ClassPathResource进行封装的。Java将不同来源的资源抽象成URL,通过注册不同的handler(URLStreamHandler)来处理不同来源的资源读取逻辑,但是没有默认定义相对于ClassPath或ServletContext等资源的handler,Spring实现了自己的抽象结构:Resource接口来封装底层资源。
传入resource参数做封装,通过SAX读取XML文件的方式来准备InputSource对象,最后将准备的数据通过参数传入真正核心处理部分doLoadBeanDefinitions(inputSource,encodedResource.getResource())。
核心处理包含:
两种验证模式 DTD (Document Type Definition) 与 XSD (XML Scheme Definition)
提取元素中的id和name属性
进一步解析其他所有属性并统一封装到GenericBeanDefinition类型的实例中。
如果检测到bean没有指定BeanName,使用默认规则生成一个
将获取到的信息封装到BeanDefinitionHolder实例中
创建用于属性承载的BeanDefinition
Spring通过BeanDefinition将配置文件中的bean配置信息转换为容器的内部表示,并将这些BeanDefinition注册到BeanDefinitionRegistry中,以map形式保存。
解析各种属性
解析子元素meta
解析子元素lookup-method
解析子元素 replaced-method
解析子元素constructor-arg
解析子元素property
解析子元素qualifier
就是processBeanDefinition函数中的BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry())。
注册分为两部分:通过beanName注册以及通过别名注册。
getReaderContext().freComponentRegistered(new BeadComponentDefinition(bdHolder)),研发可以监听该事件,Spring中没有做任何逻辑处理。
加载过程所涉及的步骤大致如下:
一般情况下Spring通过反射机制利用bean的class属性指定实现类来实例化bean。Spring为此提供了org.springframework.bean.factory.FactoryBean的工厂类接口,并且提供了70多个FactoryBean的实现。当配置文件中bean的class属性配置实现类是FactoryBean,getBean方法返回的不是FactoryBean本身,而是FactoryBean#getObject方法。在getBean方法的beanName参数前加上“&”前缀可以获得FactoryBean实例。
存储bean的不同map:
在getBean方法中,getObjectForBeanInstance是高频使用的。在调用Factorybean之后,并没有直接返回对象,而是调用了postProcessObjectFactoryBean方法。在实际开发过程中可以针对此特性设计自己的业务逻辑。
创建bean的具体步骤:
bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
if (bean != null) {
bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
}
BeanPostProcessor会调用前置和后置方法,这里可以加入我们自定义的处理方式。
AOP功能就是基于这里的短路操作判断的。
4. 创建bean
在Spring中将循环依赖的处理分成了3种情况。
实例化策略
如果判断beanDefinition.getMethodOverrides()为空,用户没有使用replace或者lookup的配置方法,就可以直接使用反射。否则就要使用动态代理。
使用populateBean这个函数进行属性填充
bean配置时有一个init-method的属性,作用是在实例化前调用。
Spring提供一些Aware接口,aware翻译过来是知道的,已感知的,意识到的
就是通过实现对应接口的setter方法获取Spring提供的属性bean。
Spring中可以通过PostProcessor来更改或扩充,大部分都是继承自BeanPostProcessor。
销毁方法的扩展入口,除了destroy-method外,还可以注册后处理器DestructionAwareBeanPostProcessor来统一处理。
ApplicationContext和BeanFactory都是用于加载Bean,但是更优先。
ClassPathXmlApplicationContext初始化的步骤:
在prepareBeanFactory方法中进行了功能扩展
#{...}
作为定界符,StandardBeanExpressionResolver注册语言解析器。可以对bean的定义(配置元数据)进行处理。Spring IoC容器允许BeanFactoryPostProcessor在容器实际实例化任何其他bean之前读取配置元数据,并可以修改它。可以通过设置order属性控制多个BeanFactoryPostProcessor的执行顺序。
其作用域范围是容器级的。典型应用:PropertyPlaceholderConfigurer
对于BeanFactoryPostProcessor的处理主要分两种情况:对于BeanDefinitionRegistry类的特殊处理,和对普通BeanFactoryPostProcessor进行处理。
对于BeanDefinitionRegistry处理类的处理包括:
继承ApplicationEvent定义监听事件,实现ApplicationListener接口定义监听器,applicationContext.pulishEvent(event)发布事件。
没有自定义事件广播器,默认使用SimpleApplicationEventMulticaster。当产生事件时,调用multicastEvent来广播事件,遍历监听器,并使用监听器中的onApplicationEvent方法来进行监听器的处理。
BeanFactory的初始化工作,其中包括ConversionService的设置,配置冻结以及非延迟加载bean的初始化工作
Spring采用@AspectJ注解对POJO进行标注,
标签完成了对AnnotationAwareAspectJAutoProxyCreator类自动注册,Spring加载这个Bean时会在实例化前调用其PostProcessorAfterInitialization方法。这是直接调用了父类AbstractAutoProxyCreator中的方法。
真正创建代理的代码从 getAdvicesAndAdvisorsForBean 开始,包含两个步骤:
普通增强器通过getAdvisor方法实现,获取切点的注解以及根据注解信息生成增强。所有的增强都由Advisor的实现类 InstantiationModelAwarePointcutAdvisorImpl统一封装。根据注解中的信息初始化对应的增强器就是在instantiateAdvice函数中实现。Spring根据不同的注解生成不同的增强器,例如AtBefore对应AspectJMethodBeforeAdvice,而在AspectJMethodBeforeAdvice中完成了增强方法的逻辑。常见的增强器实现:
如果寻找的增强器不为空且又配置了增强延迟初始化,需要在首位加入同步实例化增强器 SyntheticInstantiationAdvisor
主要用于引介增强的注解形式的实现,实现方式和普通增强类似,不过使用DeclareParentsAdvisor对功能进行封装。
要找出满足配置的通配符的增强器。具体实现在findAdvisorThatCanApply中
获取所有bean的增强器后,可以进行代理创建
Spring委托ProxyFactory处理代理类的创建及处理,createProxy中对ProxyFactory进行了初始化操作,初始化操作包括:
通过ProxyFactory提供的addAdvisor方法直接将增强器置入代理创建工厂中。拦截器封装为增强器需要一定逻辑。如果MethodInterceptor类型则使用DefaultPointcutAdvisor封装。
Spring会自动在JDK动态代理和CGLIB之间转换
JDKProxy需要创建自定义的InvocationHandler,Spring当中使用JDKDynamicAopProxy实现InvocationHandler接口,invoke方法主要工作就是创建了一个拦截器链,使用ReflectiveMethodInvocation类进行了链的封装,在ReflectiveMethodInvocation类的process方法中实现了拦截器的逐一调用。
完成CGLIB代理的类是委托给Cglib2AopProxy类实现
加载时织入(Load-Time Weaving,LTW),指的是在虚拟机载入字节码文件时动态织入AspectJ切面。
标签加入全局配置文件。
AOP的静态大力主要是在虚拟机启动时通过改变目标对象字节码的方式来完成对目标对象的增强。比动态代理有更高的效率,因为在启动时便完成了字节码增强而不需要动态创建代理类并代理目标对象的步骤。
java在1.5引入java.lang.instrument,可以实现一个Java agent,通过此agent来修改类的字节码改变一个类。可以使用JBoss的javassist改变类的字节码。
Spring中的静态AOP使用到了AspectJ,AspectJ又是在instrument基础上进行封装。