前言
Spring框架中有一个出现频次很高的注解@Configuration,特别是在SpringBoot的spring-boot-autoconfigure包中,可以发现每个配置类基本都会加上@Configuration(proxyBeanMethods=false)注解(注:Spring 5.2+)
,其中proxyBeanMethods属性起到什么作用呢?今天我们就通过源码探究它的底层原理
使用
常见用法
//属性proxyBeanMethods默认为true
@Configuration
public class AppConfig {
@Bean
public MyBean myBean() {
// instantiate, configure and return bean ...
}
}
SpringBoot Autoconfig中的用法
@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 = false
@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对象!
proxyBeanMethods = true
@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且属性proxyBeanMethods=true则使用Spring CGLIB生成一个对应的配置增强类,当调用@Bean注解上的方法进行实例化对象时会优先在spring容器里查找,如果存在就直接返回,否则就会走通用的Spring Bean的初始化流程
源码如下:
类ConfigurationClassPostProcessor
入口方法: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方法
类ConfigurationClassUtils
方法: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);
}
//...
}
类ConfigurationClassPostProcessor
入口方法: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()方法来生成的,继续往下看
类ConfigurationClassEnhancer
方法: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对象了,如果存在则直接返回,否则重新创建
类BeanMethodInterceptor
@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属性,当然不加也是可以的,对项目本身性能影响也不大,看自己的习惯啦!