前言
我们都知道本质上spring中都是bean,但是在实际中却使用不同的注解,这是为什么呢
常见的标记配置类bean注解有@Configuration@Component @ComponentScan @Import @ImportResource
等标注的类都是配置类。
与其他注解不同的是
- @Configuration注解标记的类是配置类,其中Bean定义信息被标记为full 类型
- @Component @ComponentScan @Import @ImportResource 标注的类是配置类,其中Bean定义信息被标记为lite类型
- 类中的方法被@Bean注解标记的类也是配置类,Bean定义信息被标记为lite 类型
查看@Configuration源码可知,相对于其他注解==@Configuration多出了proxyBeanMethods()==属性,这个属性默认为true,代表使用CGLIB增强(生成代理对象)。
lite模式:@Component 、@ComponentScan 、@Import @ImportResource、@Configuration(proxyBeanMethods=false)等修饰
- 该模式下,配置类本身不会被CGLIB增强,放进IoC容器内的就是类本身
- 该模式下,对于内部类是没有限制的:可以是Full模式或者Lite模式
- 该模式下,配置类内部不能通过方法调用来处理依赖,否则每次生成的都是一个新实例而并非IoC容器内的单例
- 该模式下,配置类就是一普通类,所以@Bean方法可以使用private/final等进行修饰
full模式:@Configuration修饰
full模式的方法调用总是返回容器注入的Bean是通过BeanMethodInterceptor
拦截器实现的。
- 该模式下,配置类会被CGLIB增强(生成代理对象),放进IoC容器内的是代理
- 该模式下,对于内部类是没有限制的:可以是Full模式或者Lite模式
- 该模式下,配置类内部可以通过方法调用来处理依赖,并且能够保证是同一个实例,都指向IoC内的那个单例
- 该模式下,@Bean方法不能被private/final等进行修饰,因为代理类需要重写这个方法
举例说明
1、lite模式
配置类
@Component
// 或者@Configuration(proxyBeanMethods = false)
@Slf4j
public class LogConfig {
@Bean
public TeacherBean teacherBean(){
log.info("生成TeacherBean");
//这个地方在idea中会有红色下划线,但是依然可以编译运行
return new TeacherBean(studentBean());
}
@Bean
public StudentBean studentBean() {
log.info("生成StudentBean");
return new StudentBean();
}
public static class TeacherBean {
public TeacherBean(StudentBean studentBean) {
}
}
public static class StudentBean {
}
}
运行结果
@Component
public class Test {
public static void main(String[] args) {
final AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("annotation");
final Object test = context.getBean("test");
final Object test2 = context.getBean("logConfig");
System.out.println(test);
System.out.println(test2);
}
}
结论
从日志中我们可以发现studentBean()被调用了两次。
2、full模式
配置类
@Configuration
@Slf4j
public class LogConfig {
@Bean
public TeacherBean teacherBean(){
log.info("生成TeacherBean");
//红色下划线消失
return new TeacherBean(studentBean());
}
@Bean
public StudentBean studentBean() {
log.info("生成StudentBean");
return new StudentBean();
}
public static class TeacherBean {
public TeacherBean(StudentBean studentBean) {
}
}
public static class StudentBean {
}
}
运行结果
@Component
public class Test {
public static void main(String[] args) {
final AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("annotation");
final Object test = context.getBean("test");
final Object test2 = context.getBean("logConfig");
System.out.println(test);
System.out.println(test2);
}
}
结论
从日志中我们可以发现studentBean()被调用了一次。
按照正常的思维studentBean()应该是要被调用两次的,日志的输出也证明了只调用了一次,这么做的好处是我们可以声明bean间的依赖关系。
在常见场景中,加了@Bean的方法要声明在加了@Configuration的类中,确保始终都是full模式,因此跨方法引用就会被重定向到容器的生命周期管理,spring帮你干这件事情。这样做可以防止@Bean注解的方法通过常规Java调用意外调用相同的方法。