本来觉得,Spring 经过解析 bean 的配置,生成并注册 bean defintions 的过程不太复杂,比较简单,不用单独开辟一篇博文来说述;可是当在分析前面两个章节有关 @Autowired、@Component、@Service 注解的注入机制的时候,发现,若是没有对有关 bean defintions 的解析和注册机制完全弄明白,则很难弄清楚 annotation 在 Spring 容器中的底层运行机制;因此,本篇博文做者将试图去弄清楚 Spring 容器内部是如何去解析 bean 配置并生成和注册 bean definitions 的相关主流程;java
备注,本文是做者的原创做品,转载请注明出处。node
➥ bean definitions 是什么?spring
其实很简单,就是 Java 中的 POJO,用来描述 bean 配置中的 element 元素的,好比,咱们有以下的一个简单的配置编程
beans.xml缓存
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:component-scan base-package="org.shangyang" /> <bean name="jane" class="org.shangyang.spring.container.Person"> <property name="name" value="Jane Doe"/> </bean> </beans>
能够看到,上面有三个 elementapp
在配置文件 beans.xml 被 Spring 解析的过程当中,每个 element 将会被解析为一个 bean definition 对象缓存在 Spring 容器中;ide
➥ 须要被描述为 bean definitions 的配置对象主要分为以下几大类,源码分析
➥ 最开始个人确是这么认识 bean definitions 的,可是当我分析完有关 bean definitions 的相关逻辑和源码之后,对其认识有了升华,参考写在最后;post
最好的分析源码的方式,就是经过高屋建瓴,逐个击破的方式;首先经过流程图得到它的蓝图(顶层设计图),而后再根据蓝图上的点逐个击破;最后才能达到融会贯通,成竹在胸的境界;因此,这里做者用这样的方式带你深刻剖析 Spring 容器里面的核心点,以及相关主流程究竟是如何运做的。测试
为了一次性把上述源码分析所描述有的状况阐述清楚,咱们继续使用 Spring Core Container 源码分析六:@Service 中使用的测试用例;惟一作的修改是,再使用一个特殊的 element xmlns:p 来配置 john,这样能够进一步去调试自定义 Spring 配置标签是如何实现的;
beans.xml
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:component-scan base-package="org.shangyang" /> <bean name="john" class="org.shangyang.spring.container.Person" p:name="John Doe" p:spouse-ref="jane"/> <bean name="jane" class="org.shangyang.spring.container.Person"> <property name="name" value="Jane Doe"/> </bean> <bean name="niba" class="org.shangyang.spring.container.Dog"> <property name="name" value="Niba" /> </bean> </beans>
整个流程是从解析 bean definitions 流程开始的,对应的入口是主流程的 _step 1.1.1.2 obtainFreshBeanFactory_;
而后经过 XmlBeanDefinitionReader 解析 Spring XML 配置文件
根据用户指定的 XML 文件路径 location,进行解析而且获得 Resource[] 对象,具体参考 step 1.1.3.3.1.1 getResource(location) 步骤;这里,对其如何经过 location 获得 Resource[] 对象作进一步分析,看源码,
PathMatchingResourcePatternResolver.java
public Resource[] getResources(String locationPattern) throws IOException { Assert.notNull(locationPattern, "Location pattern must not be null"); if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) { // a class path resource (multiple resources for same name possible) if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) { // a class path resource pattern return findPathMatchingResources(locationPattern); } else { // all class path resources with the given name return findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length())); } } else { // Only look for a pattern after a prefix here // (to not get fooled by a pattern symbol in a strange prefix). int prefixEnd = locationPattern.indexOf(":") + 1; if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) { // a file pattern return findPathMatchingResources(locationPattern); } else { // a single resource with the given name return new Resource[] {getResourceLoader().getResource(locationPattern)}; } } }
这里的解析过程主要分为两种状况进行解析,一种是前缀是 classpath: 的状况,一种是普通的状况,正如咱们当前所使用的测试用例的状况,既是 new ClassPathXmlApplicationContext("beans.xml") 的状况,这里不打算在这里继续深挖;
当完成上述三个步骤之后,将进入 register bean definitions process 流程
➥ 首先,重要的两件事情是,
从 document 对象中得到了 Root 实例 root_,见 _step 1.2
看一个 root 元素,长什么样的
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> </beans>
就是一个 xml 配置文件中的最顶层元素 <beans/>
➥ 后续,当前面的工做准备好了之后,来看看是如何解析 element 的?
首先,判断 root 元素的 namespace 对应的是否是 default namespace,若不是,将进入 _step 1.3.3.3: parse custom element_;这里咱们关注常规流程,既是当 root 元素的 namespace 是 default namespace 的流程;
遍历 root 元素下的全部 element,
能够看到,该流程中包含四个子流程,依次处理不一样的 element 元素的状况,其它三种都是比较特殊的状况,咱们这里,主要关注“解析 <bean/>" 元素的流程”
这里,为了可以尽可能的展现出解析 <bean/> 元素的流程中的逻辑,我将使用一个比较特殊的 <bean/> 来梳理此部分的流程;
<bean name="john" class="com.example.Person" p:name="John Doe" p:spouse-ref="jane"/>
该 <bean/> 元素使用了 namespace xmlns:p="http://www.springframework.org/schema/p"
➥ 首先,经过 BeanDefintionParserDelegate 对象解析该 element,获得一个 BeanDefinitionHolder 对象 bdHolder 实例;该解析过程当中会依次去解析 bean id, bean name, 以及相关的 scope, init, autowired model 等等属性;见 step 1.1
➥ 其次,对 bean definition 进行相关的修饰操做,见 step 1.2
常规步骤
attribute node 的修饰过程
假设,咱们当前的 attribute node 为 _p:spouse-ref="jane"_,看看该属性是如何被解析的,
RuntimeBeanReference
对象实例,表示未来在解析该 property value 为 Java Object 的时候,须要去初始化其引用的 bean 实例 _jane_,而后注入到当前的 property value 中;➥ 最后,注册 bean definition;
见 step 1.3.2 register.registerBeanDefinition(beanName, beanDefinition)_,_register 就是当前的 bean factory 实例,经过将 bean name 和 bean definition 以键值对的方式在当前的 bean factory 中进行注册;这样,咱们就能够经过 bean 的名字,获得其对应的 bean definition 对象了;
➥ 写在该小节最后,
咱们也能够自定义某个 element 或者 element attribute,而且定义与之相关的 namespace 和 namespace handler,这样,就可使得 Spring 容器解析自定义的元素;相似于 dubbo 配置中所使用的 <dubbo /> 自定义元素那样;
此步骤对应 register bean definitions process 步骤中的 step 1.3.3.2
该小节我将试图使用一个经常使用的 custom element: <context:component-scan/> 来梳理整个流程;
继续 parse custom element process 章节中所使用到的例子,<context:component-scan/> 来分析该流程,
➥ 在开始分析以前,看看 component-scan 元素长什么样,
注意,_component-scan_ element 自己包含 annotation-config attribute;
➥ 流程分析
首先,根据 element name: component-scan 找到对应的 BeanDefinitionParser,在 ContextNamespaceHandler 初始化的时候,便初始化设置好 8 对内置的 element name 与 parsers 的键值对;这里,根据名字 component-scan 找到对应的 parser ComponentScanBeanDefinitionParser 对象;
其次,使用 ComponentScanBeanDefinitionParser 对象开始解析工做,
这里主要介绍上一个小节中 #2 步骤中所提到的 do scan 流程步骤,对应 parse element by ContextNamespaceHandler 流程图中的 _step 1.2.3 scanner.doScan_;
➥ 先来看看 step 1.2.3.1 findCandidateComponent(basePackage)
ClassPathScanningCandidateComponentProvider.java (已删除大量不相干代码)
public Set<BeanDefinition> findCandidateComponents(String basePackage) { Set<BeanDefinition> candidates = new LinkedHashSet<BeanDefinition>(); try { String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + resolveBasePackage(basePackage) + '/' + this.resourcePattern; //1. 从当前用户自定的 classpath 子路径中,经过 regex 查询到全部的所匹配的 resources;要特别注意的是, // 这里为何不直接经过 Class Loader 去获取 classes 来进行判断? 由于这样的话就至关因而加载了 Class Type,而 Class Type 的加载过程是经过 Spring 容器严格控制的,是不容许随随便便加载的 // 因此,取而代之,使用一个 File Resource 去读取相关的字节码,从字节码中去解析........ Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath); boolean traceEnabled = logger.isTraceEnabled(); boolean debugEnabled = logger.isDebugEnabled(); //2. 依次遍历用户定义的 bean Class 对象 for (Resource resource : resources) { if (traceEnabled) { logger.trace("Scanning " + resource); } if (resource.isReadable()) { try { // 将从字节码中获取到的相关 annotation(@Service) 以及 FileSystemResource 对象保存在 metadataReader 当中; MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource); if (isCandidateComponent(metadataReader)) { ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader); sbd.setResource(resource); sbd.setSource(resource); if (isCandidateComponent(sbd)) { candidates.add(sbd); } ... } ... } ... } } } catch (IOException ex) { throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex); } return candidates; }
代码第 10 行
Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);
这一步经过递归搜索 base package 目录下的全部 .class 文件,并将其字节码封装成 Resource[] 对象;上面的注释解释得很是清楚了,这里封装的是 .class 文件的字节码,而非 class type;除了注解中所描述的,这里再引伸说明下,这里为何不直接加载其 Class Type 还有一个缘由就是当 Spring 在加载 Class Type 的时候,颇有可能在该 Class Type 上配置了 AOP,经过 ASM 字节码技术去修改原有的字节码之后,再加入 Class Loader 中;因此,之类不能直接去解析 Class Type,而只能经过字节码的方式去解析;
这一步一样告诫
咱们,在使用 Spring 容器来开发应用的时候,开发者不要随随便便的自行加载 Class Type 到容器中
,由于有可能在加载 Class Type 以前须要经过 Spring 容器的 ASM AOP 进行字节码的修改之后再加载;
代码第 23 行
MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
解析当前的 .class 字节码,解析出对应的 annotation,好比 @Service,并将其协同 FileSystemResource 对象一同保存到 metadataReader 对象中;
代码第 24 行
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException { for (TypeFilter tf : this.excludeFilters) { if (tf.match(metadataReader, this.metadataReaderFactory)) { return false; } } for (TypeFilter tf : this.includeFilters) { // includedFilters 包含三类 annotation,1. @Component 2. @ManagedBean 3. @Named if (tf.match(metadataReader, this.metadataReaderFactory)) { return isConditionMatch(metadataReader); } } return false; }
既是从当前的 metadataReader 中去判断是否存在 1. @Component 2. @ManagedBean 3. @Named 三种注解中的一种,若是是,则进入下面的流程
代码 25 - 29 行,将符合 #3 标准的 annotation 封装为 ScannedGenericBeanDefinition annotation-bean-definition,并加入 candidates 返回
if (isCandidateComponent(metadataReader)) { ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader); sbd.setResource(resource); sbd.setSource(resource); if (isCandidateComponent(sbd)) { candidates.add(sbd); } ... }
➥ 依次处理并注册返回的 candidates
该步骤从流程图 parse element by ContextNamespaceHandler 中的 step 1.2.3.2 开始,主要作了以下几件事情,
经过 AnnotationBeanNameGenerator 生成 bean name,由于经过 @Component、@Service 注解的方式注入的 bean 每每没有配置 bean name,因此每每须要经过程序的方式自行生成相应的 bean name,看看内部的源码,如何生成 bean name 的,
/**
*/
@Override
public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
if (definition instanceof AnnotatedBeanDefinition) { String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition); // 处理诸如 @Service("dogService") 的状况 if (StringUtils.hasText(beanName)) { // Explicit bean name found. return beanName; } } // Fallback: generate a unique default bean name. 里面的实现逻辑就是经过将 Class Name 的首字母大写编程小写,而后返回; return buildDefaultBeanName(definition, registry);
}
一般状况下,是将类名的首字母进行小写并返回;对应 _step 1.2.2.3.3_ 3. 设置 annotation bean definition 的默认值,参考 _step 1.2.4_ 4. 设置 scoped proxy 到当前的 annotation bean definition 5. 最后,将 annotation bean definition 注册到当前的 bean factory ###### 注册 post-processor-bean-definitions 该步骤从流程图 [parse element by ContextNamespaceHandler](#parse-element-by-ContextNamespaceHandler) 的 _step 1.2.4.2 registerAnnotationConfigProcessors_ 开始,将会依次注册由以下 post-processor class 对象所对应的 post-processor-bean-definitions, + ConfigurationClassPostProcessor.class + AutowiredAnnotationBeanPostProcessor.class + RequiredAnnotationBeanPostProcessor.class + CommonAnnotationBeanPostProcessor.class + 经过 PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME 发射获得的 class + EventListenerMethodProcessor.class + DefaultEventListenerFactory.class 注意,这里都是经过 Class 对象注册的,并不是注册的实例化对象,下面,咱们来简单分析一下注册相关的源码,以注册 _AutowiredAnnotationBeanPostProcessor_ post-processor-bean-definition 为例子, _AnnotationConfigUtils#registerAnnotationConfigProcessors_
if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
// 将 AutowiredAnnotationBeanPostProcessor.class 封装为 bean definition
RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
}
上面的步骤将 AutowiredAnnotationBeanPostProcessor.class 封装为 bean definition; _AnnotationConfigUtils.registerPostProcessor_
private static BeanDefinitionHolder registerPostProcessor(
BeanDefinitionRegistry registry, RootBeanDefinition definition, String beanName) {
definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(beanName, definition); // 注册 bean definition
return new BeanDefinitionHolder(definition, beanName);
}
这一步将 _AutowiredAnnotationBeanPostProcessor_ 所对应的 bean definition 注入了当前的 bean factory 当中; _AutowiredAnnotationBeanPostProcessor_ 提供了 @Autowired 注解注入机制的实现,详情参考 [AutowiredAnnotationBeanPostProcessor](/2017/04/05/spring-core-container-sourcecode-analysis-annotation-autowired/#AutowiredAnnotationBeanPostProcessor) 章节; ## 写在最后 经过上述的分析,能够清晰的看到,bean definition 的做用是什么,就是经过 bean definition 中的描述去限定经过 Class Type 实例化获得 instance 的业务规则,咱们看看由 [do scan 流程](#do-scan-流程) 所生成的 annotation-bean-definition<ScannedGenericBeanDefinition> 对象, {% asset_img debug-scanned-generic-bean-definition.png %} 能够看到,当咱们在后续要根据该 annotation-bean-definition 获得一个 DogService 实例的时候,所要遵循的业务规则,以下所示,
Generic bean: class [org.shangyang.spring.container.DogService];
scope=;
abstract=false;
lazyInit=false;
autowireMode=0;
dependencyCheck=0;
autowireCandidate=true;
primary=false;
factoryBeanName=null;
factoryMethodName=null;
initMethodName=null;
destroyMethodName=null;
defined in file [/Users/mac/workspace/spring/framework/sourcecode-analysis/spring-core-container/spring-sourcecode-test/target/classes/org/shangyang/spring/container/DogService.class]
不过,要注意,这里所获得的 ScannedGenericBeanDefinition 实例,一样没有真正去加载 _org.shangyang.spring.container.DogService_ Class Type 到容器中,而只是将 class name `字符串`赋值给了 ScannedGenericBeanDefinition.beanClass,言外之意,未来在加载 Class Type 到容器中的时候,或许与实例化 instance 同样也要根据 bean definitions 中的规则来限定其加载行为,目前我所可以想到的与其相关的就是 ASM 字节码技术,能够在 bean definition 中定义 ASM 字节码修改规则,来控制相关 Class Type 的加载行为; # References