66 KiB
@Configuration
[TOC]
1、注解说明
@Configuration
是Spring框架中的一个核心注解,主要用于类级别,标识该类是一个配置类。配置类是Spring IoC容器的重要组成部分,它包含了Spring容器如何初始化和配置应用中的bean的信息。在配置类中,你可以定义一个或多个公共的@Bean
注解方法,这些方法会实例化、配置并初始化新的对象,然后这些对象被Spring IoC容器管理
2、注解源码
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
@AliasFor(annotation = Component.class)
String value() default "";
boolean proxyBeanMethods() default true;
}
3、字段描述
3.1、value
用于指定Bean的名称的。这个属性是@Component
注解的一部分,因为@Configuration
注解是元注解@Component
的特化
3.2、proxyBeanMethods
这是Spring 5.2新增的属性,用于控制@Configuration
类的@Bean
方法是否应该被CGLIB代理。如果proxyBeanMethods
设置为true (full模式),那么@Bean方法会被代理,每次调用都会检查Spring上下文,确保返回的是同一个bean实例。如果设置为false(lite模式),那么@Bean
方法会像普通方法一样执行,每次调用都会返回一个新的实例。这种方式可能会使应用启动得更快,因为不需要生成CGLIB代理类,但是你必须自己处理@Bean方法之间的引用。
4、如何使用
首先来看看启动类入口,上下文环境使用AnnotationConfigApplicationContext
(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个MyConfiguration
组件类,在最后我们调用两次获取MyBean对象并打印查看内存地址。
public class ConfigurationApplication {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class);
MyConfiguration configuration = context.getBean(MyConfiguration.class);
System.out.println(configuration.myBean());
System.out.println(configuration.myBean());
}
}
创建MyBean类,作为IOC容器的Bean对象
public class MyBean {
private String beanId;
public String getBeanId() {
return beanId;
}
public void setBeanId(String beanId) {
this.beanId = beanId;
}
}
4.1、测试proxyBeanMethods为true情况
创建MyConfiguration
类,作为spring的启动配置引导类,由于@Configuration
中的proxyBeanMethods字段默认为true,此处使用缺省值
@Configuration
public class MyConfiguration {
@Bean
public MyBean myBean(){
return new MyBean();
}
}
通过运行main方法,我们发现打印出来的2个对象是一致的
4.2、测试proxyBeanMethods为false情况
创建MyConfiguration
类,作为spring的启动配置引导类,此处proxyBeanMethods设置为false
@Configuration(proxyBeanMethods = false)
public class MyConfiguration {
@Bean
public MyBean myBean(){
return new MyBean();
}
}
通过再次运行main方法,我们发现打印出来的2个对象是不一致的
4.3、@Configuration注解几种组合用法
@Configuration
注解在Spring框架中通常和其他注解一起使用,以满足各种各样的配置需求。下面是一些常见的组合用法:
4.3.1、与@Bean
组合
这是最常见的用法,@Bean
注解用在@Configuration
类的方法上,这个方法的返回值会作为一个bean注册到Spring容器中。
@Configuration
public class MyConfiguration {
@Bean
public MyBean myBean(){
return new MyBean();
}
}
4.3.2、与@ComponentScan
组合
@ComponentScan
注解用来配置Spring哪些包进行扫描。Spring会扫描指定包及其子包下的所有类,如果这些类上有@Component
、@Controller
、@Service
、@Repository
或者@Configuration
等注解,Spring就会把这些类作为Bean定义注册到容器中。
@Configuration
@ComponentScan(basePackages = "com.xcs.spring.bean")
public class MyConfiguration {
}
4.3.3、与@Import
组合
@Import
注解用来导入其他的@Configuration
类。这样可以把多个小的、专门用途的配置类组合成一个大的配置类
@Configuration
@Import({DatabaseConfig.class, WebConfig.class})
public class MyConfiguration {
}
4.3.4、与@PropertySource
组合
@PropertySource
注解用来指定加载哪些属性文件。加载的属性会添加到Spring的Environment
中,可以通过@Value
注解或者Environment
对象来获取属性值。
@Configuration
@PropertySource("classpath:application.properties")
public class MyConfiguration {
}
4.3.5、与@Enable*
组合
@Enable*
是一类注解,用来开启Spring的某些功能,比如@EnableTransactionManagement
开启事务管理,@EnableScheduling
开启计划任务,@EnableAsync
开启异步执行等。这些注解必须用在@Configuration
类上才能生效。
@Configuration
@EnableTransactionManagement
public class MyConfiguration {
}
4.3.6、与@Profile
组合
@Profile
注解用来定义配置类或bean定义适用的环境。只有当前环境和@Profile
指定的环境匹配时,配置类或bean定义才会被注册到容器中。
@Configuration
@Profile("development")
public class MyConfiguration {
}
4.3.7、与@Configuration内嵌组合
你可以在一个@Configuration
类中嵌套其他的@Configuration
类,这是一种组织配置的方式,可以让你的配置更加模块化和层次化。这种方式通常用在一个大的配置类中,你可以将某些特定的配置组合在一起,放在一个内嵌的@Configuration
类中。
@Configuration
public class MyConfiguration {
@Bean
public MyBean myBean() {
return new MyBean();
}
@Configuration
public static class MyDatabaseConfig {
@Bean
public DataSource dataSource() {
return new DataSource();
}
}
@Configuration
public static class MyWebConfig {
@Bean
public Controller controller() {
return new Controller();
}
}
}
4.3.8、与@Conditional
组合
可以使得某个bean定义或者配置类只有在特定的条件满足时才会被注册。例如,我们可以定义一个条件类检查某个系统属性是否存在,然后用@Conditional
注解将这个条件类应用到bean定义或配置类上。
@Configuration
@Conditional(MyCondition.class)
public class MyConfiguration {
// ...
}
public class MyCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return System.getProperty("myProperty") != null;
}
}
4.3.9、与Environment
API组合
可以用来获取系统属性、环境变量、配置文件中的属性等,然后根据这些属性来决定要创建哪些bean。例如,我们可以在@Bean
方法中检查一个特定的环境变量,然后根据这个环境变量的值来决定创建哪个版本的bean。
@Configuration
public class MyConfiguration {
@Autowired
Environment env;
@Bean
public MyBean myBean() {
if ("version1".equals(env.getProperty("myBean.version"))) {
return new MyBeanVersion1();
} else {
return new MyBeanVersion2();
}
}
}
4.3.9、组合总结
以上这些Spring注解是可以多个组合使用的,而且这种组合是很常见的。使用这些注解组合可以实现高级的配置策略,增加配置的灵活性。例如,你可以在一个@Configuration
类中同时使用@Profile
、@Bean
、@Import
等注解,每个注解都有其特定的用途。通过组合多个注解,我们可以为Spring应用创建出极具灵活性和动态性的配置。需要注意的是,虽然使用多个注解可以带来很大的灵活性,但同时也会增加配置的复杂性,因此,我们需要在保持配置简洁和提供足够的灵活性之间找到一个平衡点。
5、原理分析
当Spring容器加载@Configuration
注解的类时,它实际上会创建一个CGLIB代理的子类来替代这个类。在代理类中,每个@Bean
方法都会被重写,以确保每个方法的调用都会通过Spring的Bean工厂,这样就可以保证单例Bean的语义。
在Spring 5.2版本之前,对于@Configuration
注解的类,无论其@Bean
方法是否被设计为单例,Spring容器都会确保它们的行为如同单例Bean一样。即使在同一个配置类中,一个@Bean
方法调用另一个@Bean
方法,也会得到同一个实例。这个特性就是full模式的核心。
// 使用@Configuration,5.2以前没有proxyBeanMethods字段,默认就是full模式
@Configuration
public class MyConfiguration {
@Bean
public ServiceA serviceA() {
return new ServiceA();
}
@Bean
public ServiceB serviceB() {
// 这里调用的 serviceA() 返回的是同一个 ServiceA 实例
return new ServiceB(serviceA());
}
}
在Spring 5.2版本之前,如果你想使用lite模式
// 使用类标记为@Component或@Service等
@Component
public class MyConfiguration {
@Bean
public ServiceA serviceA() {
return new ServiceA();
}
@Bean
public ServiceB serviceB() {
// 这里调用的 serviceA() 每次都会返回一个新的 ServiceA 实例
return new ServiceB(serviceA());
}
}
而在Spring 5.2及之后的版本中,@Configuration
注解有一个proxyBeanMethods
属性,它用来决定是否需要使用CGLIB代理。如果proxyBeanMethods
设置为true
,那么Spring会为这个配置类创建一个CGLIB代理类,这被称为full模式。
// 使用@Configuration,5.2以后新增proxyBeanMethods字段,默认只为true,表示走full模式
@Configuration(proxyBeanMethods = true)
public class MyConfiguration {
@Bean
public ServiceA serviceA() {
return new ServiceA();
}
@Bean
public ServiceB serviceB() {
// 这里调用的 serviceA() 返回的是同一个 ServiceA 实例
return new ServiceB(serviceA());
}
}
如果proxyBeanMethods
设置为false
,那么Spring不会创建代理类,这被称为lite模式。在lite模式下,@Bean
方法的调用就像普通的Java方法调用一样,不会通过Spring的Bean工厂,也不会确保单例语义。因此,即使你将一个Bean定义为单例,如果你在一个配置类中多次调用这个Bean的方法,也会得到不同的实例。这种方式在某些场景下可能会更快,但是需要你自己来保证配置的正确性。
@Configuration(proxyBeanMethods = false)
public class MyConfiguration {
@Bean
public ServiceA serviceA() {
return new ServiceA();
}
@Bean
public ServiceB serviceB() {
// 这里调用的 serviceA() 每次都会返回一个新的 ServiceA 实例
return new ServiceB(serviceA());
}
}
前面说到@Configuration
是走的CGLIB代理来实现的,那么我们可以借助一个arthas
的工具,查看一下是否生成了代理类,被代理后的类长什么样的呢?
5.1、分析proxyBeanMethods为true
首先MyConfiguration
类中的proxyBeanMethods
字段默认为true,此处就不设置了,使用缺省值。
@Configuration
public class MyConfiguration {
@Bean
public MyBean myBean(){
return new MyBean();
}
}
下面是我的启动类,通过context.getBean(MyConfiguration.class)
获得MyConfiguration
对象,最后通过configuration.getClass().getName()打印类名,查看是原始类名还是被CGLIB代理后的类名,最后使用了System.in.read()是防止spring程序结束退出程序。
public class ConfigurationApplication {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class);
MyConfiguration configuration = context.getBean(MyConfiguration.class);
System.out.println(configuration.myBean());
System.out.println(configuration.myBean());
System.out.println("MyConfiguration = " + configuration.getClass().getName());
try {
System.in.read();
} catch (IOException e) {
e.printStackTrace();
}
}
}
运行结果发现,MyConfiguration
已经成功被CGLIB代理,代理类为com.xcs.spring.MyConfiguration$$EnhancerBySpringCGLIB$$fce000ac
,接下来我们使用arthas工具反编译一下此类,查看具体被代理后的代码是什么样子的呢?
通过arthas的反编译,首先我们发现 MyConfiguration$$EnhancerBySpringCGLIB$$fce000ac
是MyConfiguration
的一个子类,并实现了ConfigurationClassEnhancer.EnhancedConfiguration
接口中的setBeanFactory方法
package com.xcs.spring;
import com.xcs.spring.MyConfiguration;
import com.xcs.spring.bean.MyBean;
import java.lang.reflect.Method;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.cglib.core.ReflectUtils;
import org.springframework.cglib.core.Signature;
import org.springframework.cglib.proxy.Callback;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import org.springframework.cglib.proxy.NoOp;
import org.springframework.context.annotation.ConfigurationClassEnhancer;
public class MyConfiguration$$EnhancerBySpringCGLIB$$fce000ac extends MyConfiguration implements ConfigurationClassEnhancer.EnhancedConfiguration {
// 标记是否已经将CGLIB的回调对象绑定到了当前对象上。
private boolean CGLIB$BOUND;
// 存储CGLIB用于创建代理对象的工厂数据。
public static Object CGLIB$FACTORY_DATA;
// 线程局部变量,用于存储当前线程的CGLIB回调对象。
private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
// 存储静态的CGLIB回调对象
private static final Callback[] CGLIB$STATIC_CALLBACKS;
// CGLIB的回调对象,用于处理方法调用。Spring通过这些回调对象,拦截对@Bean方法的调用,并确保返回的是Spring容器管理的bean实例。
private MethodInterceptor CGLIB$CALLBACK_0;
private MethodInterceptor CGLIB$CALLBACK_1;
private NoOp CGLIB$CALLBACK_2;
// CGLIB的回调过滤器,用于决定某个方法调用应该使用哪个回调对象来处理。
private static Object CGLIB$CALLBACK_FILTER;
// 被代理的方法,例如myBean()方法和setBeanFactory()方法。
private static final Method CGLIB$myBean$0$Method;
// CGLIB的MethodProxy对象,用于代理方法调用。
private static final MethodProxy CGLIB$myBean$0$Proxy;
// 这个字段是一个空的参数数组,用于在调用没有参数的方法时使用。
private static final Object[] CGLIB$emptyArgs;
private static final Method CGLIB$setBeanFactory$5$Method;
private static final MethodProxy CGLIB$setBeanFactory$5$Proxy;
// MyConfiguration类实现了BeanFactoryAware接口,因此Spring在创建bean实例后,会自动调用setBeanFactory方法
public BeanFactory $$beanFactory;
public MyConfiguration$$EnhancerBySpringCGLIB$$fce000ac() {
MyConfiguration$$EnhancerBySpringCGLIB$$fce000ac myConfiguration$$EnhancerBySpringCGLIB$$fce000ac = this;
MyConfiguration$$EnhancerBySpringCGLIB$$fce000ac.CGLIB$BIND_CALLBACKS(myConfiguration$$EnhancerBySpringCGLIB$$fce000ac);
}
static {
MyConfiguration$$EnhancerBySpringCGLIB$$fce000ac.CGLIB$STATICHOOK2();
MyConfiguration$$EnhancerBySpringCGLIB$$fce000ac.CGLIB$STATICHOOK1();
}
public final MyBean myBean() {
// 首先,它尝试获取一个名为`CGLIB$CALLBACK_0,这个拦截器是CGLIB回调机制的核心,它负责处理`@Bean`方法的调用。
MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_0;
// 如果拦截器不存在,那么它会尝试调用`CGLIB$BIND_CALLBACKS(this)`方法,该方法负责将拦截器绑定到当前对象上。
if (methodInterceptor == null) {
MyConfiguration$$EnhancerBySpringCGLIB$$fce000ac.CGLIB$BIND_CALLBACKS(this);
// 绑定拦截器后,它再次获取拦截器。
methodInterceptor = this.CGLIB$CALLBACK_0;
}
// 如果拦截器存在,那么它就调用拦截器的`intercept()`方法,处理对`myBean()`方法的调用。`intercept()`方法的参数包括:当前对象、代表`myBean()`方法的`Method`对象、一个空的参数数组(因为`myBean()`方法没有参数),以及一个`MethodProxy`对象(用于通过CGLIB调用超类的原始方法)。
if (methodInterceptor != null) {
return (MyBean)methodInterceptor.intercept(this, CGLIB$myBean$0$Method, CGLIB$emptyArgs, CGLIB$myBean$0$Proxy);
}
// 如果拦截器不存在,那么它就直接调用超类的`myBean()`方法,即原始的`MyConfiguration`类的`myBean()`方法。
return super.myBean();
}
@Override
public final void setBeanFactory(BeanFactory beanFactory) throws BeansException {
// 获取一个名为CGLIB$CALLBACK_1的MethodInterceptor(方法拦截器)
MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_1;
// 如果拦截器不存在,那么它会尝试调用CGLIB$BIND_CALLBACKS(this)方法,该方法负责将拦截器绑定到当前对象上
if (methodInterceptor == null) {
MyConfiguration$$EnhancerBySpringCGLIB$$fce000ac.CGLIB$BIND_CALLBACKS(this);
// 绑定拦截器后,它再次获取拦截器。
methodInterceptor = this.CGLIB$CALLBACK_1;
}
// 如果拦截器存在,那么它就调用拦截器的intercept()方法,处理对setBeanFactory方法的调用。
// 这个intercept()方法的参数包括:当前对象、代表setBeanFactory方法的Method对象、一个包含一个元素(即传入的BeanFactory)的参数数组,
// 以及一个MethodProxy对象(用于通过CGLIB调用超类的原始方法)。
if (methodInterceptor != null) {
Object object = methodInterceptor.intercept(this, CGLIB$setBeanFactory$5$Method, new Object[]{beanFactory},CGLIB$setBeanFactory$5$Proxy);
return;
}
// 如果拦截器不存在,那么它就直接调用超类的setBeanFactory方法,即原始的MyConfiguration类的setBeanFactory方法。
super.setBeanFactory(beanFactory);
}
public static void CGLIB$SET_STATIC_CALLBACKS(Callback[] callbackArray) {
CGLIB$STATIC_CALLBACKS = callbackArray;
}
public static void CGLIB$SET_THREAD_CALLBACKS(Callback[] callbackArray) {
CGLIB$THREAD_CALLBACKS.set(callbackArray);
}
// CGLIB库的内部实现细节,一般情况下,你不需要直接使用或理解这些代码
public static MethodProxy CGLIB$findMethodProxy(Signature signature) {
String string = ((Object)signature).toString();
switch (string.hashCode()) {
case -1352508034: {
if (!string.equals("myBean()Lcom/xcs/spring/bean/MyBean;")) break;
return CGLIB$myBean$0$Proxy;
}
case 2095635076: {
if (!string.equals("setBeanFactory(Lorg/springframework/beans/factory/BeanFactory;)V")) break;
return CGLIB$setBeanFactory$5$Proxy;
}
}
return null;
}
final void CGLIB$setBeanFactory$5(BeanFactory beanFactory) throws BeansException {
super.setBeanFactory(beanFactory);
}
// 通过这个方法,CGLIB可以在需要的时候将回调对象绑定到代理对象上,
// 然后通过这些回调对象来处理方法调用。这是CGLIB实现方法拦截的一部分,也是Spring实现@Configuration注解的重要机制。
private static final void CGLIB$BIND_CALLBACKS(Object object) {
block2: {
Object object2;
MyConfiguration$$EnhancerBySpringCGLIB$$fce000ac myConfiguration$$EnhancerBySpringCGLIB$$fce000ac;
block3: {
// 首先,它将传入的对象转换为MyConfiguration$$EnhancerBySpringCGLIB$$fce000ac类型。
myConfiguration$$EnhancerBySpringCGLIB$$fce000ac = (MyConfiguration$$EnhancerBySpringCGLIB$$fce000ac)object;
// 如果这个对象还没有绑定回调对象(即,CGLIB$BOUND字段为false),跳出整个代码块block2
if (myConfiguration$$EnhancerBySpringCGLIB$$fce000ac.CGLIB$BOUND) break block2;
// 那么它将CGLIB$BOUND字段设为true,表示已经绑定了回调对象。
myConfiguration$$EnhancerBySpringCGLIB$$fce000ac.CGLIB$BOUND = true;
// 然后,它尝试从CGLIB$THREAD_CALLBACKS线程局部变量中获取回调对象。
object2 = CGLIB$THREAD_CALLBACKS.get();
if (object2 != null) break block3;
// 如果没有找到,就尝试获取静态的CGLIB$STATIC_CALLBACKS回调对象。
object2 = CGLIB$STATIC_CALLBACKS;
// 跳出整个代码块block2
if (CGLIB$STATIC_CALLBACKS == null) break block2;
}
// 如果找到了回调对象,就将它们绑定到当前对象上。例如,
// CGLIB$CALLBACK_2字段是一个NoOp对象,它是Callback接口的一个实现,
// 表示一个没有操作的回调。CGLIB$CALLBACK_0和CGLIB$CALLBACK_1字段是MethodInterceptor对象,它们用于处理方法调用。
Callback[] callbackArray = (Callback[])object2;
MyConfiguration$$EnhancerBySpringCGLIB$$fce000ac myConfiguration$$EnhancerBySpringCGLIB$$fce000ac2 = myConfiguration$$EnhancerBySpringCGLIB$$fce000ac;
myConfiguration$$EnhancerBySpringCGLIB$$fce000ac2.CGLIB$CALLBACK_2 = (NoOp)callbackArray[2];
myConfiguration$$EnhancerBySpringCGLIB$$fce000ac2.CGLIB$CALLBACK_1 = (MethodInterceptor)callbackArray[1];
myConfiguration$$EnhancerBySpringCGLIB$$fce000ac2.CGLIB$CALLBACK_0 = (MethodInterceptor)callbackArray[0];
}
}
// 这个方法在代理类的静态初始化块中被调用,它初始化了代理类需要的一些字段和数据结构。
// 静态初始化块是Java语言的一部分,用于初始化静态字段。这个块在类被加载时执行一次。
static void CGLIB$STATICHOOK1() {
CGLIB$THREAD_CALLBACKS = new ThreadLocal();
CGLIB$emptyArgs = new Object[0];
Class<?> clazz = Class.forName("com.xcs.spring.MyConfiguration$$EnhancerBySpringCGLIB$$fce000ac");
Class<?> clazz2 = Class.forName("org.springframework.beans.factory.BeanFactoryAware");
CGLIB$setBeanFactory$5$Method = ReflectUtils.findMethods(new String[]{"setBeanFactory", "(Lorg/springframework/beans/factory/BeanFactory;)V"}, clazz2.getDeclaredMethods())[0];
CGLIB$setBeanFactory$5$Proxy = MethodProxy.create(clazz2, clazz, "(Lorg/springframework/beans/factory/BeanFactory;)V", "setBeanFactory", "CGLIB$setBeanFactory$5");
clazz2 = Class.forName("com.xcs.spring.MyConfiguration");
CGLIB$myBean$0$Method = ReflectUtils.findMethods(new String[]{"myBean", "()Lcom/xcs/spring/bean/MyBean;"}, clazz2.getDeclaredMethods())[0];
CGLIB$myBean$0$Proxy = MethodProxy.create(clazz2, clazz, "()Lcom/xcs/spring/bean/MyBean;", "myBean", "CGLIB$myBean$0");
}
final MyBean CGLIB$myBean$0() {
return super.myBean();
}
static void CGLIB$STATICHOOK2() {
}
}
5.2、分析proxyBeanMethods为false
我们再次调整MyConfiguration
类中的proxyBeanMethods
字段设置为false
@Configuration(proxyBeanMethods = false)
public class MyConfiguration {
@Bean
public MyBean myBean(){
return new MyBean();
}
}
下面是我的启动类,通过context.getBean(MyConfiguration.class)
获得MyConfiguration
对象,最后通过configuration.getClass().getName()
打印类名,查看是原始类名还是被CGLIB代理后的类名,最后使用了System.in.read()
是防止spring程序结束退出程序。
public class ConfigurationApplication {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class);
MyConfiguration configuration = context.getBean(MyConfiguration.class);
System.out.println(configuration.myBean());
System.out.println(configuration.myBean());
System.out.println("MyConfiguration = " + configuration.getClass().getName());
try {
System.in.read();
} catch (IOException e) {
e.printStackTrace();
}
}
}
我们运行后,发现CGLIB代理并未生效,而是使用原始的MyConfiguration
作为Bean对象,所以此处我们就没有必要进行类反编译操作啦,到此已经发现full模式与lite模式的一些区别了吧。
6、源码分析
首先在最前面透露一下,处理@Configuration
的核心类是在ConfigurationClassPostProcessor
类
首先通过IDEA查看类图,发现ConfigurationClassPostProcessor
类实现了一个重要的接口BeanDefinitionRegistryPostProcessor
,并实现了该接口中有postProcessBeanDefinitionRegistry
方法,并间接实现了BeanFactoryPostProcessor
接口中的postProcessBeanFactory
方法。那么ConfigurationClassPostProcessor
类是什么时候还是起作用并生效的呢?我们此时需要跟踪一下源码就知道啦
我们的ConfigurationApplication
类的main方法开始跟踪源码,我们使用的是AnnotationConfigApplicationContext
做为上下文环境,并传入了一个组件类的类名,那么我们继续进入AnnotationConfigApplicationContext
的构造函数查看源码
public class ConfigurationApplication {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class);
MyConfiguration configuration = context.getBean(MyConfiguration.class);
System.out.println(configuration.myBean());
System.out.println(configuration.myBean());
System.out.println("MyConfiguration = " + configuration.getClass().getName());
try {
System.in.read();
} catch (IOException e) {
e.printStackTrace();
}
}
}
在AnnotationConfigApplicationContext
构造函数中,执行了三个步骤
public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
// (this构造函数) 6.1
this();
// 注册MyConfiguration 6.2
register(componentClasses);
// 刷新容器 6.3
refresh();
}
6.1、this构造函数
无参构造函数中使用了AnnotatedBeanDefinitionReader
(该类主要用于从注解类中解析出 BeanDefinition
,然后将解析出的 BeanDefinition
注册到 DefaultListableBeanFactory
中)与ClassPathBeanDefinitionScanner
(该类用于扫描类路径下指定包(包括子包)中的类,解析这些类中的注解信息,然后生成对应的 BeanDefinition
,最后同样对的也是将解析出的 BeanDefinition
注册到 DefaultListableBeanFactory
中),接下来我们重点关注一下AnnotatedBeanDefinitionReader
类的构造函数
public AnnotationConfigApplicationContext() {
StartupStep createAnnotatedBeanDefReader = this.getApplicationStartup().start("spring.context.annotated-bean-reader.create");
this.reader = new AnnotatedBeanDefinitionReader(this);
createAnnotatedBeanDefReader.end();
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
首先会通过调用 getOrCreateEnvironment(registry)
来获取或创建一个 Environment
。Environment
是 Spring 中用于处理应用环境的接口,它能够访问到应用的环境变量、系统属性等信息。然后,构造函数会用传入的 registry
和获取到的 Environment
作为参数,调用另一个构造函数 AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment)
,完成AnnotatedBeanDefinitionReader
的初始化。
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) {
this(registry, getOrCreateEnvironment(registry));
}
在继续跟进构造函数,在函数中做了一些参数校验工作,这里 this.conditionEvaluator
是通过创建一个新的 ConditionEvaluator
实例来初始化的,ConditionEvaluator
是用来处理 @Conditional
注解的,这里只做了解就好,不是本次关注的重点。接下来的重点在AnnotationConfigUtils.registerAnnotationConfigProcessors()
是一个用来注册各种注解处理器的静态方法。其中我们本次关注的核心类ConfigurationClassPostProcessor
就是在这里被注册上的。
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
Assert.notNull(environment, "Environment must not be null");
this.registry = registry;
this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
如果容器中还没有这个后置处理器的 ConfigurationClassPostProcessor
,那么就创建一个 RootBeanDefinition
对象,并设置其 Bean 类型为 ConfigurationClassPostProcessor.class
,即后置处理器的类型。然后,为这个 BeanDefinition
设置来源 source
,这里传入的 source
参数是一个可选的对象,用于标识注册这些 BeanDefinition 的来源。接着,通过调用 registerPostProcessor()
方法将这个 BeanDefinition
注册到容器中。
public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(BeanDefinitionRegistry registry, @Nullable Object source) {
Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8);
// -------------------忽略其他代码-------------------------
if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
}
// -------------------忽略其他代码-------------------------
return beanDefs;
}
这个过程实际上是将 ConfigurationClassPostProcessor
类注册为一个特殊的后置处理器,用于处理 @Configuration
注解的配置类,使得这些配置类能够正常生效并且能够注册其中的 BeanDefinition
到容器中。
private static BeanDefinitionHolder registerPostProcessor(BeanDefinitionRegistry registry, RootBeanDefinition definition, String beanName) {
definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(beanName, definition);
return new BeanDefinitionHolder(definition, beanName);
}
下面是ConfigurationClassPostProcessor
被注册过程的时序图
sequenceDiagram
ConfigurationApplication->>AnnotationConfigApplicationContext: AnnotationConfigApplicationContext(componentClasses)
AnnotationConfigApplicationContext-->>ConfigurationApplication: 返回context
AnnotationConfigApplicationContext->>AnnotationConfigApplicationContext: AnnotationConfigApplicationContext()
AnnotationConfigApplicationContext->>AnnotatedBeanDefinitionReader: AnnotatedBeanDefinitionReader(registry)
AnnotatedBeanDefinitionReader-->>AnnotationConfigApplicationContext: 返回reader
AnnotatedBeanDefinitionReader-->>AnnotationConfigUtils: registerAnnotationConfigProcessors(registry)
AnnotationConfigUtils-->>AnnotationConfigUtils: registerAnnotationConfigProcessors(registry,source)
AnnotationConfigUtils-->>AnnotationConfigUtils: registerPostProcessor(registry,definition,beanName)
AnnotationConfigUtils-->>DefaultListableBeanFactory: registerBeanDefinition(beanName, beanDefinition)
6.2、register(componentClasses)
在上一个步骤中this()
已经执行完毕,接下来我们回到AnnotationConfigApplicationContext
的构造函数中的register(componentClasses)
方法来,该方法我们重点关注MyConfiguration是如何被注册的过程。
public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
this();
register(componentClasses);
refresh();
}
通过调用 this.reader.register(componentClasses);
进行实际的组件类注册。这里的 reader
(AnnotatedBeanDefinitionReader
),在构造函数中初始化完成的)是一个用于解析和注册注解配置的对象,这个方法会处理给定的组件类,解析其注解,并将相应的 BeanDefinition
注册到Spring容器中。componentClasses
实际的类就是我们的MyConfiguration
。
public void register(Class<?>... componentClasses) {
Assert.notEmpty(componentClasses, "At least one component class must be specified");
StartupStep registerComponentClass = this.getApplicationStartup().start("spring.context.component-classes.register").tag("classes", () -> Arrays.toString(componentClasses));
this.reader.register(componentClasses);
registerComponentClass.end();
}
接收一个可变参数数组 componentClasses
在循环内部,对每一个 componentClass
,都调用了 registerBean
方法。这个方法
public void register(Class<?>... componentClasses) {
for (Class<?> componentClass : componentClasses) {
registerBean(componentClass);
}
}
在这个方法中,主要的逻辑被委托给了 doRegisterBean
方法
public void registerBean(Class<?> beanClass) {
doRegisterBean(beanClass, null, null, null, null);
}
在这个方法中为MyConfiguration
类创建BeanDefinition
定义,最后,bean定义被封装在BeanDefinitionHolder
中,并使用BeanDefinitionReaderUtils.registerBeanDefinition
方法在bean定义注册表中注册。
private <T> void doRegisterBean(Class<T> beanClass, @Nullable String name,
@Nullable Class<? extends Annotation>[] qualifiers, @Nullable Supplier<T> supplier,
@Nullable BeanDefinitionCustomizer[] customizers) {
// 1. 给MyConfiguration类创建一个新的AnnotatedGenericBeanDefinition
AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);
// 2. 使用conditionEvaluator检查当前bean是否应被跳过
if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
return;
}
// 3. 如果提供了Supplier,则设置到bean定义中
abd.setInstanceSupplier(supplier);
// 4. 解析bean的作用域(singleton, prototype等)
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
abd.setScope(scopeMetadata.getScopeName());
// 5. 生成或使用给定的bean名称
String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));
// 6. 处理常见的注解定义(例如:@Lazy, @Primary等)
AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
// 7. 根据给定的限定符处理bean定义
if (qualifiers != null) {
for (Class<? extends Annotation> qualifier : qualifiers) {
if (Primary.class == qualifier) {
abd.setPrimary(true);
}
else if (Lazy.class == qualifier) {
abd.setLazyInit(true);
}
else {
abd.addQualifier(new AutowireCandidateQualifier(qualifier));
}
}
}
// 8. 使用任何提供的自定义器来修改bean定义
if (customizers != null) {
for (BeanDefinitionCustomizer customizer : customizers) {
customizer.customize(abd);
}
}
// 9. 创建一个BeanDefinitionHolder来持有bean定义及其名称
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
// 10. 如果需要,应用作用域代理模式
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
// 11. 将bean定义注册到bean定义注册表
BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
}
方法首先获取bean的主名称,并使用此名称将bean定义注册到注册表中。如果bean有别名,该方法还会将这些别名也注册到注册表中。别名在Spring中允许我们使用替代名称引用相同的bean。
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
// Register bean definition under primary name.
String beanName = definitionHolder.getBeanName();
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// Register aliases for bean name, if any.
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}
下面是MyConfiguration
被注册过程的时序图
sequenceDiagram
ConfigurationApplication->>AnnotationConfigApplicationContext: AnnotationConfigApplicationContext(componentClasses)
AnnotationConfigApplicationContext-->>ConfigurationApplication: 返回context
AnnotationConfigApplicationContext-->>AnnotationConfigApplicationContext: register(componentClasses)
AnnotationConfigApplicationContext-->>AnnotatedBeanDefinitionReader: register(componentClasses)
AnnotatedBeanDefinitionReader-->>AnnotatedBeanDefinitionReader: registerBean(beanClass)
AnnotatedBeanDefinitionReader-->>AnnotatedBeanDefinitionReader: doRegisterBean(beanClass,name,qualifiers, supplier,customizers)
AnnotatedBeanDefinitionReader-->>BeanDefinitionReaderUtils: registerBeanDefinition(definitionHolder,registry)
BeanDefinitionReaderUtils-->>DefaultListableBeanFactory: registerBeanDefinition(beanName,beanDefinition)
6.3、refresh()
在上一个步骤中register(componentClasses)
已经执行完毕,接下来我们关注refresh()
方法,在refresh()
方法中调用了invokeBeanFactoryPostProcessors(beanFactory);
是一个关键步骤,它确保所有的BeanFactoryPostProcessor
被按预期的顺序执行,从而允许对bean定义进行必要的修改和处理,而ConfigurationClassPostProcessor
类间接实现了BeanFactoryPostProcessor
接口。该方法我们重点关注ConfigurationClassPostProcessor是如何被调用过程。
public void refresh() throws BeansException, IllegalStateException {
// ----------------------忽略其他代码---------------------------
invokeBeanFactoryPostProcessors(beanFactory);
// ----------------------忽略其他代码---------------------------
}
在invokeBeanFactoryPostProcessors
方法中又委托了PostProcessorRegistrationDelegate
类中的invokeBeanFactoryPostProcessors
去执行。在getBeanFactoryPostProcessors()
这个方法从当前的ApplicationContext
实例中检索所有已注册BeanFactoryPostProcessor
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
// ----------------------忽略其他代码---------------------------
}
public static void invokeBeanFactoryPostProcessors(
ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
// ----------------------忽略其他代码---------------------------
// Now, invoke the postProcessBeanFactory callback of all processors handled so far.
invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
// ----------------------忽略其他代码---------------------------
}
最后在第147行被调用,执行了invokeBeanFactoryPostProcessors(registryProcessors, beanFactory)
方法
在此代码段中,ConfigurationClassPostProcessor
的postProcessBeanFactory
方法被调用,
private static void invokeBeanFactoryPostProcessors(
Collection<? extends BeanFactoryPostProcessor> postProcessors, ConfigurableListableBeanFactory beanFactory) {
for (BeanFactoryPostProcessor postProcessor : postProcessors) {
StartupStep postProcessBeanFactory = beanFactory.getApplicationStartup().start("spring.context.bean-factory.post-process").tag("postProcessor", postProcessor::toString);
postProcessor.postProcessBeanFactory(beanFactory);
postProcessBeanFactory.end();
}
}
接下来,我们看看ConfigurationClassPostProcessor
类中的postProcessBeanFactory
方法是如何对@Configuration
注解进行CGLIB增强的。postProcessBeanFactory
方法中的enhanceConfigurationClasses
调用,对标注为@Configuration
的类进行了增强,确保了它们的@Bean
方法行为符合预期。继续跟进enhanceConfigurationClasses
方法
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// ----------------------忽略其他代码---------------------------
enhanceConfigurationClasses(beanFactory);
beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
}
enhanceConfigurationClasses
方法中主要的功能就是从beanFactory.getBeanDefinitionNames()
中遍历BeanDefinition
,筛选出full模式(proxyBeanMethods = true)下的@Configuration
注解,然后通过ConfigurationClassEnhancer
这个类来生成代理类(com.xcs.spring.MyConfiguration$$EnhancerBySpringCGLIB$$60658f22
),然后进行替换BeanDefinition
对象中的beanClass
字段
public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
StartupStep enhanceConfigClasses = this.applicationStartup.start("spring.context.config-classes.enhance");
// 用于存储需要增强的配置类的bean定义的map
Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>();
// 循环遍历bean工厂中的所有bean定义
for (String beanName : beanFactory.getBeanDefinitionNames()) {
BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
// 获取元数据属性以识别配置类
Object configClassAttr = beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE);
AnnotationMetadata annotationMetadata = null;
MethodMetadata methodMetadata = null;
if (beanDef instanceof AnnotatedBeanDefinition) {
AnnotatedBeanDefinition annotatedBeanDefinition = (AnnotatedBeanDefinition) beanDef;
annotationMetadata = annotatedBeanDefinition.getMetadata();
methodMetadata = annotatedBeanDefinition.getFactoryMethodMetadata();
}
// 检查bean是否为配置类,且尚未增强
if ((configClassAttr != null || methodMetadata != null) && beanDef instanceof AbstractBeanDefinition) {
// Configuration class (full or lite) or a configuration-derived @Bean method
// -> eagerly resolve bean class at this point, unless it's a 'lite' configuration
// or component class without @Bean methods.
AbstractBeanDefinition abd = (AbstractBeanDefinition) beanDef;
if (!abd.hasBeanClass()) {
boolean liteConfigurationCandidateWithoutBeanMethods =
(ConfigurationClassUtils.CONFIGURATION_CLASS_LITE.equals(configClassAttr) &&
annotationMetadata != null && !ConfigurationClassUtils.hasBeanMethods(annotationMetadata));
if (!liteConfigurationCandidateWithoutBeanMethods) {
try {
abd.resolveBeanClass(this.beanClassLoader);
}
catch (Throwable ex) {
throw new IllegalStateException(
"Cannot load configuration class: " + beanDef.getBeanClassName(), ex);
}
}
}
}
// 检查bean定义是否为full模式配置类(proxyBeanMethods = true),如果是,则将其添加到configBeanDefs中。
if (ConfigurationClassUtils.CONFIGURATION_CLASS_FULL.equals(configClassAttr)) {
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'.");
}
// 添加到需要增强的配置类的bean定义的configBeanDefs
configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef);
}
}
// // 如果没有配置类需要增强,则只需结束该步骤
if (configBeanDefs.isEmpty() || NativeDetector.inNativeImage()) {
// nothing to enhance -> return immediately
enhanceConfigClasses.end();
return;
}
// 初始化增强器,用于增强配置类
ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();
// 循环遍历需要增强的配置类
for (Map.Entry<String, AbstractBeanDefinition> entry : configBeanDefs.entrySet()) {
AbstractBeanDefinition beanDef = entry.getValue();
// 对于@Configuration类,始终代理目标类
beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
// 在定义中设置增强后的类作为bean类
Class<?> configClass = beanDef.getBeanClass();
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()));
}
// 吧BeanClass修改为代理类
beanDef.setBeanClass(enhancedClass);
}
}
enhanceConfigClasses.tag("classCount", () -> String.valueOf(configBeanDefs.keySet().size())).end();
}
接下来我们看看ConfigurationClassEnhancer
类中的enhance
方法是如何产生代理类的呢,在这个方法中调用了2个比较重要的方法,newEnhancer
方法,createClass
方法,我们继续跟进源码。。。
public Class<?> enhance(Class<?> configClass, @Nullable ClassLoader classLoader) {
// 如果configClass已经是增强的(或者说它已经是EnhancedConfiguration的子类或实例),
// 则不再进行增强,并返回原始的configClass。
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 " +
"<context:annotation-config>). This is harmless, but you may " +
"want check your configuration and remove one CCPP if possible",
configClass.getName()));
}
return configClass;
}
// 如果configClass不是已知的增强类型,那么它将会被增强。
// 使用CGLIB(或其他技术)为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;
}
这个方法newEnhancer
的目的是为一个指定的配置类(MyConfiguration
)创建一个CGLIB的Enhancer
对象,用于后续生成该配置类的代理或子类。在Spring中,CGLIB是用来在运行时生成Java类的代码库
private Enhancer newEnhancer(Class<?> configSuperClass, @Nullable ClassLoader classLoader) {
// 创建一个新的CGLIB Enhancer实例。
Enhancer enhancer = new Enhancer();
// 设置要增强的类的超类,即原始的配置类。
enhancer.setSuperclass(configSuperClass);
// 设置增强类实现的接口。这里,增强的类会实现EnhancedConfiguration接口,
// 这通常用于后续的检查或识别。
// 使用场景,在上个方法中(enhance)作为判断条件 if (EnhancedConfiguration.class.isAssignableFrom(configClass))
enhancer.setInterfaces(new Class<?>[] {EnhancedConfiguration.class});
// 设置使用工厂模式。这里设置为false意味着生成的增强类不会实现Factory接口。
enhancer.setUseFactory(false);
// 设置命名策略,这决定了生成的增强类的名称。
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
// 设置策略,该策略决定如何生成增强类的字节码。
// 这里,策略还负责使增强类变为“BeanFactory-aware”,
// 这意味着它可以与Spring的BeanFactory交互。
enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));
// 设置回调过滤器,该过滤器决定哪些方法需要被代理以及如何被代理。
enhancer.setCallbackFilter(CALLBACK_FILTER);
// 设置增强类需要使用的回调类型,基于前面设置的回调过滤器。
enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes());
// 返回为指定配置类准备好的Enhancer。
return enhancer;
}
传入的CGLIB的Enhancer
对象来创建一个新的增强类,并注册其相关的回调,我们看看CALLBACKS
到底设置了那些回调参数
private Class<?> createClass(Enhancer enhancer) {
// 使用Enhancer创建一个新的增强类。这实际上会生成一个新的类的字节码,
// 该类是原始配置类的子类,并增加了一些额外的功能或行为。
Class<?> subclass = enhancer.createClass();
// 注册回调函数。这些回调函数决定增强类中的哪些方法如何被增强或代理。
// 使用静态注册(而不是基于线程局部的)在OSGi环境中是关键的,因为它确保
// 回调在所有线程和类加载器之间都是可见的和一致的。
// (注:OSGi是一个Java模块化系统,在这种环境中,类加载器和线程的行为可能与
// 标准Java应用有所不同,所以特殊处理是必要的。)
Enhancer.registerStaticCallbacks(subclass, CALLBACKS);
// 返回新创建的增强类。
return subclass;
}
让我们看看这三个回调:
BeanMethodInterceptor
: 用于增强或代理那些对应于bean的方法
BeanFactoryAwareMethodInterceptor
: 为代理类中的$$beanFactory
字段赋值,具体请查看arthas反编译后的字节码类
NoOp.INSTANCE
: 这是CGLIB提供的一个特殊回调,代表不执行任何操作。当使用这个回调增强方法时,方法的原始行为将不会被改变。
private static final Callback[] CALLBACKS = new Callback[] {
new BeanMethodInterceptor(),
new BeanFactoryAwareMethodInterceptor(),
NoOp.INSTANCE
};
首先看看BeanMethodInterceptor
类的实现,此类主要用于拦截对 @Configuration
类中定义的 @Bean
方法的调用,以确保当这些方法被调用时,返回的 bean 实例是正确管理和处理的。
下面是拦截方法的参数介绍
enhancedConfigInstance
: 这是经过 CGLIB 增强的@Configuration
类的实例。beanMethod
: 被调用的@Bean
方法。beanMethodArgs
: 调用该方法时传递的参数。cglibMethodProxy
: CGLIB 提供的方法代理,用于调用原始或超类的方法。
/**
* 拦截并处理对 @Bean 方法的调用。
*/
public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs,
MethodProxy cglibMethodProxy) throws Throwable {
// 获取关联的 BeanFactory 通过反射读取了代理类中的$$beanFactory字段
ConfigurableBeanFactory beanFactory = getBeanFactory(enhancedConfigInstance);
// 确定当前 @Bean 方法对应的 bean 名称
String beanName = BeanAnnotationHelper.determineBeanNameFor(beanMethod);
// 检查当前的 @Bean 方法是否定义了一个作用域代理
if (BeanAnnotationHelper.isScopedProxy(beanMethod)) {
String scopedBeanName = ScopedProxyCreator.getTargetBeanName(beanName);
if (beanFactory.isCurrentlyInCreation(scopedBeanName)) {
beanName = scopedBeanName;
}
}
// FactoryBeans 在 Spring 中是特殊的 beans,它们不产生 bean 实例本身,而是产生其他 beans。
// 此代码块处理了当 FactoryBean 被请求时的情况,
// 确保返回的是 FactoryBean 创建的实际 bean,而不是 FactoryBean 本身。
if (factoryContainsBean(beanFactory, BeanFactory.FACTORY_BEAN_PREFIX + beanName) &&
factoryContainsBean(beanFactory, beanName)) {
// 此部分代码省略,但它处理 FactoryBean 创建的 bean 的返回和增强
}
// 检查当前的方法是否是正在被工厂调用的工厂方法
if (isCurrentlyInvokedFactoryMethod(beanMethod)) {
// 如果是,直接调用方法的原始实现
return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);
}
// 尝试从 bean 工厂中解析并返回 bean 的引用
return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);
}
在看看BeanFactoryAwareMethodInterceptor
类的实现,这个类的目的主要是为代理类设置的$$beanFactory
的字段赋值
下面是拦截方法的参数介绍
obj
: 被代理的对象。method
: 正在被调用的原方法。args
: 调用方法时传入的参数。proxy
: 代表CGLIB用于调用原始方法的MethodProxy
对象。
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
// 使用反射查找obj类中名为$$beanFactory的字段
Field field = ReflectionUtils.findField(obj.getClass(), BEAN_FACTORY_FIELD);
// 确保找到了相关字段,如果没有找到,则抛出异常。
Assert.state(field != null, "Unable to find generated BeanFactory field");
// 将obj对象的BEAN_FACTORY_FIELD字段设置为args[0],这里args[0]是BeanFactory的实例。
field.set(obj, args[0]);
// 检查obj的实际超类(不包括CGLIB生成的部分)是否实现了BeanFactoryAware接口。
if (BeanFactoryAware.class.isAssignableFrom(ClassUtils.getUserClass(obj.getClass().getSuperclass()))) {
// 如果实际超类实现了BeanFactoryAware接口,那么它会有一个setBeanFactory()方法,
// 所以我们继续调用该方法。
return proxy.invokeSuper(obj, args);
}
// 如果实际超类没有实现BeanFactoryAware接口,那么直接返回null。
return null;
}
最后看看NoOp.INSTANCE
类的实现,你会发现这个类什么都没有干,那么为什么会设置一个这样的回调呢?其目的是为什么呢?
举个例子,假设你只想拦截和处理代理对象的setXXX
方法,而其他所有方法(如getXXX
)都应该按原样执行,没有额外的逻辑。在这种情况下,你可以为setXXX
方法设置特定的拦截器,而为getXXX
方法设置NoOp.INSTANCE
。因为CGLIB默认是代理所有的方法的,如果不提供NoOp.INSTANCE类,那么你可能会出现一个这样的异常信息Exception in thread "main" java.lang.IllegalArgumentException: No callback found for index 1
public interface NoOp extends Callback {
NoOp INSTANCE = new NoOp() {
};
}
下面是ConfigurationClassPostProcessor
被执行的时序图
sequenceDiagram
ConfigurationApplication->>AnnotationConfigApplicationContext: AnnotationConfigApplicationContext(componentClasses)
AnnotationConfigApplicationContext-->>ConfigurationApplication: 返回context
AnnotationConfigApplicationContext-->>AnnotationConfigApplicationContext: refresh
AnnotationConfigApplicationContext-->>AnnotationConfigApplicationContext: invokeBeanFactoryPostProcessors
AnnotationConfigApplicationContext-->>PostProcessorRegistrationDelegate: invokeBeanFactoryPostProcessors(beanFactory,beanFactoryPostProcessors)
PostProcessorRegistrationDelegate-->>PostProcessorRegistrationDelegate: invokeBeanFactoryPostProcessors(postProcessors,beanFactory)
PostProcessorRegistrationDelegate-->>ConfigurationClassPostProcessor: postProcessBeanFactory(beanFactory)
ConfigurationClassPostProcessor-->>ConfigurationClassPostProcessor: enhanceConfigurationClasses(beanFactory)
ConfigurationClassPostProcessor-->>ConfigurationClassEnhancer: enhance(configClass,classLoader)
ConfigurationClassEnhancer-->>ConfigurationClassEnhancer: createClass(enhancer)
ConfigurationClassEnhancer-->>ConfigurationClassPostProcessor: 增强后的Class类
7、常见问题
7.1、在@Bean主键在方法上时,访问修饰符为什么不能是private或者final修饰呢?
那么我们对这两种场景做个测试。。。
7.1.1、private修饰符
@Configuration
public class MyConfiguration {
@Bean
private MyBean myBean(){
return new MyBean();
}
}
运行代码发现直接报错,启动失败。
8月 10, 2023 2:30:41 下午 org.springframework.context.support.AbstractApplicationContext refresh
警告: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: @Bean method 'myBean' must not be private or final; change the method's modifiers to continue
Offending resource: com.xcs.spring.MyConfiguration
Exception in thread "main" org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: @Bean method 'myBean' must not be private or final; change the method's modifiers to continue
Offending resource: com.xcs.spring.MyConfiguration
at org.springframework.beans.factory.parsing.FailFastProblemReporter.error(FailFastProblemReporter.java:72)
at org.springframework.context.annotation.BeanMethod.validate(BeanMethod.java:52)
at org.springframework.context.annotation.ConfigurationClass.validate(ConfigurationClass.java:220)
at org.springframework.context.annotation.ConfigurationClassParser.validate(ConfigurationClassParser.java:216)
at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:332)
at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:247)
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:311)
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:112)
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:746)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:564)
at org.springframework.context.annotation.AnnotationConfigApplicationContext.<init>(AnnotationConfigApplicationContext.java:93)
at com.xcs.spring.ConfigurationApplication.main(ConfigurationApplication.java:15)
7.1.2、final修饰符
@Configuration
public class MyConfiguration {
@Bean
public final MyBean myBean(){
return new MyBean();
}
}
同样的依旧是此错误
8月 10, 2023 2:33:24 下午 org.springframework.context.support.AbstractApplicationContext refresh
警告: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: @Bean method 'myBean' must not be private or final; change the method's modifiers to continue
Offending resource: com.xcs.spring.MyConfiguration
Exception in thread "main" org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: @Bean method 'myBean' must not be private or final; change the method's modifiers to continue
Offending resource: com.xcs.spring.MyConfiguration
at org.springframework.beans.factory.parsing.FailFastProblemReporter.error(FailFastProblemReporter.java:72)
at org.springframework.context.annotation.BeanMethod.validate(BeanMethod.java:52)
at org.springframework.context.annotation.ConfigurationClass.validate(ConfigurationClass.java:220)
at org.springframework.context.annotation.ConfigurationClassParser.validate(ConfigurationClassParser.java:216)
at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:332)
at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:247)
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:311)
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:112)
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:746)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:564)
at org.springframework.context.annotation.AnnotationConfigApplicationContext.<init>(AnnotationConfigApplicationContext.java:93)
at com.xcs.spring.ConfigurationApplication.main(ConfigurationApplication.java:15)
7.1.3、原因分析
- CGLIB代理: 当Spring使用CGLIB创建
@Configuration
类的代理时,它实际上是为这个类创建了一个子类。为了使代理可以工作,CGLIB需要能够重写@Configuration
类中的@Bean
方法。- private方法: 在java规范中被声明为
private
的方法不能被子类重写。由于CGLIB子类无法访问或重写这些方法,所以如果@Bean
方法被声明为private
,CGLIB代理将无法正确地管理它们。 - final方法: 同样地在java规范中,
final
方法也不能被子类重写。因此,如果@Bean
方法被声明为final
,CGLIB也将无法管理这些方法。
- private方法: 在java规范中被声明为
- 单例语义: 在标准的
@Configuration
类中,当一个@Bean
方法被调用多次时,它实际上只创建一个实例,因为这些方法调用是通过代理拦截的。这保证了单例bean的单例语义。如果@Bean
方法是private
或final
的,Spring将无法拦截这些方法调用,从而可能导致每次调用都创建一个新的bean实例,违反了单例语义。
7.2 @Configuration中full模式与lite模式如何选择?
@Configuration
注解有两种模式:full
和 lite
。它们在功能和性能上有所不同。了解它们的优缺点有助于为特定的场景做出合适的选择。
7.2.1 Full 模式
- 启用方式:在
@Configuration
注解中不设置proxyBeanMethods
或将其设置为true
。 - 功能:当在配置类中的
@Bean
方法内部调用另一个@Bean
方法时,Spring 会确保返回的是容器中的单例bean,而不是一个新的实例。这是通过CGLIB代理实现的。 - 优势:保持单例语义,确保容器中的单例Bean在配置类中的调用中始终是单例的。
- 劣势:需要通过CGLIB创建配置类的子类,可能带来一些性能开销,增加了启动时间,可能与某些库不兼容,这些库期望操作实际类而不是其CGLIB代理。
7.2.2 Lite 模式
- 启用方式:在
@Configuration
注解中设置proxyBeanMethods
为false
。 - 功能:禁用CGLIB代理。
@Bean
方法之间的调用就像普通的Java方法调用,每次都会创建一个新的实例。 - 优势:更快的启动时间,因为不需要通过CGLIB增强配置类,对于简单的注入,这种模式可能更为简洁和直接。
- 劣势:不保持单例语义。如果在一个
@Bean
方法内部调用另一个@Bean
方法,会创建一个新的bean实例。
7.2.3 如何选择
- 如果你的配置中需要确保在配置类中调用的bean始终是Spring容器中的单例bean,选择full模式。
- 如果你的配置类只是简单地定义beans并注入依赖,且不需要在配置类方法之间共享单例实例,选择lite模式。
- 如果你关心应用的启动性能,特别是在云环境或微服务中,使用lite模式可能更合适,因为它避免了额外的CGLIB处理。
最终,根据项目的具体需求和场景选择合适的模式。如果没有特殊的单例需求,推荐使用lite模式,因为它更简单且启动性能更好。
8、总结
在这一节源码分析中,了解到了@Configuration
的full模式(proxyBeanMethods=true)
与lite模式(proxyBeanMethods=false)
,并在4.1与4.2中做了测试并验证,另外还了解到了@Configuration
注解几种组合用法,甚至我们可以多个组合使用的在spring中是非常常见的一种使用方式。然后我们利用arthas进行了反编译字节码进行原理分析,发现是利用CGLIB对MyConfiguration
类继承方式然后重写了@Bean注解修饰的的方法来完成代理并保证了@Bean
的语义。最后我们对源码进行了分析,其中最核心的类就是ConfigurationClassPostProcessor
这个类间接实现了BeanFactoryPostProcessor
接口中的postProcessBeanFactory
方法,在这个方法中筛选出full模式下的的BeanDefinition
,然后进行CGLIB增强处理。