@configuration注解_聊聊 @Configuration 注解

57c5ba7430552d7be435f06271b70fe7.png

3849c52ff44136e7b14bd1af5fb84d19.png

点击上方蓝字关注我们!

2910051e2305f282a605961387125938.png

分享一个小的知识点,在一个类上添加 @Configuration 注解,这个类就是配置类。

提出有三个问题:

  • 如果我们没有添加这个注解程序还能不能运行?

  • 如果能是为什么?不能又是为什么?

  • 如果能,加不加 @Configuration 注解有什么区别?

我们先创建一个简单的程序。

配置类:

@Configuration@ComponentScan("com.future")public class ConfigClass {}

dao层用来输出:

@Repositorypublic class IndexDao {    public void query() {        System.out.println("query");    }}

测试类:

public class Test {    public static void main(String[] args) {        // 调用 AnnotationConfigApplicationContext 类的无参构造方法,初始化两个对象        // 一个是 AnnotatedBeanDefinitionReader()        // 另一个是 ClassPathBeanDefinitionScanner()        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();        // Register one or more component classes to be processed.        context.register(ApplicationConfig.class);        // 初始化 Spring 环境        context.refresh();        IndexDao indexDao = context.getBean(IndexDao.class);        indexDao.query();    }}

检查一个类是否含有 @Configuration 注解,我们需要进入 refresh() 方法。

@Overridepublic void refresh() throws BeansException, IllegalStateException {    synchronized (this.startupShutdownMonitor) {        ......        try {            // Allows post-processing of the bean factory in context subclasses.            postProcessBeanFactory(beanFactory);            // Invoke factory processors registered as beans in the context.            invokeBeanFactoryPostProcessors(beanFactory);            // Register bean processors that intercept bean creation.            registerBeanPostProcessors(beanFactory);            ......  }  catch (BeansException ex) {    ......  }  finally {    ......  }}

找到 invokeBeanFactoryPostProcessors() 方法,意思是说在上下文中调用注册为 bean 的工厂处理器

进入这个方法。

/** * Instantiate and invoke all registered BeanFactoryPostProcessor beans, * respecting explicit order if given. * 

Must be called before singleton instantiation.

*/protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) { PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors()); // Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime // (e.g. through an @Bean method registered by ConfigurationClassPostProcessor) if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) { beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory)); beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader())); }}

这个方法注释为:实例化和调用所有注册的 BeanFactoryPostProcessor bean。

该方法调用了 invokeBeanFactoryPostProcessors() 方法,进入这个方法。

在 invokeBeanFactoryPostProcessors() 方法中,会调用另外的方法 invokeBeanDefinitionRegistryPostProcessors()。

进入 invokeBeanDefinitionRegistryPostProcessors() 方法。

// 找到这行代码。invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);

参数:currentRegistryProcessors,是一个 List 集合,里边存放的是 Spring 内部自己实现的 BeanDefinitionRegistryPostProcessor 接口的对象。

进入 invokeBeanDefinitionRegistryPostProcessors() 方法,

/** * Invoke the given BeanDefinitionRegistryPostProcessor beans. */private static void invokeBeanDefinitionRegistryPostProcessors(        Collection extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry) {    for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {        postProcessor.postProcessBeanDefinitionRegistry(registry);    }}

注释是说:调用给定的 BeanDefinitionRegistryPostProcessor 的 bean。

里边只有一个方法,进入 postProcessBeanDefinitionRegistry() 方法。该方法为接口方法,调用了 ConfigurationClassPostProcessor 实现类的的方法。

/** * Derive further bean definitions from the configuration classes in the registry. */@Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {    int registryId = System.identityHashCode(registry);    if (this.registriesPostProcessed.contains(registryId)) {        throw new IllegalStateException(                "postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);    }    if (this.factoriesPostProcessed.contains(registryId)) {        throw new IllegalStateException(                "postProcessBeanFactory already called on this post-processor against " + registry);    }    this.registriesPostProcessed.add(registryId);    processConfigBeanDefinitions(registry);}

注释大概意思是从注册表中的配置类派生更多的bean定义

进入 processConfigBeanDefinitions() 这个方法。

/** * Build and validate a configuration model based on the registry of * {@link Configuration} classes. */

意思为:建立和验证一个配置模型给予注册表的 Configuration 类。

找到源码中如下的部分。

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));    }}

这个循环就是我们要找的,首先通过 beanName,获得 BeanDefinition 对象,判断对象是否为 Full 或者 Lite,如果都不是那么说明配置类还没有被处理过,需要进入 else-if 进行处理。

在 if 判断中调用了 checkConfigurationClassCandidate() 方法,进入该方法。

找到如下代码片段。

if (isFullConfigurationCandidate(metadata)) {    beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);}else if (isLiteConfigurationCandidate(metadata)) {    beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);}else {    return false;}

判断当前这个 bd 中是否存在 @Configuration 这个注解,如果存在,则Spring 认为它是一个全注解的类,也就是 if 判断中,将 CONFIGURATION_CLASS_ATTRIBUTE 属性设置为 full,那么进入 if 判断,如果没有加,则 Spring认为它是一个部分注解类,将 CONFIGURATION_CLASS_ATTRIBUTE 属性设置为 lite,进入 else-if 判断。

public static boolean isFullConfigurationCandidate(AnnotationMetadata metadata) {   return metadata.isAnnotated(Configuration.class.getName());}

判断这个类是否有 @Configuration 注解,有的话返回 true,否则返回 false。

public static boolean isLiteConfigurationCandidate(AnnotationMetadata metadata) {    // Do not consider an interface or an annotation...    if (metadata.isInterface()) {        return false;    }    // Any of the typical annotations found?    for (String indicator : candidateIndicators) {        if (metadata.isAnnotated(indicator)) {            return true;        }    }    // Finally, let's look for @Bean methods...    try {        return metadata.hasAnnotatedMethods(Bean.class.getName());    }    catch (Throwable ex) {        if (logger.isDebugEnabled()) {            logger.debug("Failed to introspect @Bean methods on class [" + metadata.getClassName() + "]: " + ex);        }        return false;    }}

在循环当中,注释说找到任何一个典型的注解,有哪些注解,我们看 candidateIndicators 这个对象,查看这个对象它是一个 Set 集合,这个集合会在初始化过程中添加四个对象的 name。

private static final Set candidateIndicators = new HashSet<>(8);static {    candidateIndicators.add(Component.class.getName());    candidateIndicators.add(ComponentScan.class.getName());    candidateIndicators.add(Import.class.getName());    candidateIndicators.add(ImportResource.class.getName());}

结合 for 循环来看,只要配置类中含有这四个注解 (@Component、@ComponentScan、@Import、@ImportResource),就会返回 true。

返回到如下代码。

if (isFullConfigurationCandidate(metadata)) {    beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);}else if (isLiteConfigurationCandidate(metadata)) {    beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);}else {    return false;}

所以可以看到,只要是 @Configuration、@Component、@ComponentScan、@Import、@ImportResource,这五个注解,都会在后边返回 true。

所以只要配置类中使用这五个注解中的一个都会在 processConfigBeanDefinitions() 方法返回 true,并将这个类放到 BeanDefinitionHolder 类型的 List 集合中。

else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {    configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));}
e451895a97a57c01066d37cfe314a8fe.png

现在知道,为什么没有 @Configuration 注解,程序也可以运行。

现在说第二个问题,加与不加有什么区别?

在 test 类当中添加一行代码,可以得到 applicationConfig 的类型。

ApplicationConfig applicationConfig = context.getBean(ApplicationConfig.class);

加 @Configuration 注解

346ed13a70aa8e4f6d69a7a3e7bbf578.png

不加 @Configuration 注解

b3cca5d87abd60bf93bcf0f764b46d11.png

体现着源码中:

首先进入 refresh() 方法,在该方法中找到 invokeBeanFactoryPostProcessors() 方法,并进入,在该方法中找到 invokeBeanFactoryPostProcessors() 方法,并进入。

找到如下代码:

// Now, invoke the postProcessBeanFactory callback of all processors handled so far.invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);

这两个方法唯一不同的是参数。

List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();

区别就是存放的对象不同,BeanFactoryPostProcessor 是父类,而 BeanDefinitionRegistryPostProcessor 即是父类,也是子类。

从前面的代码中就可以看出来。

for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {   if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {      BeanDefinitionRegistryPostProcessor registryProcessor =            (BeanDefinitionRegistryPostProcessor) postProcessor;      registryProcessor.postProcessBeanDefinitionRegistry(registry);      registryProcessors.add(registryProcessor);   }   else {      regularPostProcessors.add(postProcessor);   }}

如果 postProcessor 是 BeanDefinitionRefistryPostProcessor 的子类,那么就把这个类放到 registryProcessor 中,否则放到 regularPostProcessors 中。

进入 invokeBeanFactoryPostProcessors() 方法。

/** * Invoke the given BeanFactoryPostProcessor beans. */private static void invokeBeanFactoryPostProcessors(      Collection extends BeanFactoryPostProcessor> postProcessors, ConfigurableListableBeanFactory beanFactory) {   for (BeanFactoryPostProcessor postProcessor : postProcessors) {      postProcessor.postProcessBeanFactory(beanFactory);   }}

再进入 postProcessBeanFactory() 方法,这是接口方法,要进入 ConfigurationClassPostProcessor 类的 postProcessBeanFactory() 方法。

/** * Prepare the Configuration classes for servicing bean requests at runtime * by replacing them with CGLIB-enhanced subclasses. */@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {   int factoryId = System.identityHashCode(beanFactory);   if (this.factoriesPostProcessed.contains(factoryId)) {      throw new IllegalStateException(            "postProcessBeanFactory already called on this post-processor against " + beanFactory);   }   this.factoriesPostProcessed.add(factoryId);   if (!this.registriesPostProcessed.contains(factoryId)) {      // BeanDefinitionRegistryPostProcessor hook apparently not supported...      // Simply call processConfigurationClasses lazily at this point then.      processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);   }   enhanceConfigurationClasses(beanFactory);   beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));}

enhanceConfigurationClasses() 这个方法就是我们要找的,它的作用是给配置类 ( @Configuration ) 产生 CGLIB 代理。

进入 enhanceConfigurationClasses() 方法,找到如下的代码。

Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>();for (String beanName : beanFactory.getBeanDefinitionNames()) {   BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);   if (ConfigurationClassUtils.isFullConfigurationClass(beanDef)) {      if (!(beanDef instanceof AbstractBeanDefinition)) {         throw new BeanDefinitionStoreException("Cannot enhance @Configuration bean definition '" +               beanName + "' since it is not stored in an AbstractBeanDefinition subclass");      }      else if (logger.isInfoEnabled() && beanFactory.containsSingleton(beanName)) {         logger.info("Cannot enhance @Configuration bean definition '" + beanName +               "' since its singleton instance has been created too early. The typical cause " +               "is a non-static @Bean method with a BeanDefinitionRegistryPostProcessor " +               "return type: Consider declaring such methods as 'static'.");      }      configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef);   }}if (configBeanDefs.isEmpty()) {   // nothing to enhance -> return immediately   return;}

for 循环中有 if 判断,判断是否是一个全注解的类,也就是拥有 full 属性值,如果是全注解的类,那么会将这个类添加到一个 Map 中,否则 Map 为空,直接返回。

这里就可以进行我们 Test 测试,在这里打上断点,查看有或者没有 @Configuration 注解,是否会进入或者不进入第二个 if 判断。

当有 @Configuration 注解,它会跳过 if 判断。

d9cbb93564d4b4ff360f7b1873136ae4.png

当没有 @Configuration 注解,它会进入 if 判断,直接返回。

5aa76fe82cf6cffd907bc82db030be6a.png

当跳过 if 判断后,就会执行 CGLIB 代理。

我们可以看到两个注释,第一个注释,如果 @Configuration 注解的类被代理,那么它总是代理这个目标类。

也就是说如果加了 @Configuration 注解,那么我们得到的就是一个被 CGLIB 代理的代理类。

第二个注释,设置一个增强的子类,对于用户指定的bean类。

我们指定的类是含有 @Configuration 注解的类,所以这里的 configClass 就是我们的 ConfigClass 类。

ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();for (Map.Entry<String, AbstractBeanDefinition> entry : configBeanDefs.entrySet()) {    AbstractBeanDefinition beanDef = entry.getValue();    // If a @Configuration class gets proxied, always proxy the target class    beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);    try {        // Set enhanced subclass of the user-specified bean class        Class> configClass = beanDef.resolveBeanClass(this.beanClassLoader);        if (configClass != null) {            Class> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);            if (configClass != enhancedClass) {                if (logger.isTraceEnabled()) {                    logger.trace(String.format("Replacing bean definition '%s' existing class '%s' with " +                            "enhanced class '%s'", entry.getKey(), configClass.getName(), enhancedClass.getName()));                }                beanDef.setBeanClass(enhancedClass);            }        }    }    catch (Throwable ex) {        throw new IllegalStateException("Cannot load configuration class: " + beanDef.getBeanClassName(), ex);    }}

我们进入 enhance() 这个方法。

Class> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);

先看到一个 if,是说如果这个类已经被增强了,那么直接返回这个增强的类。

如果没有被代理,就先调用 newEnhancer() 方法创建一个增强器 Enhancer。

再看 createClass() 方法,使用这个增强器,生成代理类的 class 对象。

createClass() 方法上的注释也说了,使用增强器生成一个超类的子类,确保回调注册了新的子类。

/** * Loads the specified class and generates a CGLIB subclass of it equipped with * container-aware callbacks capable of respecting scoping and other bean semantics. * @return the enhanced subclass */public Class> enhance(Class> configClass, @Nullable ClassLoader classLoader) {  if (EnhancedConfiguration.class.isAssignableFrom(configClass)) {    if (logger.isDebugEnabled()) {      logger.debug(String.format("Ignoring request to enhance %s as it has " +          "already been enhanced. This usually indicates that more than one " +          "ConfigurationClassPostProcessor has been registered (e.g. via " +          "). This is harmless, but you may " +          "want check your configuration and remove one CCPP if possible",          configClass.getName()));    }    return configClass;  }  Class> enhancedClass = createClass(newEnhancer(configClass, classLoader));  if (logger.isTraceEnabled()) {    logger.trace(String.format("Successfully enhanced %s; enhanced class name is: %s",        configClass.getName(), enhancedClass.getName()));  }  return enhancedClass;}

到此我们就解决了刚开始说的三个问题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值