0
点赞
收藏
分享

微信扫一扫

刨析Spring的@Configuration注解属性proxyBeanMethods原理

guanguans 2022-02-02 阅读 102

前言

Spring框架中一个出现频次很高的注解@Configuration,如果还觉得陌生,那么我们可以去看看SpringBoot的spring-boot-autoconfigure包,你可以发现每个配置类必有@Configuration注解,其中有个属性proxyBeanMethods,它有什么作用呢?今天我们就来通过源码探究它的原理

案例

@Configuration
public class AppConfig {
    @Bean
    public MyBean myBean() {
        // instantiate, configure and return bean ...
    }
}
@Configuration(proxyBeanMethods = false)
public class TaskExecutionAutoConfiguration {

	@Lazy
	@Bean(name = APPLICATION_TASK_EXECUTOR_BEAN_NAME)
	@ConditionalOnMissingBean(Executor.class)
	public ThreadPoolTaskExecutor applicationTaskExecutor(TaskExecutorBuilder builder) {
		return builder.build();
	}
}

看到区别了吗?多了一个proxyBeanMethods属性,这个属性有什么用呢?

给出案例:

@Configuration(proxyBeanMethods = false)
public class AppConfig {
	@Bean
    public Role role() {
        System.out.println("初始化Role对象!");
        return new Role();
    }
    
    @Bean
    public User user() {
        Role role = this.role();
        return new User();
    }
}

输出结果
初始化Role对象!
初始化Role对象!

@Configuration(proxyBeanMethods = true)
public class AppConfig {
	@Bean
    public Role role() {
        System.out.println("初始化Role对象!");
        return new Role();
    }
    
    @Bean
    public User user() {
        Role role = this.role();
        return new User();
    }
}

输出结果
初始化Role对象!

源码分析

Spring启动时会扫描所有带@Component和@Configuration注解的类,并将这些类定义为BeanDefinition并且放到beanDefinitionMap集合中,然后Spring会针对这些BeanDefinition做一些BeanFactory的后置处理,其中有一个类ConfigurationClassPostProcessor,如果类上配置了@Configuration,则使用CGLIB生成一个增强Config代理类,实例化内部的Bean对象时都会先在spring容器里查找,如果存在就直接返回使用

源码如下:

入口方法:postProcessBeanDefinitionRegistry()
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
	//省略...
	processConfigBeanDefinitions(registry);
}
方法:processConfigBeanDefinitions

遍历beanDefinitionNames集合,判断这些类是否配置了@Configuration注解

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
	//遍历
	for (String beanName : candidateNames) {
		//判断beanDef定义的类上是否有@Configuration注解
		if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
				configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
		}
	}
}

进入ConfigurationClassUtils类,查看checkConfigurationClassCandidate方法

方法:checkConfigurationClassCandidate()

该方法会判断bean类上是否配置了@Configuration注解,如果配置了且proxyBeanMethods属性为true,则给该类对应的beanDefinition设置一个Attribute值(CONFIGURATION_CLASS_ATTRIBUTE->full),否则Attribute值为(CONFIGURATION_CLASS_ATTRIBUTE->lite),这是@Configuration的两种配置模式即:FULL和LITE

注意:后面的步骤会使用到该值

public static boolean checkConfigurationClassCandidate(
			BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {
	...
	Map<String, Object> config = metadata.getAnnotationAttributes(Configuration.class.getName());
		//如果proxyBeanMethods属性值为True,则设值为full
		if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) {
			beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
		}
		//如果proxyBeanMethods属性值为false,则庙会为lite
		else if (config != null || isConfigurationCandidate(metadata)) {
			beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
		}
	...
}
入口方法:postProcessBeanFactory()

通过用CGLIB增强的子类替换配置类,调用enhanceConfigurationClasses方法进行增强

@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
	//省略...
	//针对配置类进行增强,生成cglib代理对象
	enhanceConfigurationClasses(beanFactory);
	//省略...
}
方法:enhanceConfigurationClasses()

主要关注beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE)这一段代码,上一步已经对该beanDefinition对象设置了attribute值,如果该值为lite,则不会生成代理对象,如果该值为full(说明类上配置了@Congiguration且proxyBeanMethods=true)则生成cglib对象,格式为:

com.showcase.config.AppConfig$$EnhancerBySpringCGLIB$$1678297f

代码如下:

public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
	Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>()
	//循环所有的BeanDefinition对象,这里我们只看AppConfig(例子)
	for (String beanName : beanFactory.getBeanDefinitionNames()) {
		BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
		Object configClassAttr = beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE);
		//...
		//如果配置了Configuration且proxyBeanMethods=true,将生成cglib对象,否则返回原生对象
		if (ConfigurationClassUtils.CONFIGURATION_CLASS_FULL.equals(configClassAttr)) {
			configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef)
		}
	}
	if (configBeanDefs.isEmpty()) {
		return;
	}
	ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();
	for (Map.Entry<String, AbstractBeanDefinition> entry : configBeanDefs.entrySet()) {
		//...
		Class<?> configClass = beanDef.getBeanClass();
		//针对类AppConfig生成enhanced增强类
		Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);
		if (configClass != enhancedClass) {
			//设置AppConfig对象的类型为AppConfig$$EnhancerBySpringCGLIB
			beanDef.setBeanClass(enhancedClass);
		}
	}
}

通过代码我们可以看到,代理增强类是通过ConfigurationClassEnhancer类的enhance()方法来生成的,继续往下看

方法:enhance()
public Class<?> enhance(Class<?> configClass, @Nullable ClassLoader classLoader) {
	//...
	Class<?> enhancedClass = createClass(newEnhancer(configClass, classLoader));
	return enhancedClass;
}
方法:createClass()
private Class<?> createClass(Enhancer enhancer) {
	Class<?> subclass = enhancer.createClass();
	// Registering callbacks statically (as opposed to thread-local)
	// is critical for usage in an OSGi environment (SPR-5932)...
	Enhancer.registerStaticCallbacks(subclass, CALLBACKS);
	return subclass;
}

基中CALLBACKS变量定义了具体的interceptor对象,该变量声明在类ConfigurationClassEnhancer中,如下:

private static final Callback[] CALLBACKS = new Callback[] {
		new BeanMethodInterceptor(),
		new BeanFactoryAwareMethodInterceptor(),
		NoOp.INSTANCE
};

我们关注的BeanMethodInterceptor就是文章开始提到的问题关键点了,通过这个interceptor类就可以实现从beanFacory中查找是否已经存在Role对象了,如果有则直接返回,否则去创建,继续往下看:

@Override
@Nullable
public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs,
			MethodProxy cglibMethodProxy) throws Throwable {
	//①方法
	if (isCurrentlyInvokedFactoryMethod(beanMethod)) {
		return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);
	}
	
	//②方法
	return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);
}

①、这里判断当前调用的方法和主方法是不是同一个,有点绕!以代码为例

@Bean
public Role role() {
	System.out.println("初始化Role对象!");
	return new Role();
}
    
@Bean
public User user() {
    Role role = this.role();
    return new User();
}
  • Role对象初始化:
    当spring调用AppConfig.role()方法,此时主方法是role(),因为AppConfig为cglib代理对象,所以这里调用role()方法时会走BeanMethodInterceptor的intercept()方法,此时isCurrentlyInvokedFactoryMethod会判断主方法AppConfig.role()和子调用方法AppConfig.role()相等,则会通过反射机制cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs)调用到实际对象AppConfig的role()方法完成Role对象的初始化

  • User对象初始化:
    当spring调用AppConfig.user()方法,此时主方法是user(),而内部又调用了role()方法,因为AppConfig为cglib代理对象,所以这里调用role()方法时又会走BeanMethodInterceptor的intercept()方法,此时isCurrentlyInvokedFactoryMethod会判断主方法AppConfig.user()和子调用方法AppConfig.role()不相等,于是就会走到方法,该方法返回BeanFactory中已经创建的Role对象,然后继续通过反射机制调用到实际对象AppConfig的user()方法完成User对象的初始化

②、方法先到BeanFactory中查找是否存在name等于role的bean,如果存在就直接返回

private Object resolveBeanReference(Method beanMethod, Object[] beanMethodArgs,
				ConfigurableBeanFactory beanFactory, String beanName) {
	//...
	//从singleton集合中查找是否存在role bean
	Object beanInstance = (useArgs ? beanFactory.getBean(beanName, beanMethodArgs) :
							beanFactory.getBean(beanName));
	}
	return beanInstance;
}

总结

Spring在5.2版本中引入了proxyBeanMethods,且默认值为true,即默认会对配置了@Configuration的类生成代理对象,文章前面我们看到SpringBoot2.2版本开始所有的AutoConfiguration类都会显示的配置@Configuration(proxyBeanMethods = false),这样做是有原因的,因为SpringBoot内部大量使用到自动配置特性,生成代理对象会使spring的启动时间增加,同时也会额外增加代理部分的对象内存

建议平时创建Configuration类时,模仿SpringBoot,给加上proxyBeanMethods =false属性,当然不加也是可以的,对项目性能本身影响也不大,看自己的习惯啦!😁

如果文章观点有误,欢迎留言讨论!

举报

相关推荐

0 条评论