Spring 用的爽不爽?在你爽的同时,你也知道为什么这么爽,在 Spring 中,@Configuration 是一个重重重要的注解,那么配置类为什么要添加 @Configuration 注解呢?本篇文章就带你 get 这个点。
不加 @Configuration 导致的问题
我们先来看看如果不在配置类上添加 @Configuration 注解会有什么问题,代码示例如下:
不添加 @Configuration 注解运行结果:
create dmzService
create A by dmzService
create dmzService
添加 @Configuration 注解运行结果:
create dmzService
create A by dmzService
在上面的例子中,我们会发现没有添加 @Configuraion注解时dmzService被创建了两次。
这是因为第一次创建是被 Spring 容器所创建的,Spring 调用这个 dmzService() 创建了一个 Bean 被放入了单例池中(没有添加其它配置默认是单例的)。
第二次创建是 Spring 容器在创建 a 时调用了a(),而 a() 又调用了 dmzService() 方法。
这样的话,就出现问题了。
第一,对于 dmzService 而言,它被创建了两次,打破了单例的条件。
第二,对于 a 而言,它所依赖的 dmzService 不是 Spring 所管理的,而是直接调用的一个普通的 java 方法创建的普通对象。这个对象没有被 Spring 对象管理,首先它的域(Scope)定义失效了,其次它没有经过一个完整的生命周期,那么我们所定义所有的 Bean 的后置处理器都没有作用到它身上,其中就包括了完成 AOP 的后置处理器,所以 AOP 也失效了。
上面的分析不能说服你的话,我们可以看看官方在 @Bean 上给出的这一段注释
首先,Spring 就在注释中指出了,通常来说,BeanMethod 一般都声明在一个由 @Configuration 注解标注的类中,在这种情况下,BeanMethod 可能直接引用了在同一个类中申明的 beanMethod , 就像本文给出的例子那样,a() 直接引用了 dmzService(),我们重点再看看划红线的部分,通过调用另外一个 beanMethod 进入的 Bean的引用会被保证是遵从域定义以及 AOP 语义的,就像 getBean 所做的那样。这是怎么实现的呢?在最后被红线标注的地方也有说明,是通过在运行时期为没有被 @Configuration 注解标注的配置类生成一个 CGLIB 的子类。
源码分析
那么,Spring 是在什么时候创建的代理呢?到目前为止我们应该没有进入 Spring 启动流程的任何关键代码,那么我们不妨带着这个问题继续往下看。目前来说我们已经阅读到了Spring执行流程图中的3-5步,也就是org.springframework.context.support.AbstractApplicationContext#invokeBeanFactoryPostProcessors方法。其执行逻辑如下:
在之前的分析中我们已经知道了,这个方法的主要作用就是执行 BeanFactoryPostProcessor 中的方法,首先执行的是 BeanDefinitionRegistryPostProcessor(继承了BeanFactoryPostProcessor)的postProcessBeanDefinitionRegistry方法,然后执行postProcessBeanFactory方法。
那么目前为止容器中有哪些 BeanFactoryPostProcessor 呢?
Spring 内置的 BeanFactoryPostProcessor 在当前这个时机就已经被注册到容器中的只有一个,就是 ConfigurationClassPostProcessor,在之前的文章中我们已经分析过了它的postProcessBeanDefinitionRegistry方法,这个方法主要是为了完成配置类的解析以及对组件的扫描。
紧接着我们就来看看它的postProcessBeanFactory方法做了什么。其源码如下:
enhanceConfigurationClasses
接下来我们来分析一下 enhanceConfigurationClasses 方法
这段代码非常简单,其目的就是生成一个 enhancedClass(经过了 cglib 增强的 class),然后用其替换目标配置类对应的 BeanDefinition 中的 beanClass 属性。
那么我们接下来需要分析的就是 enhancedClass 是如何生成的。它的核心代码在ConfigurationClassEnhancer中,所以我们要分析下ConfigurationClassEnhancer的源码,在分析它的源码前,我们需要对 cglib 有一定的了解。
1、cglib原理分析
在分析 cglib 前,我们先通过一个例子来认识一下什么是 cglib。
1.1、使用示例
运行结果为:
可以看到,通过上面这种方式,我们已经对 Target 中的方法完成了增