Spring3.2中Bean界说之基于Annotation和Java Code设置方法的源码理会
当前位置:以往代写 > JAVA 教程 >Spring3.2中Bean界说之基于Annotation和Java Code设置方法的源码理会
2019-06-14

Spring3.2中Bean界说之基于Annotation和Java Code设置方法的源码理会

Spring3.2中Bean界说之基于Annotation和Java Code设置方法的源码理会

副标题#e#

基于 Annotation 设置 Bean Definition 的源码解读

本系列文章第一部门阐明白 Spring 理会 XML 设置文件中 <bean /> 元素的源码,这是 Spring 最原始的一种设置方法,同时也使 XML 中的节点具有定名空间特性。参考 Spring 相关文档,假如有如下的设置方法: <context:component-scan base-package="com.colorcc.spring.sample" /> 则可知:其一,该元素回收了"http://www.springframework.org/schema/context" 定名空间的设置方法。其二,针对 "com.colorcc.spring.sample" 包里的每个工具,Spring 大概回收基于 Annotation 方法设置息争析对应的工具。说大概是因为还需要在相关 Java 代码中利用如 @Component 及其子 Annotation 注解后才可以。

基于 Annotation 的 Bean 理会进口

如上文的图 2 所示,其右下角最后一个 Loop 顺序图即为 Annotation 设置理会 Bean 的进口,因此其具体步调可参考上文的图 2 的阐明。 XML Context 定名空间 bean 元素理会由上文的清单 6 可知,假如回收"http://www.springframework.org/schema/context" 定名空间,则执行"delegate.parseCustomElement(ele)" 要领举办 Bean 元素理会,其顺序图如图 1 和图 2 所示。

图 1. 基于 Annotation 理会 Bean 的顺序图 – Part1

Spring3.2中Bean定义之基于Annotation和Java Code配置要领的源码剖析

图 2. 基于 Annotation 理会 Bean 的顺序图 – Part2

Spring3.2中Bean定义之基于Annotation和Java Code配置要领的源码剖析


#p#副标题#e#

假如开拓人员在 XML 文件中设置雷同 <context:component-scan … /> 元素时,个中 context 标识了对应的定名空间,而 component-scan 则具有占位符的成果,通过该占位符,Spring 框架可以界说对应的理会工具(如 component-scan 对应于 ComponentScanBeanDefinitionParser)举办详细的业务逻辑处理惩罚。

从 图 1阐明可知,Spring 框架碰着非默认定名空间界说的节点时,会按照该节点理会对应的定名空间对应的 URI。假如是第一次理会该定名空间,则会按照图 1 描写找到对应的 schema 举办语法查抄。同时也会找处处理惩罚该定名空间对应的 Handler。如 context 对应的 handler 为 org.springframework.context.config.ContextNamespaceHandler,这是一个字符串,然后通过反射机制建设该字符串对应的 Java 工具。并缓存该工具供今后继承利用。

步调 2 获得的 handler 工具即为每个定名空间处理惩罚的 handler,也就是完成对 <context … /> 的理会。当 handler 建设后,Spring 框架会挪用其 init() 要领对其后头跟从的占位符(如 component-scan 等)建设对应的东西类工具,凡是以 Parser 末了。同时 Spring 框架也会缓存该东西类供今后继承利用。一个例子如 清单 1 所示。

清单 1. 通过 Handler 界说后缀处理惩罚工具
public class ContextNamespaceHandler extends NamespaceHandlerSupport { 
    public void init() { 
    registerBeanDefinitionParser("property-placeholder", 
                new PropertyPlaceholderBeanDefinitionParser()); 
    registerBeanDefinitionParser("property-override", 
                new PropertyOverrideBeanDefinitionParser()); 
    registerBeanDefinitionParser("annotation-config", 
                new AnnotationConfigBeanDefinitionParser()); 
    //  component-scan 占位符对应 ComponentScanBeanDefinitionParser 工具
    registerBeanDefinitionParser("component-scan", 
                new ComponentScanBeanDefinitionParser()); 
    registerBeanDefinitionParser("load-time-weaver", 
                new LoadTimeWeaverBeanDefinitionParser()); 
    registerBeanDefinitionParser("spring-configured", 
                new SpringConfiguredBeanDefinitionParser()); 
    registerBeanDefinitionParser("mbean-export", 
                new MBeanExportBeanDefinitionParser()); 
    registerBeanDefinitionParser("mbean-server", 
                new MBeanServerBeanDefinitionParser()); 
    } 
}

通过前述步调,Spring 获得定名空间对应的 handler 工具荟萃。这样就可以通过要理会的节点定名空间对应的占位符寻找其 parser 工具,如未找到,则报错返回。按照清单 1 可知,对付 component-scan 占位符,其对应的 parser 工具为 ComponentScanBeanDefinitionParser 实例化后的工具(简称 csbdParser)。同时,delegate 会按照读入的 XML 相关信息,实例化一个 ParserContext 工具用于存储 xml, delegate 等上下文信息。这样,NamespaceHandler 会按照传入的节点元素和 ParserContext 信息,找到理会 XML 设置节点的理会工具 csbdParser,挪用其 parse(element, parserContect) 要领举办理会 , 如图 1 的 (1) 地方示。

#p#分页标题#e#

parse 要领的处理惩罚可以简朴归结为三个步调:一、建设 Scanner 东西。 二、利用东西扫描 Java 工具,理会切正当则的 Bean 到对应 Bean Definition。三、对获得的 Bean Definitons举办处理惩罚。

csbdParser 按照 ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element); 建设一个 Scanner 东西,详细步调如下: 6.1 按照图 1 的(2)地方示,csbdParser 会 new 出一个 ClassPathBeanDefinitionScanner 工具 scanner,其传入参数为 BeanDefinitionRegistry 工具和 boolean 范例参数 useDefaultFilters,前者即为 BeanFactory 工具,前文已经阐明过,用于生存 Spring 理会出来的 Bean Definition工具的容器。后者提供应开拓人员一个钩子成果,由开拓人员抉择是否利用 Spring 框架提供的默认参数寻找满意条件的 bean 并理会,默认环境为 true。 清单 2 列出了 Spring 框架界说的默认条件的 filters。从中可知通过 @Component、@javax.annotation.ManagedBean 和 @javax.inject.Named 以及标志了这些 Annotation 的新 Annotation 注解过的 Java 工具即为 Spring 框架通过 Annotation 设置的默认法则。 清单 2. Annotation 理会 Bean 界说的默认法则

// 界说默认环境下 filter 法则,每每被 @Component、@ManagedBean、@Named 及被这些 Annotation 
 // 注解过的 Annotaion 注解的 Java 文件会被 Spring 框架利用 Annotation 方法处理惩罚其 Bean 界说
 @SuppressWarnings("unchecked") 
 protected void registerDefaultFilters() { 
     // 将 @Component 插手到 filter 中
     this.includeFilters.add(new AnnotationTypeFilter(Component.class)); 
     ClassLoader cl = 
     ClassPathScanningCandidateComponentProvider.class.getClassLoader(); 
     try { 
         // 将 @javax.annotation.ManagedBean 插手到 filter 中
         this.includeFilters.add(new AnnotationTypeFilter( 
             ((Class<? extends Annotation>)   
             cl.loadClass("javax.annotation.ManagedBean")), false)); 
         logger.info("JSR-250 'javax.annotation.ManagedBean' found 
             and supported for component scanning"); 
     } 
     catch (ClassNotFoundException ex) { 
//JSR-250 1.1 API (as included in Java EE 6) not available - simply skip. 
     } 
     try { 
         // 将 @javax.inject.Named 插手到 filter 中
         this.includeFilters.add(new AnnotationTypeFilter( 
                 ((Class<? extends Annotation>) 
                 cl.loadClass("javax.inject.Named")), false)); 
         logger.info("JSR-330 'javax.inject.Named' annotation found and 
                 supported for component scanning"); 
     } 
     catch (ClassNotFoundException ex) { 
     //JSR-330 API not available - simply skip. 
     } 
 }

#p#副标题#e#

6.2 由类图可知,scanner 担任了 ClassPathScanningCandidateComponentProvider 工具。该工具提供了一些民众属性的配置,如默认的扫描文件 Pattern 为"**/*.class",Pattern Resolver 为 PathMatchingResourcePatternResolverd 等。这里需要出格留意的两个重要的过滤器 includeFilters 和 excludeFilters,Spring 框架在扫描给定 pattern 的工具时,会按照 excludeFilters 过滤掉满意条件的工具,然后才按照 includeFilters 过滤需要理会的工具。因此界说 Scanner 时可以按照这两个过滤器实现开拓人员自界说 Scanner 的过滤条件。 Scanner 本身也界说了一些东西类,如 BeanDefinitionDefaults 用于配置由 Annotation 方法理会 Bean 工具获得的 Bean Definition的默认属性值;BeanNameGenerator 用于配置默认 beanName 属性等。

6.3 建设了 Scanner 后,Spring 框架会按照一些业务逻辑配置一些属性供今后利用,如图 1 的 (3) 处。

步调 6 建设了一个东西类 scanner, 同时 Spring 框架也会获得开拓人员设置的 base-package 属性值。这样 csbdParser 会执行 Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages); 来扫描 basePackages 里满意条件的 bean 界说,并生成对应的 Bean Definition,如图 2 所示。详细阐明如下:

7.1 Scanner 首先会界说一个 Set<BeanDefinitionHolder> 容器,用于存放理会每个 bean 对应的 BeanDefinitionHolder 工具。

7.2 传入的 basePackages 假如以",;"形式脱离,则支解 basePackages 字符串为 String 数组,并轮回每个元素,执行后头操纵。

查察本栏目

7.3 如图 2 的 (1) 所示,该轮回首先通过 findCandidateComponents 要领按照必然的法则结构 Spring 框架需要加载的 class 文件 pattern,如 basePackage 为 com.colorcc.spring.sample 结构的需要加载的 class 文件 pattern 为 :classpath*:com/colorcc/spring/sample/**/*.class。这样 Spring 框架会将满意条件的 class 文件加载并转换为 Resource 范例工具,返回给 scanner。

7.4 针对获得的每个 Resource 工具,通过 ASM 东西阐明其 Annotation 元素,结构 MetadataReader 工具,并将 annotation 信息封装到 AnnotationMetadata 范例的 annotationMetadata 属性中。

#p#分页标题#e#

7.5 针对上步获得的 MetadataReader 工具,按照 annotationMetadata 属性,如上文 6.2 的描写,如满意 excludeFilters 过滤器条件,则直接返回,不再理会。反之,如满意 includeFilters 过滤器条件(默认环境即为被 6.1 描写的 annotation 标志过),则继承后续步调。

7.6 利用颠末上步过滤后获得的 MetadataReader 工具作为结构函数的参数,建设 ScannedGenericBeanDefinition 工具 sbd。按照类图(如上文图 5 所示),其直接担任 GenericBeanDefinition 工具,因此其具有上一章阐明的所有 GenericBeanDefinition 工具的属性值。同时 sdb 义了一个 AnnotationMetadata 属性,生存了 MetadataReader 工具中关于 Annotation 元数据的一些信息。而且 sbd 的 beanClass 属性也是通过 Annotation 元数据获得并配置的。

7.7 获得的 sbd 返回给 scanner,通过 scanner 配置其一些属性如 resource 和 source 等。同时判定该 sbd 的属性若为非接口,非抽象类且子类要领可重写,则插手到一个 Set 荟萃 candidates 中。反之轮回下一个 resource 继承举办 7.4-7.7 的业务处理惩罚。

7.8 针对所有的开拓人员界说的 package,举办 7.3-7.7 的处理惩罚,获得所有满意 Annotation 理会的 bean 元素的团结 candidates。

7.9 轮回 7.8 获得的每个 candidate,假如其是 AbstractBeanDefinition 范例,则按照 6.2 界说的 BeanDefinitionDefaults 工具配置 candidate 的一些默认属性,如图 2 的 (3) 地方示。

假如 candidate 是 AnnotatedBeanDefinition 范例,则获得其 Annotation 元数据,并按照该元数据中的 annotation 配置 candidate 的一些属性,如被 @Primary 标识,则配置 candidate 的 primary 属性为 true,其他 annotation 还包罗 @Lazy、@DependsOn 和 @Role 等。

7.10 查询 registory 的 beanDefinitionMap 属性容器,假如其不包括 candidate 的 beanName 元素,则通过 candidate 及其 beanName 结构一个 BeanDefinitionHolder 工具,将该工具插手到 7.1 建设都容器中。

7.11 最后一步就是将获得的 beanDefinitionHolder 理会并注册到 registry 中。该步调与上文图 4 黄色配景的的顺序图片断(sd Registry Bean Definition)完全一致。详细阐明可参考前文。

csbdParser 最后一步需要完成的操纵是将获得的每个 BeanDefinitionHolder 通过 registerComponents(parserContext.getReaderContext(), beanDefinitions, element); 做一些收尾的处理惩罚,如图 1 的右下角部门所示。详细阐明如下:

8.1 框架按照 BeanDefinitionHolder 的 BeanDefinition 属性结构一个 BeanComponentDefinition 工具,该工具担任 BeanDefinitionHolder,界说了两个私有属性 innerBeanDefinitions 和 beanReferences ,用于存放每个 bean 元素理会时所引用的嵌套 bean 和引用 bean 工具。

8.2 按照 XML 设置的 element 信息,界说一个容器 CompositeComponentDefinition 用于存放每个 BeanComponentDefinition 工具。

8.3 默认环境下,将 ConfigurationClassPostProcessor、AutowiredAnnotationBeanPostProcessor、RequiredAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor 等通用 processor 也理会成对应的 BeanDefinitionHolder 并插手到 8.2 建设的 CompositeComponentDefinition 容器中。

8.4 雷同前文,Spring 框架发送一个 ReaderEventListener 事件。默认环境下,该事件为 EmptyReaderEventListener 工具的 componentRegistered 事件,这是一个空事件,没有详细的业务逻辑。

#p#副标题#e#

基于 Java Code 设置 Bean Definition 的源码解读

前文阐明白基于 XML 和 Annotation 设置 Beam Definition的源码实现。从 Spring3.0 开始,增加了一种新的路过来设置 Bean Definition,这就是通过 Java Code 设置 Bean Definition。与前两种设置方法差异点在于:

前两种设置方法为预界说方法,即开拓人员通过 XML 文件可能 Annotation 预界说设置 bean 的各类属性后,启动 Spring 容器,Spring 容器会首先理会这些设置属性,生成对应都 Bean Definition,装入到 DefaultListableBeanFactory 工具的属性容器中去。与此同时,Spring 框架也会界说一些内部利用的 Bean 界说,如 bean 名为”org.springframework.context.annotation.internalConfigurationAnnotationProcessor”的 ConfigurationClassPostProcessor 界说。

尔后者而今不会做任何 Bean Definition 的界说理会行动,Spring 框架会按照前两种设置,过滤出 BeanDefinitionRegistryPostProcessor 范例的 Bean 界说,并通过 Spring 框架生成其对应的 Bean 工具(如 ConfigurationClassPostProcessor 实例)。团结 Spring 上下文源码可知这个工具是一个 processor 范例东西类,Spring 容器会在实例化开拓人员所界说的 Bean 前先挪用该 processor 的 postProcessBeanDefinitionRegistry(…) 要领。此处实现基于 Java Code 设置 Bean Definition的处理惩罚。

#p#分页标题#e#

由上文图 5 可知,Spring 框架界说了一系列 Bean Definition工具,通过 XML 设置方法界说的 Bean 属性,经 Spring 框架理会后会封装成 GenericBeanDefinition 工具,然后注册到 DefaultListableBeanFactory 工具的属性容器中去。通过 Annotation 设置方法界说的 Bean 属性经 Spring 框架理会后会封装成 ScannedGenericBeanDefinition 工具(即 GenericBeanDefinition 工具增加了 AnnotationMetadata 属性),然后注册到 DefaultListableBeanFactory 工具的属性容器中去。RootBeanDefinition 类雷同 GenericBeanDefinition,都是 AbstractBeanDefinition 的实现类,其特别界说了一些属性如 externallyManagedConfigMembers,externallyManagedInitMethods 以及 constructorArgumentLock 等,供 Spring 框架将 Bean Definition实例化为 Java 工具时利用。通过 Java Code 设置的 Bean Definition经 Spring 框架理会后会封装成 ConfigurationClassBeanDefinition 工具(RootBeanDefinition 工具增加了 AnnotationMetadata 属性), 然后注册到 DefaultListableBeanFactory 工具的属性容器中去。

图 3. 基于 Java Code 理会 Bean 的顺序图

Spring3.2中Bean定义之基于Annotation和Java Code配置要领的源码剖析

图 3 展示了基于 Java Code 理会 Bean 的顺序图。简朴阐明如下:

按照前文阐明,该顺序图的进口为 ConfigurationClassPostProcessor 的 postProcessBeanDefinitionRegistry(BeanDefinitionRegistry reigistry) 要领。

步调 1 首先注册一个东西类 ImportAwareBeanPostProcessor 的 Bean 界说到 reigsitry 中去。然后处理惩罚 resistry 已有的 Bean Definition。

至图 3 的步调 (1) 处,Spring 框架顺序扫描每个 Bean Definition,假如被扫描的 Bean Definition 被 @Commponent 及其子注解可能 @ConfigureClass 注解过,则插手到范例为 Set<BeanDefinitionHolder> 的 configCandidates 容器中。假如容器为空,则暗示不存在通过 Java Code 设置的 Bean 界说,直接返回。不然,执行步调 4。

Spring 框架按照 registry 及自界说的一系列属性如 environment、resourceLoader 等,建设 Bean 理会的东西类 ConfigurationClassParser 工具 parser,然后利用该东西类理会 configCandidates 容器中每个 Bean Definition,如图 3 的 (2) 地方示。该处扫描 Bean Definition,将所有被 @Bean 注解过的 method 要领及所属 class 信息封装成 BeanMethod 工具,并装入 class 的属性容器中。最终将处理惩罚过的 class 工具装入 parser 容器中。

Spring 框架按照 registry 及子界说的一系列属性建设东西类 ConfigurationClassBeanDefinitionReader 工具 reader, 该东西类认真理会 parser 容器中每个 class 的 bean 信息,如图 3 的 (3) 地方示。

步调 4 的 parser 和步调 5 的 reader 区别在于:parser 认真理会被特定 annotation 注解过的 class 工具的被注解 @Bean 注解过的 method 封装为 BeanMethod 工具。而 reader 认真将每个 BeanMethod 工具理会为 Bean Definition。

有了前述常识,图 3 的 (3) 处理会将很是简朴。首先通过 parser 找到步调 4 理会的每个 class 工具,然后以该工具为参数,挪用 reader 的 loadBeanDefinitions(class) 要领,如图 3 的 (4) 处。

步调 6 的要了解轮回其每个被 @Bean 注解过的要领工具 BeanMethod,针对每个 BeanMethod,首先会以 class 为参数,建设一个 ConfigurationClassBeanDefinition 工具 ccbd(这是一个带有 AnnotationMeta 元数据的 RootBeanDefinition)code type="inline">,配置一些自身属性。然后将要领名配置为 ccbd 的一个 factory name,再按照 BeanMethod 的 annotation 信息如 autowired、initMethod 以及 destroyMethod 等配置 ccbd 的相关属性值。

雷同前述步调,将获得的 ccbd 注册到 rigistry 的属性容器中去。该步调与上文图 4 黄色配景的的顺序图片断(sd Registry Bean Definition)完全一致。详细阐明可参考前文。

小结

本文具体阐明白 Spring 框架理会开拓人员界说的 Bean 到 Spring 容器的 Bean Definition工具的处理惩罚进程。

基于 XML 设置方法是最原始也是利用最普遍的一种要领,其利益在于将设置会合在一起(XML 设置文件中),且与 Java 代码疏散,利便打点。对付设置文件的改变,不需要从头编译源代码,极大的提高了开拓效率。其缺点在于对大型基于 Spring 设置的项目,冗余的 XML 设置较多,增加了开拓的事情量和维护本钱。

基于 Annotation 设置方法是跟着 JDK 对 Annotation 支持新引入的一种设置要领,其利益在于可以淘汰开拓人员的事情量,担保代码的整洁性。而其缺点在于与 Java 代码团结,同时设置信息散入到每个 Java 文件中,对新插手项目标开拓人员,需要必然的进修本钱,别的,基于 Annotation 不能实现所有基于 XML 设置的元数据信息。

#p#分页标题#e#

基于 Java Code 的设置方法,其执行道理差异于前两种。它是在 Spring 框架已经理会了基于 XML 和 Annotation 设置后,通过插手 BeanDefinitionRegistryPostProcessor 范例的 processor 来处理惩罚设置信息,让开拓人员通过 Java 编程方法界说一个 Java 工具。其利益在于可以将设置信息会合在必然数量的 Java 工具中,同时通过 Java 编程方法,比基于 Annotation 方法具有更高的机动性。而且该设置方法给开拓人员提供了一种很是好的典型来增加用户自界说的理会东西类。其主要缺点在于与 Java 代码团结细密,设置信息的改变需要从头编译 Java 代码,别的这是一种新引入的理会方法,需要必然的进修本钱。

    关键字:

在线提交作业