@Enable驱动逻辑

这个系列分为5篇

1. @Component,@Service等注解是如何被解析的

2. @Enable驱动原理

3. @EnableAutoConfiguration处理逻辑

4. spring,springBoot事件

5. 仅需四步,写一个springboot starter

前言

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

@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 Framework2.0引入的可扩展的XML编程机制,XML Schema命名空间需要与Handler建立映射关系。

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

查看ContextNamespaceHandler 源码

1public class ContextNamespaceHandler extends NamespaceHandlerSupport { 2 @Override 3 public void init() { 4 //省略其他代码 5 registerBeanDefinitionParser("annotation-config", 6 new AnnotationConfigBeanDefinitionParser()); 7 } 8}

context:annotation-config 对应AnnotationConfigBeanDefinitionParser这个就是要找的入口

找核心类

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

AnnotationConfigUtils.registerAnnotationConfigProcessors中注册了ConfigurationClassPostProcessor。

ConfigurationClassPostProcessor类注释说明

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

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

否则需要手工编程

3. ConfigurationClassPostProcessor第一优先级,保证

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

扩展

AnnotationConfigApplicationContext中new AnnotationBeanDefinitionReader也调用了 AnnotationConfigUtils . registerAnnotationConfigProcessors

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

找核心方法

查看 ConfigurationClassPostProcessor 的层级关系为

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

postProcessBeanDefinitionRegistry了。

postProcessBeanDefinitionRegistry其内部有2个方法

  1. postProcessBeanDefinitionRegistry在BeanDefinition注册之后,BeanFactoryPostProcessor执行之前,修改或重写BeanDefinition
  2. 继承自BeanFactoryPostProcessor的postProcessBeanFactory,BeanDefinition加载之后,Bean实例化之前,重写或添加BeanDefinition,修改BeanFactory

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

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

梳理流程

1public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) { 2 List<BeanDefinitionHolder> configCandidates = new ArrayList<>(); 3 String[] candidateNames = registry.getBeanDefinitionNames(); 4 5 for (String beanName : candidateNames) { 6 BeanDefinition beanDef = registry.getBeanDefinition(beanName); 7 if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) || 8 ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) { 9 if (logger.isDebugEnabled()) { 10 logger.debug("Bean definition has already been processed as a configuration class: " + beanDef); 11 } 12 } 13 else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) { 14 configCandidates.add(new BeanDefinitionHolder(beanDef, beanName)); 15 } 16 } 17 18 // 没有找到 @Configuration classes 立即返回 19 if (configCandidates.isEmpty()) { 20 return; 21 } 22 //根据@Order 值进行排序 23 configCandidates.sort((bd1, bd2) -> { 24 int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition()); 25 int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition()); 26 return Integer.compare(i1, i2); 27 }); 28 //通过封闭的应用程序上下文, 检测任何自定义bean名称生成策略 29 supplied through the enclosing application context 30 SingletonBeanRegistry sbr = null; 31 if (registry instanceof SingletonBeanRegistry) { 32 sbr = (SingletonBeanRegistry) registry; 33 if (!this.localBeanNameGeneratorSet) { 34 BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR); 35 if (generator != null) { 36 this.componentScanBeanNameGenerator = generator; 37 this.importBeanNameGenerator = generator; 38 } 39 } 40 } 41 if (this.environment == null) { 42 this.environment = new StandardEnvironment(); 43 } 44 // 解析@Configuration class 45 ConfigurationClassParser parser = new ConfigurationClassParser( 46 this.metadataReaderFactory, this.problemReporter, this.environment, 47 this.resourceLoader, this.componentScanBeanNameGenerator, registry); 48 49 Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates); 50 Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size()); 51 do { 52 parser.parse(candidates); 53 parser.validate(); 54 55 Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses()); 56 configClasses.removeAll(alreadyParsed); 57 // 读ConfigurationClass的信息,创建BeanDefinition 58 if (this.reader == null) { 59 this.reader = new ConfigurationClassBeanDefinitionReader( 60 registry, this.sourceExtractor, this.resourceLoader, this.environment, 61 this.importBeanNameGenerator, parser.getImportRegistry()); 62 } 63 this.reader.loadBeanDefinitions(configClasses); 64 alreadyParsed.addAll(configClasses); 65 66 candidates.clear(); 67 if (registry.getBeanDefinitionCount() > candidateNames.length) { 68 String[] newCandidateNames = registry.getBeanDefinitionNames(); 69 Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames)); 70 Set<String> alreadyParsedClasses = new HashSet<>(); 71 for (ConfigurationClass configurationClass : alreadyParsed) { 72 alreadyParsedClasses.add(configurationClass.getMetadata().getClassName()); 73 } 74 for (String candidateName : newCandidateNames) { 75 if (!oldCandidateNames.contains(candidateName)) { 76 BeanDefinition bd = registry.getBeanDefinition(candidateName); 77 if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) && 78 !alreadyParsedClasses.contains(bd.getBeanClassName())) { 79 candidates.add(new BeanDefinitionHolder(bd, candidateName)); 80 } 81 } 82 } 83 candidateNames = newCandidateNames; 84 } 85 } 86 while (!candidates.isEmpty()); 87 88 // 将ImportRegistry注册为bean以支持importware@Configuration类 89 if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) { 90 sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry()); 91 } 92 if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) { 93 // Clear cache in externally provided MetadataReaderFactory; this is a no-op 94 // for a shared cache since it'll be cleared by the ApplicationContext. 95 ((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache(); 96 } 97}

ConfigurationClassPostProcessor#processConfigBeanDefinitions核心如下:

  1. 根据@Order 值进行排序
  2. 解析@Configuration class 为ConfigurationClass对象
  3. 读ConfigurationClass的信息,创建BeanDefinition
  4. 将ImportRegistry注册为bean以支持importware@Configuration类

重点关注解析方法

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

查阅其源码如下:

ConfigurationClassParser#doProcessConfigurationClass代码如下:

1protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass) 2 throws IOException { 3 4 if (configClass.getMetadata().isAnnotated(Component.class.getName())) { 5 // Recursively process any member (nested) classes first 6 processMemberClasses(configClass, sourceClass); 7 } 8 9 // Process any @PropertySource annotations 10 for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable( 11 sourceClass.getMetadata(), PropertySources.class, 12 org.springframework.context.annotation.PropertySource.class)) { 13 if (this.environment instanceof ConfigurableEnvironment) { 14 processPropertySource(propertySource); 15 } 16 else { 17 logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() + 18 "]. Reason: Environment must implement ConfigurableEnvironment"); 19 } 20 } 21 22 // Process any @ComponentScan annotations 23 Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable( 24 sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class); 25 if (!componentScans.isEmpty() && 26 !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) { 27 for (AnnotationAttributes componentScan : componentScans) { 28 // The config class is annotated with @ComponentScan -> perform the scan immediately 29 Set<BeanDefinitionHolder> scannedBeanDefinitions = 30 this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName()); 31 // Check the set of scanned definitions for any further config classes and parse recursively if needed 32 for (BeanDefinitionHolder holder : scannedBeanDefinitions) { 33 BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition(); 34 if (bdCand == null) { 35 bdCand = holder.getBeanDefinition(); 36 } 37 if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) { 38 parse(bdCand.getBeanClassName(), holder.getBeanName()); 39 } 40 } 41 } 42 } 43 44 // Process any @Import annotations 45 processImports(configClass, sourceClass, getImports(sourceClass), true); 46 47 // Process any @ImportResource annotations 48 AnnotationAttributes importResource = 49 AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class); 50 if (importResource != null) { 51 String[] resources = importResource.getStringArray("locations"); 52 Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader"); 53 for (String resource : resources) { 54 String resolvedResource = this.environment.resolveRequiredPlaceholders(resource); 55 configClass.addImportedResource(resolvedResource, readerClass); 56 } 57 } 58 59 // Process individual @Bean methods 60 Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass); 61 for (MethodMetadata methodMetadata : beanMethods) { 62 configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass)); 63 } 64 65 // Process default methods on interfaces 66 processInterfaces(configClass, sourceClass); 67 68 // Process superclass, if any 69 if (sourceClass.getMetadata().hasSuperClass()) { 70 String superclass = sourceClass.getMetadata().getSuperClassName(); 71 if (superclass != null && !superclass.startsWith("java") && 72 !this.knownSuperclasses.containsKey(superclass)) { 73 this.knownSuperclasses.put(superclass, configClass); 74 // Superclass found, return its annotation metadata and recurse 75 return sourceClass.getSuperClass(); 76 } 77 } 78 79 // No superclass -> processing is complete 80 return null; 81}

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

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

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

浏览代码解决疑问

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

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

1public class ConfigurationClassUtils{ 2//省略其他方法 3public static boolean isFullConfigurationCandidate(AnnotationMetadata metadata) { 4 return metadata.isAnnotated(Configuration.class.getName()); 5} 6}

@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实例。

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )