写在前面

但是从Spring 3.0起支持了一种更优的方式:基于Java类的配置方式,这一下子让我们Javaer可以从标签语法里解放了出来。毕竟作为Java程序员,我们擅长的是写Java类,而非用标签语言去写xml文件。
基本概念
@Import @Component @ComponentScan
...
这其中就包括了@Configuration、通常配合@Configuration一起出现的还有@Bean,这里需要注意的是:
@Configuration用来标注类
@Bean用来标注方法


用@Configuration注解标注的类表明其主要目的是作为bean定义的源。此外,@Configuration类允许通过调用同一类中的其他@Bean method方法来定义bean之间的依赖关系。

简单粗暴理解:@Configuration标注的类等同于一个xml文件,@Bean标注的方法等同于xml文件里的一个<bean/> 标签
Lite模式和Full模式
Lite模式
https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#spring-core

这里简单的总结一下,官方定义在没有标注@Configuration的类里面有@Bean方法就称为Lite模式的配置。不过这里需要注意,通过源码再看这个定义是不完全正确的,而应该是有如下情况均认为是Lite模式的配置类:
Class上标注有@Component注解 Class上标注有@ComponentScan注解 Class上标注有@Import注解 Class上标注有@ImportResource注解 若类上没有任何注解,存在@Bean方法



user01和user02是否为同一个对象? MyConfiguration对象的真实类型是什么?


通过上面代码我们可以得出结论
MyConfiguration01并没有被CGLib增强,也就是说,它并不是一个代理对象 通过MyConfiguration01#user01()获取的对象user02,并不是Ioc容器中的对象 @Bean可以在普通类中使用,但是该类必须是Ioc容器中的对象,并且标注的方法可以是private、final
优点:
运行时不再需要给对应类生成CGLIB子类,每次获取对象并不会优先去获取Ioc容器中的对象,提高了运行性能,降低了服务的启动和加载时间
配置类可以当作一个普通类使用:也就是说@Bean方法可以是private、final
缺点:
不能声明@Bean之间的依赖,也就是说不能通过方法调用来依赖其它Bean
小总结
配置类本身不会被CGLIB增强,放进容器内的就是原始类型 配置类内部不能通过方法调用来处理依赖,否则每次生成的都是一个新实例而并非IoC容器内的单例 配置类可以是普通类,所以@Bean方法可以使用private/final/static等进行修饰
Full模式
在常见的场景中,@Bean方法都会在标注有@Configuration的类中声明,以确保总是使用“Full模式”,这么一来,交叉方法引用会被重定向到容器的生命周期管理,所以就可以更方便的管理Bean依赖。
MyConfiguration01中的proxyBeanMethod属性修改为true,再次观察打印结果

MyConfiguration01变成了代理对象 user01和user02是同一个对象
优点:
可以支持通过常规Java调用相同类的@Bean方法而保证是容器内的Bean,这有效规避了在“Lite模式”下操作时难以跟踪的细微错误。特别对于萌新程序员,这个特点很有意义
缺点:
运行时会给该类生成一个CGLIB子类放进容器,有一定的性能、时间开销(这个开销在Spring Boot这种拥有大量配置类的情况下是不容忽视的,这也是为何Spring5.2新增了proxyBeanMethods属性的最直接原因)
正因为被代理了,所以@Bean方法不可以是private、final,也就是说必须是可以被继承的方法,具体原因可以去看CGLib的相关知识,这里不做过多赘述。
小总结
配置类会被CGLIB增强(生成代理对象),放进IoC容器内的是代理 配置类内部可以通过方法调用来处理依赖,并且能够保证是同一个实例,都指向IoC内的那个单例 @Bean方法不能被private/final等进行修饰,否则编译报错
应用场景
说了这么多,有很多小伙伴可能还是一头雾水,觉得这个东西是用来干嘛的?具体的使用场景是什么?下面我们就来讨论一下
首先举个小栗子,在SpringBoot中集成过数据库配置的小伙伴应该都知道,我们在配置数据库时,代码可能像下面这样
@Configurationpublic class DataSourceConfig {@Beanpublic DataSource dataSource() {// ...return dataSource;}@Bean(name = "transactionManager")public DataSourceTransactionManager transactionManager() {return new DataSourceTransactionManager(dataSource());}}
在准备DataSourceTransactionManager这个Bean时调用了dataSource()方法,当调用dataSource()方法时产生的数据源实例,和我们在容器中注册的数据源实例是不是同一个呢?如果不是同一个,那么事务管理器管理的不就是一个“全新数据源”么?何谈事务呢?所以这里的答案显而易见,通过外部调用获取的数据源实例其实就是注册到容器中的数据源实例,通过这个例子我们能知道什么呢?没错,Full模式和Lite模式的应用场景最主要的就是要解决组件依赖的问题。当容器中的组件需要依赖其他组件的实例时,如果存在外部调用,Full模式就可以帮助我们每次将依赖的组件指向容器中的组件实例,即每次获取的组件实例都是同一个,即Ioc容器中的实例。所以上面的例子中,如果设置为Lite模式,即
@Configuration(proxyBeanMethod=false)(默认为true),当被外部调用时,就会创建一个全新的数据源实例,这样显然是行不通的。
最佳实践
配置类组件之间无依赖关系时使用Lite模式,这样做可以加快容器启动速度,减少判断
配置类组件之间有依赖关系时使用Full模式,这样做可以保证当方法被调用时,得到之前单实例的组件
从这里也可以看出,Spring底层也是想通过这种方式来降低服务启动的开销。










