`
IXHONG
  • 浏览: 438038 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Spring之@Configuration配置解析

阅读更多

 

 1.简单的示例:

技术分享
1 @Configuration
2 @EnableConfigurationProperties({DemoProperties.class})
3 public class DemoConfiguration {
4 
5     @Bean
6     public Book getBook(){
7        return new Book();
8     }
9 }
Configuration
技术分享
1     @Autowired Book book;
2 
3     @Test
4     public void testBook(){
5         System.out.println(book.toString());
6     }
单元测试

结果打印出book对象,证明Book已经被注入到Spring 容器中了。

2.@Configuration配置的bean是如何注入到Spring容器中的呢?

首先先来简单介绍下通过BeanDefinitionRegistry注入bean

技术分享
1     @Autowired
2     public void registBeanDefinition(BeanFactory factory){
3         if(factory instanceof BeanDefinitionRegistry) {
4             BeanDefinitionRegistry registry = (BeanDefinitionRegistry)factory;
5             BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Book.class).getBeanDefinition();
6             registry.registerBeanDefinition("mybook", beanDefinition);
7         }
8     }
View Code
技术分享
1     @Autowired @Qualifier("mybook") Book book2;
2     @Test
3     public void testBook2(){
4         System.out.println(book2);
5     }
单元测试

结果同样打印出book对象,这里,笔者将beanFactory强转为BenDefinitionRegistry,因为笔者的Demo中使用的是默认BeanFactory,----DefaultListableBeanFactory,他实现了BeanDefinitionRegistry接口。

 3.入口,下图为ApplicaitonContext refresh方法的简化,只保留了BeandefinitionRegistry注册bean部分功能。

技术分享

然,似乎并没什么用?此处会调用BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry方法。要解决的一个问题是,BeanDefinitionRegistryPostProcessor类型的bean是如何注入的。以SpringBoot初始化为例。

注意到,ApplicationContext的构造方法:

技术分享
1 public AnnotationConfigEmbeddedWebApplicationContext() {
2         this.reader = new AnnotatedBeanDefinitionReader(this);
3         this.scanner = new ClassPathBeanDefinitionScanner(this);
4     }
ApplicationContext构造
技术分享
1 public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) {
2         this(registry, getOrCreateEnvironment(registry));
3     }
View Code
技术分享
1     public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
2         Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
3         Assert.notNull(environment, "Environment must not be null");
4         this.registry = registry;
5         this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
6         AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
7     }
View Code
技术分享
 1     public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
 2             BeanDefinitionRegistry registry, Object source) {
 3 
 4         DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);
 5         if (beanFactory != null) {
 6             if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) {
 7                 beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
 8             }
 9             if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) {
10                 beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
11             }
12         }
13 
14         Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<BeanDefinitionHolder>(4);
15                 //注解@Configuration处理    
16         if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
17             RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
18             def.setSource(source);
19             beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
20         }
21 
22         .......//省略部分代码
23 
24         return beanDefs;
25     }
View Code

注意到对@Configuration的处理为ConfigurationClassPostProcessor。

技术分享

 注意到ConfigurationClassPostProcessor实现了BeanDefinitionRegistryPostProcessor接口,显然关键方法为postProcessBeanDefinitionRegistry。ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry会调用ConfirgurationClassParser的parse方法。会依次解析注解,我们一步一步查看对各个注解的解析。

(1)@PropertySources和@PropertySource  

技术分享
1 @Target(ElementType.TYPE)
2 @Retention(RetentionPolicy.RUNTIME)
3 @Documented
4 public @interface PropertySources {
5 
6     PropertySource[] value();
7 
8 }
PropertySources定义

最终都是处理@PropertySource,@PropertySources仅仅只是包含多个@PropertySource,@PropertySource注解的主要功能是引入配置文件,将配置的属性键值对与环境变量中的配置合并。其中最关键的类为MutablePropertySources

技术分享
1 public class MutablePropertySources implements PropertySources {
2 
3     private final Log logger;
4 
5     private final List<PropertySource<?>> propertySourceList = new CopyOnWriteArrayList<PropertySource<?>>();
6 ......
7 }
View Code

显然MutablePropertySources中包含有一个PropertySource列表。MutablePropertySources仅仅是封装了迭代器功能。可以理解成PropertySources是PropertySource的集合,增加了常用的集合操作。

(2)@ComponentScan

定义自动扫描的包。简化的序列图如下:

 技术分享

其最关键的方法为doScan方法,会注册BeanDefinition到容器中。

技术分享
 1     protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
 2         Assert.notEmpty(basePackages, "At least one base package must be specified");
 3         Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>();
 4         for (String basePackage : basePackages) {
 5             Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
 6             for (BeanDefinition candidate : candidates) {
 7                 ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
 8                 candidate.setScope(scopeMetadata.getScopeName());
 9                 String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
10                 if (candidate instanceof AbstractBeanDefinition) {
11                     postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
12                 }
13                 if (candidate instanceof AnnotatedBeanDefinition) {
14                     AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
15                 }
16                 if (checkCandidate(beanName, candidate)) {
17                     BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
18                     definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
19                     beanDefinitions.add(definitionHolder);
20                     registerBeanDefinition(definitionHolder, this.registry);
21                 }
22             }
23         }
24         return beanDefinitions;
25     }
doScan方法

registerBeanDefinition(definitionHolder, this.registry);能说明一切。

(3)@Import

@Import注解可以配置需要引入的class(假设配置为A,可以是数组),有三种方式。其流程图如下:

技术分享

如果A为ImportSelector的子类,调用selectImports()方法,返回class类名数组,循环解析每一个import的类,如果A为BeanDefinitionRegistrar则直接调用registerBeanDefinition直接注入bean到容器中。如果A为普通的类(非前面提到的两种类型),则将A当做@Configuration配置的类,重新解析Configuration.

(4)@ImportSource

主要功能为引入资源文件。

(5)@Bean,比较简单,童FactoryMethod一样

技术分享
 1         // Process individual @Bean methods
 2         Set<MethodMetadata> beanMethods = sourceClass.getMetadata().getAnnotatedMethods(Bean.class.getName());
 3         for (MethodMetadata methodMetadata : beanMethods) {
 4             configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
 5         }
 6 
 7         // Process default methods on interfaces
 8         for (SourceClass ifc : sourceClass.getInterfaces()) {
 9             beanMethods = ifc.getMetadata().getAnnotatedMethods(Bean.class.getName());
10             for (MethodMetadata methodMetadata : beanMethods) {
11                 if (!methodMetadata.isAbstract()) {
12                     // A default method or other concrete method on a Java 8+ interface...
13                     configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
14                 }
15             }
16         }
@Bean解析

最后真实加载beanDefinition是loadBeanDefinitionsForConfigurationClass方法:

技术分享
 1     private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass,
 2             TrackedConditionEvaluator trackedConditionEvaluator) {
 3 
 4         if (trackedConditionEvaluator.shouldSkip(configClass)) {
 5             String beanName = configClass.getBeanName();
 6             if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
 7                 this.registry.removeBeanDefinition(beanName);
 8             }
 9             this.importRegistry.removeImportingClassFor(configClass.getMetadata().getClassName());
10             return;
11         }
12 
13         if (configClass.isImported()) {
14             registerBeanDefinitionForImportedConfigurationClass(configClass);
15         }
16         for (BeanMethod beanMethod : configClass.getBeanMethods()) {
17             loadBeanDefinitionsForBeanMethod(beanMethod);
18         }
19         loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
20         loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
21     }
0
1
分享到:
评论

相关推荐

    Spring核心注解深入解析:提升开发效率

    @Configuration 和 @Bean 注解用于Java配置,允许开发者用程序的方式定义Spring容器中的bean,取代传统的XML配置文件。 @Value 提供了一种方便的方式来注入简单类型的属性,支持SpEL表达式和外部配置。

    Spring-Reference_zh_CN(Spring中文参考手册)

    6.8.3. 使用Spring IoC来配置AspectJ的切面 6.8.4. 在Spring应用中使用AspectJ Load-time weaving(LTW) 6.9. 其它资源 7. Spring AOP APIs 7.1. 简介 7.2. Spring中的切入点API 7.2.1. 概念 7.2.2. 切入点实施 ...

    springboot学习思维笔记.xmind

    @Configuration声明当前类是一个配置类 @Bean注解在方法上,声明当前方法的返回值为一个Bean AOP @Aspect 声明是一个切面 拦截规则@After @Before @Around PointCut JoinPoint Spring...

    spring-boot-project:spring-boot原始解析-源码解析

    spring-boot-configuration-sample配置模块 spring-boot-import-sample导入实现模块 spring-boot-alias-annotation-sample注解属性别名模块 spring-boot-actuator-sample监控组件模块 spring-boot-enable-sample自动...

    Spring 2.0 开发参考手册

    6.8.3. 使用Spring IoC来配置AspectJ的切面 6.8.4. 在Spring应用中使用AspectJ Load-time weaving(LTW) 6.9. 其它资源 7. Spring AOP APIs 7.1. 简介 7.2. Spring中的切入点API 7.2.1. 概念 7.2.2. 切入点...

    Spring中文帮助文档

    6.8.3. 使用Spring IoC来配置AspectJ的切面 6.8.4. 在Spring应用中使用AspectJ加载时织入(LTW) 6.9. 更多资源 7. Spring AOP APIs 7.1. 简介 7.2. Spring中的切入点API 7.2.1. 概念 7.2.2. 切入点运算 ...

    spring chm文档

    6.8.3. 使用Spring IoC来配置AspectJ的切面 6.8.4. 在Spring应用中使用AspectJ Load-time weaving(LTW) 6.9. 其它资源 7. Spring AOP APIs 7.1. 简介 7.2. Spring中的切入点API 7.2.1. 概念 7.2.2. 切入点...

    Spring API

    6.8.3. 使用Spring IoC来配置AspectJ的切面 6.8.4. 在Spring应用中使用AspectJ加载时织入(LTW) 6.9. 更多资源 7. Spring AOP APIs 7.1. 简介 7.2. Spring中的切入点API 7.2.1. 概念 7.2.2. 切入点运算 ...

    SpringBoot2的最佳实践 SpringBoot2基础入门+底层注解+自动配置+高级特性与原理解析

    ├─02、基础入门-Spring生态圈.mp4 ├─03、基础入门-SpringBoot的大时代背景.mp4 ├─04、基础入门-SpringBoot官方文档架构.mp4 ├─05、基础入门-SpringBoot-HelloWorld.mp4 ├─06、基础入门-SpringBoot-依赖...

    Spring.net框架

    我们首先要能够解析配置文件中的信息,然后建立包含相关信息的对象。最后根据这些信息利用反射机制完成对象的创建。首先我们看一下配置文件所包含的内容: &lt;configuration&gt; , MainApp" /&gt; ...

    spring.net中文手册在线版

    Spring.NET是一个应用程序框架,其目的是协助开发人员创建企业级的.NET应用程序。它提供了很多方面的功能,比如依赖注入、面向方面编程(AOP)、数据访问抽象及ASP.NET扩展等等。Spring.NET以Java版的Spring框架为...

    搞定J2EE:STRUTS+SPRING+HIBERNATE整合详解与典型案例 (1)

    12.2.1 Configuration(配置Connection) 12.2.2 SessionFactory(Connection工厂) 12.2.3 Session(提供Connection) 12.3 使用Hibernate操作数据库的示例 12.3.1 创建数据库表 12.3.2 编写表对应的JavaBean ...

    搞定J2EE:STRUTS+SPRING+HIBERNATE整合详解与典型案例 (3)

    12.2.1 Configuration(配置Connection) 12.2.2 SessionFactory(Connection工厂) 12.2.3 Session(提供Connection) 12.3 使用Hibernate操作数据库的示例 12.3.1 创建数据库表 12.3.2 编写表对应的JavaBean ...

    史上最全 SpringBoot 注解详解

    其中@ComponentScan让Spring Boot扫描到Configuration类并把它加入到程序上下文。 2、@ComponentScan 组件扫描,可自动发现和装配一些Bean。 3、@Configuration 等同于Spring的XML配置文件;使用Java代码可以检查...

    搞定J2EE:STRUTS+SPRING+HIBERNATE整合详解与典型案例 (2)

    12.2.1 Configuration(配置Connection) 12.2.2 SessionFactory(Connection工厂) 12.2.3 Session(提供Connection) 12.3 使用Hibernate操作数据库的示例 12.3.1 创建数据库表 12.3.2 编写表对应的JavaBean ...

    Struts课堂笔记.rar--struts2的struts.properties配置文件详解

    专为multipart请求信息使用的org.apache.struts2.dispatcher.multipart.MultiPartRequest解析器接口(文件上传用) struts.multipart.saveDir The directory to use for storing uploaded files 设置存储上传...

    xsdsAppServer.rar

    使用IDEA搭建了一个springmvc框架 配置了视图解析jsp&freemaker视图解析 可以导入就用接口项目或者web项目都可以使用 配置文件需要修改一下 &lt;!-- 使用SqlSessionFactoryBean工厂产生SqlSession对象,方便后期注入...

    《程序天下:J2EE整合详解与典型案例》光盘源码

    12.2.1 Configuration(配置Connection) 12.2.2 SessionFactory(Connection工厂) 12.2.3 Session(提供Connection) 12.3 使用Hibernate操作数据库的示例 12.3.1 创建数据库表 12.3.2 编写表对应的JavaBean ...

    sfaf:适用于MCEB Pub 7或SFAF的工具

    SFAF 该SFAF存储库是用于处理MCEB Pub 7或... 类似于基于Configuration over Code的Spring Configuration over Code方法,该方法侧重于外部化代码之外的所有配置,以仅使用配置来重用/重新部署。 此外,与“ Convent

    SSH的jar包.rar

    读取并解析hibernate.cfg.xml配置文件 2.由hibernate.cfg.xml中的读取并解析映射信息 3.通过config.buildSessionFactory();//创建SessionFactory 4.sessionFactory.openSession();//打开Sesssion 5.session....

Global site tag (gtag.js) - Google Analytics