点击上方蓝色“方志朋”,选择“设为星标”

回复“666”获取独家整理的学习资料!

作者 | 温安适

来源 | https://juejin.im/post/5efdd689e51d4534af686ca9

工作中经常用到,如下注解:

  • @EnableEurekaClient

  • @EnableFeignClients

  • @EnableCircuitBreaker

  • @EnableHystrix

他们都是@Enable开头,各自实现不同的功能,解析这种@Enable的逻辑是什么呢?

@Enable驱动逻辑

找入口

@Enable的模块驱动,依赖于@Import实现。

@Import作用是装载导入类,主要包括@Configuration class,ImportSelector实现类,ImportBeanDefinitionRegistrar实现类。

XML时代,经常是@Import,<context:component-scan>,<context:annotation-config>一起使用。

<context:annotation-config>(注解配置)中大概率有我们需要找的逻辑。

根据 Spring Framework 2.0引入的可扩展的XML编程机制,XML Schema命名空间需要与Handler建立映射关系。

该关系配置在相对于classpath下的/META-INF/spring.handlers中。

查看ContextNamespaceHandler 源码

public class ContextNamespaceHandler extends NamespaceHandlerSupport {@Overridepublic void init() {//省略其他代码registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser());}
}复制代码

** 对应AnnotationConfigBeanDefinitionParser这个就是要找的入口**

找核心类

从AnnotationConfigBeanDefinitionParser的parse方法开始一路向下,找到

AnnotationConfigUtils.registerAnnotationConfigProcessors中注册了ConfigurationClassPostProcessor。

img

ConfigurationClassPostProcessor类注释说明

\1. 用于的引导处理@Configuration类

\2. context:annotation-config/或 context:component-scan/时会注册

否则需要手工编程

\3. ConfigurationClassPostProcessor第一优先级,保证

@Configuration}类中声明@Bean,在其他 BeanFactoryPostProcessor执行之前被注册

扩展

AnnotationConfigApplicationContext中new AnnotationBeanDefinitionReader也调用了 AnnotationConfigUtils .

registerAnnotationConfigProcessors

从类注释中,可以看出ConfigurationClassPostProcessor就是要找的核心类

找核心方法

查看 ConfigurationClassPostProcessor 的层级关系为

img

Aware系列注入相应资源,Ordered设置优先级,值得关注的就是

postProcessBeanDefinitionRegistry了。

postProcessBeanDefinitionRegistry其内部有2个方法

  1. postProcessBeanDefinitionRegistry在BeanDefinition注册之后,BeanFactoryPostProcessor执行之前,修改或重写BeanDefinition

  2. 继承自BeanFactoryPostProcessor的postProcessBeanFactory,BeanDefinition加载之后,Bean实例化之前,重写或添加BeanDefinition,修改BeanFactory

浏览2个方法,都有processConfigBeanDefinitions,从名称可以看出是处理配置类Bean定义

img

ConfigurationClassPostProcessor#processConfigBeanDefinitions就是要找的核心方法

梳理流程

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {List<BeanDefinitionHolder> configCandidates = new ArrayList<>();String[] candidateNames = registry.getBeanDefinitionNames();for (String beanName : candidateNames) {BeanDefinition beanDef = registry.getBeanDefinition(beanName);if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {if (logger.isDebugEnabled()) {logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);}}else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));}}// 没有找到 @Configuration classes 立即返回if (configCandidates.isEmpty()) {return;}//根据@Order 值进行排序configCandidates.sort((bd1, bd2) -> {int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());return Integer.compare(i1, i2);});//通过封闭的应用程序上下文, 检测任何自定义bean名称生成策略supplied through the enclosing application contextSingletonBeanRegistry sbr = null;if (registry instanceof SingletonBeanRegistry) {sbr = (SingletonBeanRegistry) registry;if (!this.localBeanNameGeneratorSet) {BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);if (generator != null) {this.componentScanBeanNameGenerator = generator;this.importBeanNameGenerator = generator;}}}if (this.environment == null) {this.environment = new StandardEnvironment();}// 解析@Configuration classConfigurationClassParser parser = new ConfigurationClassParser(this.metadataReaderFactory, this.problemReporter, this.environment,this.resourceLoader, this.componentScanBeanNameGenerator, registry);Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());do {parser.parse(candidates);parser.validate();Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());configClasses.removeAll(alreadyParsed);// 读ConfigurationClass的信息,创建BeanDefinitionif (this.reader == null) {this.reader = new ConfigurationClassBeanDefinitionReader(registry, this.sourceExtractor, this.resourceLoader, this.environment,this.importBeanNameGenerator, parser.getImportRegistry());}this.reader.loadBeanDefinitions(configClasses);alreadyParsed.addAll(configClasses);candidates.clear();if (registry.getBeanDefinitionCount() > candidateNames.length) {String[] newCandidateNames = registry.getBeanDefinitionNames();Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));Set<String> alreadyParsedClasses = new HashSet<>();for (ConfigurationClass configurationClass : alreadyParsed) {alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());}for (String candidateName : newCandidateNames) {if (!oldCandidateNames.contains(candidateName)) {BeanDefinition bd = registry.getBeanDefinition(candidateName);if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&!alreadyParsedClasses.contains(bd.getBeanClassName())) {candidates.add(new BeanDefinitionHolder(bd, candidateName));}}}candidateNames = newCandidateNames;}}while (!candidates.isEmpty());// 将ImportRegistry注册为bean以支持importware@Configuration类if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());}if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {// Clear cache in externally provided MetadataReaderFactory; this is a no-op// for a shared cache since it'll be cleared by the ApplicationContext.((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();}
}复制代码

ConfigurationClassPostProcessor#processConfigBeanDefinitions核心如下:

  1. 根据@Order 值进行排序

  2. 解析@Configuration class 为ConfigurationClass对象

  3. 读ConfigurationClass的信息,创建BeanDefinition

  4. 将ImportRegistry注册为bean以支持importware@Configuration类

重点关注解析方法

ConfigurationClassParser#parse方法负责解析@Configuration class 为ConfigurationClass对象

查阅其源码如下:

ConfigurationClassParser#doProcessConfigurationClass代码如下:

protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)throws IOException {if (configClass.getMetadata().isAnnotated(Component.class.getName())) {// Recursively process any member (nested) classes firstprocessMemberClasses(configClass, sourceClass);}// Process any @PropertySource annotationsfor (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), PropertySources.class,org.springframework.context.annotation.PropertySource.class)) {if (this.environment instanceof ConfigurableEnvironment) {processPropertySource(propertySource);}else {logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +"]. Reason: Environment must implement ConfigurableEnvironment");}}// Process any @ComponentScan annotationsSet<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);if (!componentScans.isEmpty() &&!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {for (AnnotationAttributes componentScan : componentScans) {// The config class is annotated with @ComponentScan -> perform the scan immediatelySet<BeanDefinitionHolder> scannedBeanDefinitions =this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());// Check the set of scanned definitions for any further config classes and parse recursively if neededfor (BeanDefinitionHolder holder : scannedBeanDefinitions) {BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();if (bdCand == null) {bdCand = holder.getBeanDefinition();}if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {parse(bdCand.getBeanClassName(), holder.getBeanName());}}}}// Process any @Import annotationsprocessImports(configClass, sourceClass, getImports(sourceClass), true);// Process any @ImportResource annotationsAnnotationAttributes importResource =AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);if (importResource != null) {String[] resources = importResource.getStringArray("locations");Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");for (String resource : resources) {String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);configClass.addImportedResource(resolvedResource, readerClass);}}// Process individual @Bean methodsSet<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);for (MethodMetadata methodMetadata : beanMethods) {configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));}// Process default methods on interfacesprocessInterfaces(configClass, sourceClass);// Process superclass, if anyif (sourceClass.getMetadata().hasSuperClass()) {String superclass = sourceClass.getMetadata().getSuperClassName();if (superclass != null && !superclass.startsWith("java") &&!this.knownSuperclasses.containsKey(superclass)) {this.knownSuperclasses.put(superclass, configClass);// Superclass found, return its annotation metadata and recursereturn sourceClass.getSuperClass();}}// No superclass -> processing is completereturn null;
}复制代码

ConfigurationClassParser#doProcessConfigurationClass(ConfigurationClass,AnnatationMetatdata)将@PropertySource,   @ComponentScan@Import,@ImportResource,@Bean等一起处理了。

看到这里基本逻辑已经理清 了,但是有一个疑问

@Configuration中的@Bean没有其他特殊处理吗?

浏览代码解决疑问

![img](data:image/svg+xml;utf8,)

从上边浏览的代码可以看到完全模式,会被AOP增强

那什么是完全模式呢?在ConfigurationClassUtils找到如下方法:

public class ConfigurationClassUtils{
//省略其他方法
public static boolean isFullConfigurationCandidate(AnnotationMetadata metadata) {return metadata.isAnnotated(Configuration.class.getName());
}
}复制代码

@Configuration class是完全模式,@Component,@Bean是轻量级模式

那AOP增强了作用是什么呢?查看 ConfigurationClassEnhancer 的类注释如下:

/**

* Enhances {@link

Configuration

} classes by generating a CGLIB subclass which

* interacts with the Spring container to respect bean scoping semantics for

* {@code @Bean} methods. Each such {@code @Bean} method will be overridden in

* the generated subclass, only delegating to the actual {@code @Bean} method

* implementation if the container actually requests the construction of a new

* instance. Otherwise, a call to such an {@code @Bean} method serves as a

* reference back to the container, obtaining the corresponding bean by name.

* @author Chris Beams

* @author Juergen Hoeller

* @since 3.0

* @see #enhance

* @see

ConfigurationClassPostProcessor

*/

class  ConfigurationClassEnhancer  {

大概意思如下:

通过CGLIB增强@Configuration class。

每个@Bean方法会生成子类。

首次被调用时,@Bean方法会被执行用于创建bean实例;

再次被调用时,不会再执行创建bean实例,而是根据bean名称返回首次该方法被执行时创建的bean实例。

总结

1.ConfigurationClassPostProcessor负责筛选@Component Class、@Configuration Class以及@Bean定义的Bean,**

2.ConfigurationClassParser从候选的Bean定义中解析出ConfigurationClass集合,随后被3.ConfigurationClassBeanDefinitionReader转换为BeanDefinition

4.ConfigurationClassParser的解析顺序,

@PropertySource->@ComponentScan->@Import->@ImportResource->@Bean->接口的默认方法->处理父类

5.@Configuration class是完全模式,@Component,@Bean是轻量级模式

6.CGLIB增强@Configuration class。每个@Bean方法会生成子类。

首次被调用时,@Bean方法会被执行用于创建bean实例;

再次被调用时,不会再执行创建bean实例,而是根据bean名称返回首次该方法被执行时创建的bean实例。

热门内容:
  • 推荐一套开源通用后台管理系统

  • 使用Netty如何做到单机秒级接收35万个对象

  • 别再写满屏的try-catch了,真丑,全局异常处理不会吗?

  • Sentinel实现限流,竟是如此的简单!

  • 《最受欢迎的男友职业排行榜Top10》

  • CTO 写的代码,真是绝了

最近面试BAT,整理一份面试资料《Java面试BAT通关手册》,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。获取方式:点“在看”,关注公众号并回复 666 领取,更多内容陆续奉上。
明天见(。・ω・。)

Spring Boot 中 @EnableXXX 注解的驱动逻辑相关推荐

  1. Spring Boot 中 @EnableXXX 注解的驱动逻辑探讨

    作者 | 温安适 来源 | https://juejin.im/post/5efdd689e51d4534af686ca9 工作中经常用到,如下注解: @EnableEurekaClient @Ena ...

  2. enablefeignclients 注解_Spring Boot 中 @EnableXXX 注解的驱动逻辑

    作者 | 温安适 来源 | https://juejin.im/post/5efdd689e51d4534af686ca9 点击赠书:聊聊「分布式架构」那些事儿 工作中经常用到,如下注解: @Enab ...

  3. spring boot 自定义@EnableXXX注解

    前言 spring boot 自带了很多@EnableXXX这样的注解,通过这些注解我们可以很方便地启用某些功能,比如@EnableAutoConfiguration用来开启自动装配的功能.内部实现主 ...

  4. Spring Boot中常见注解诠释

    一:@Mapper和@MapperScan 1.@Mapper @Mapper 将接口交给Spring进行管理,为这个接口生成一个实现类,让别的类进行引用.不再写mapper映射文件. @Mapper ...

  5. spring boot中的注解

    spring boot是个好东西,可以不用容器直接在main方法中启动,而且无需配置文件,方便快速搭建环境. 1.@RequestMapping的params参数使用场景 当同一个类中的两个方法的功能 ...

  6. springboot初始化逻辑_详解Spring Boot中初始化资源的几种方式

    假设有这么一个需求,要求在项目启动过程中,完成线程池的初始化,加密证书加载等功能,你会怎么做?如果没想好答案,请接着往下看.今天介绍几种在Spring Boot中进行资源初始化的方式,帮助大家解决和回 ...

  7. Spring Boot中Spring data注解的使用

    文章目录 Spring Data Annotations @Transactional @NoRepositoryBean @Param @Id @Transient @CreatedBy, @Las ...

  8. springboot异步注解_Spring Boot 2 :Spring Boot 中的响应式编程和 WebFlux 入门

    [小宅按]Spring 5.0 中发布了重量级组件 Webflux,拉起了响应式编程的规模使用序幕. WebFlux 使用的场景是异步非阻塞的,使用 Webflux 作为系统解决方案,在大多数场景下可 ...

  9. Spring Boot中的缓存支持(一)注解配置与EhCache使用

    随着时间的积累,应用的使用用户不断增加,数据规模也越来越大,往往数据库查询操作会成为影响用户使用体验的瓶颈,此时使用缓存往往是解决这一问题非常好的手段之一.Spring 3开始提供了强大的基于注解的缓 ...

最新文章

  1. Linq 等式运算符:SequenceEqual
  2. Linux循环链表删除节点,删除循环单链表开头元素
  3. 构建iscsi网络存储服务
  4. 一加7充电_一加真无线耳机曝光,65W快速充电器获认证
  5. 软件构建--项目总结
  6. MATLAB语言初步学习(六)
  7. luogu4360 锯木厂选址 (斜率优化dp)
  8. 智慧灯杆供电系统设计要求
  9. 【YY手机】用AVR单片机制作手机系列教程-基础篇
  10. 照片估计明星身高matlab,娱乐圈部分明星真实身高 (净高版)
  11. myeclipse8.5汉化
  12. excel使用教程_Excel筛选和高级筛选怎么使用?筛选最全教程
  13. vant4的showtoast非正常显示问题
  14. 注册一个微信公众号的测试平台
  15. 360 浏览器页面兼容 IE7
  16. 在线思维导图怎么用?MindNow给你分享
  17. Python基于basemap画论文级别的多子图空间场
  18. 华为HGU改成SFU的方法
  19. 利用Office Online 实现文档在线预览
  20. java查看线程使用情况,查看java的线程cpu使用情况

热门文章

  1. 灭霸—个人冲刺(4)
  2. 反射拷贝对象的思路:
  3. JavaScript夯实基础系列(四):原型
  4. Linux C中strcpy , strncpy , strlcpy 的区别
  5. Winform开发中常见界面的DevExpress处理操作
  6. ZOJ 3735 dp
  7. [转]wxODBC(wxWidgets)中使用驱动程序方式打开数据库
  8. (转载)新年——顺民的牢骚
  9. 使用 SAX 解析器简化文档处理程序的编写
  10. 数据结构与算法:05 Leetcode同步练习(一)