一.概念分析
在Spring框架中,@Configuration注解用于声明一个Java类作为配置类,它替代了传统的XML配置方式。通过@Configuration注解标记的类可以包含@Bean注解,用于定义Spring容器中的Bean对象。而在@Configuration注解中,有一个非常重要的属性proxyBeanMethods,它控制着@Configuration类中@Bean方法的代理行为。让我们对这个属性进行非常详细的介绍:
-
@Configuration注解:
- @Configuration是Spring框架提供的一个元注解,用于表示一个类是一个配置类。
- 配置类通常包含@Bean注解,定义了创建和配置Bean的方法。
- Spring会扫描@Configuration类,并将其中的@Bean方法注册为Spring容器中的Bean。
-
@Bean注解:
- @Bean是用于定义Spring Bean的注解。
- 在@Configuration类中,通过@Bean注解标记的方法会返回一个对象,该对象将被注册到Spring容器中作为Bean。
- 默认情况下,Spring容器会使用方法名作为Bean的名称,也可以通过@Bean的name属性指定Bean的名称。
-
proxyBeanMethods属性:
- proxyBeanMethods是@Configuration注解的一个属性,它用于控制@Bean方法的代理行为。
- 在Spring 5及之前的版本,默认值为true,从Spring 5开始,默认值为true。
- 该属性接受一个布尔值,设置为true表示启用代理模式,设置为false表示禁用代理模式。
-
代理模式(Proxy Mode):
- 在@Configuration类中,当proxyBeanMethods属性设置为true时,Spring会对@Configuration类进行CGLIB代理。
- CGLIB是一个强大的第三方库,用于在运行时生成Java类的子类。
- 对@Configuration类进行代理后,调用@Bean方法时,Spring会检查是否已经存在该Bean,如果存在,则直接返回已存在的Bean,否则调用方法创建新的Bean并缓存起来。
- 这样做的好处是,当多个@Bean方法之间存在相互调用时,可以保证对相同Bean的调用不会重复执行,提高了应用程序的性能。
-
禁用代理模式:
- 当proxyBeanMethods属性设置为false时,禁用了CGLIB代理。
- 在禁用代理模式下,Spring容器每次调用@Bean方法时都会执行一次方法体,不会缓存Bean对象。
- 这样做的好处是,每次调用@Bean方法都会重新创建一个新的Bean对象,适用于那些需要每次返回新实例的场景。
-
如何选择代理模式:
- 选择是否启用代理模式取决于应用程序的需求。
- 如果@Configuration类中的@Bean方法之间没有相互调用,并且Bean的创建不涉及复杂的逻辑,可以考虑启用代理模式,以提高性能。
- 如果@Bean方法之间有相互调用,或者Bean的创建逻辑比较复杂,为了保证每次调用都返回新的实例,可以禁用代理模式。
示例代码:
@Configuration
public class MyConfiguration {
@Bean
public MyBean myBean() {
return new MyBean();
}
@Bean
public AnotherBean anotherBean() {
return new AnotherBean(myBean());
}
}
总结:
@Configuration注解用于声明配置类,其中的@Bean注解用于定义Spring容器中的Bean对象。proxyBeanMethods属性控制@Bean方法的代理行为。启用代理模式可以提高性能,禁用代理模式可以保证每次调用@Bean方法都返回新的实例。选择是否启用代理模式取决于应用程序的需求。
场景再现
为了更好地理解proxyBeanMethods的不同使用场景,我们创建一个简单的示例来演示它们的效果。我们将创建一个Spring应用程序,其中包含一个@Configuration类,其中定义了两个@Bean方法,并在不同场景下测试proxyBeanMethods的行为。
场景一:启用代理模式
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = true)
public class ProxyEnabledConfiguration {
@Bean
public MyBean myBean() {
return new MyBean();
}
@Bean
public AnotherBean anotherBean() {
//开启代理,实例Bean会被缓存,直接返回已存在的Bean
return new AnotherBean(myBean());
}
}
场景二:禁用代理模式
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
public class ProxyDisabledConfiguration {
@Bean
public MyBean myBean() {
return new MyBean();
}
@Bean
public AnotherBean anotherBean() {
//禁用代理,每次都会生成新的实例Bean
return new AnotherBean(myBean());
}
}
MyBean.java:
public class MyBean {
private static int instanceCounter = 0;
private int instanceNumber;
public MyBean() {
instanceNumber = ++instanceCounter;
System.out.println("Creating MyBean instance: " + instanceNumber);
}
public int getInstanceNumber() {
return instanceNumber;
}
}
AnotherBean.java:
public class AnotherBean {
private MyBean myBean;
public AnotherBean(MyBean myBean) {
this.myBean = myBean;
System.out.println("Creating AnotherBean instance using MyBean with instance number: " + myBean.getInstanceNumber());
}
}
测试类:
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class App {
public static void main(String[] args) {
// proxyBeanMethods enabled
AnnotationConfigApplicationContext context1 = new AnnotationConfigApplicationContext(ProxyEnabledConfiguration.class);
AnotherBean anotherBean1 = context1.getBean(AnotherBean.class);
// proxyBeanMethods disabled
AnnotationConfigApplicationContext context2 = new AnnotationConfigApplicationContext(ProxyDisabledConfiguration.class);
AnotherBean anotherBean2 = context2.getBean(AnotherBean.class);
}
}
在启用代理模式的场景中(ProxyEnabledConfiguration),MyBean只会被实例化一次,因为AnotherBean会复用已经创建的MyBean实例,从控制台输出可以看到:
Creating MyBean instance: 1
Creating AnotherBean instance using MyBean with instance number: 1
而在禁用代理模式的场景中(ProxyDisabledConfiguration),MyBean会被每次调用都实例化一次,因为没有缓存,从控制台输出可以看到:
Creating MyBean instance: 1
Creating MyBean instance: 2
Creating AnotherBean instance using MyBean with instance number: 1
Creating MyBean instance: 3
Creating AnotherBean instance using MyBean with instance number: 2
通过这两个场景的对比,我们可以清楚地看到proxyBeanMethods属性的影响。
概念性东西确实很绕,有时候我们想要探寻底层原理,往往会碰壁,但是,不能放弃,多读几遍,多查查资料,兼容众家之长,把它拿下,吸收,彻底理解,慢慢的,一步一步来,不要着急!!!