本文是学习过程中的笔记,原作者https://github.com/lenve
@Configuration 我们常在Spring Boot项目中使用这个注解实现一些类的配置,这里贴下@Configuration和@Conmponet显示@Configuration 与 @Component 是类似的作用,但实际并非如此
@Configuration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
/**
* Explicitly specify the name of the Spring bean definition associated with the
* {@code @Configuration} class. If left unspecified (the common case), a bean
* name will be automatically generated.
* <p>The custom name applies only if the {@code @Configuration} class is picked
* up via component scanning or supplied directly to an
* {@link AnnotationConfigApplicationContext}. If the {@code @Configuration} class
* is registered as a traditional XML bean definition, the name/id of the bean
* element will take precedence.
* @return the explicit component name, if any (or empty String otherwise)
* @see AnnotationBeanNameGenerator
*/
@AliasFor(annotation = Component.class)
String value() default "";
/**
* Specify whether {@code @Bean} methods should get proxied in order to enforce
* bean lifecycle behavior, e.g. to return shared singleton bean instances even
* in case of direct {@code @Bean} method calls in user code. This feature
* requires method interception, implemented through a runtime-generated CGLIB
* subclass which comes with limitations such as the configuration class and
* its methods not being allowed to declare {@code final}.
* <p>The default is {@code true}, allowing for 'inter-bean references' via direct
* method calls within the configuration class as well as for external calls to
* this configuration's {@code @Bean} methods, e.g. from another configuration class.
* If this is not needed since each of this particular configuration's {@code @Bean}
* methods is self-contained and designed as a plain factory method for container use,
* switch this flag to {@code false} in order to avoid CGLIB subclass processing.
* <p>Turning off bean method interception effectively processes {@code @Bean}
* methods individually like when declared on non-{@code @Configuration} classes,
* a.k.a. "@Bean Lite Mode" (see {@link Bean @Bean's javadoc}). It is therefore
* behaviorally equivalent to removing the {@code @Configuration} stereotype.
* @since 5.2
*/
boolean proxyBeanMethods() default true;
}
@Component
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface Component {
/**
* The value may indicate a suggestion for a logical component name,
* to be turned into a Spring bean in case of an autodetected component.
* @return the suggested component name, if any (or empty String otherwise)
*/
String value() default "";
}
这里可以看出两个区别是,是否代理Bean的方法
public class Teacher {
}
public class School {
public class School {
private Teacher teacher;
public Teacher getTeacher() {
return teacher;
}
public School(Teacher teacher) {
this.teacher = teacher;
}
}
@Configuration
public class MyConfig {
@Bean
public Teacher teacher() {
return new Teacher();
}
@Bean
public School school() {
return new School(teacher());
}
}
编写一个测试程序查看两个不同注解生成的对象
@SpringBootTest
class MainTest {
@Test
void test() {
System.out.println(myConfig);
System.out.println(myComponent);
}
@Autowired private MyConfig myConfig;
@Autowired private School school;
@Autowired private Teacher teacher;
}
结果
这里显示@Configuration注解的类是个被cglib代理的类,而@Component的类是个普通的实例化的类
这里编写一个单元测试测试下方法是否有被代理
@SpringBootTest
class MainTest {
@Test
void test() {
assertThat(school.getTeacher()).isEqualTo(teacher);
}
@Autowired private School school;
@Autowired private Teacher teacher;
}
当MyConfig上添加的@Configuration时候,运行这个测试
这里测试通过,说明teacher选取的是同一个对象,接着我们换成@Component继续进行测试
@Component
public class MyConfig {
@Bean
public Teacher teacher() {
return new Teacher();
}
@Bean
public School school() {
return new School(teacher());
}
}
接着继续运行单元测试
运行失败,这说明School实例化时所取得teacher与@Bean注解得teacher并不是一个对象
由于@Configuration注解的类是代理类,所以这里需要去源代码中一坛究竟
这里我们选择ConfigurationClassPostProcessor这个后置处理类来查看源代码
这个方法即为处理@Configuration注解类中@Bean注解的方法
首先使用了for循环判断类中@Bean的方法,将其放入一个configBeanDefs的LinkedHashMap中去
然后判断集合中元素是否为空 如果为空则return方法,否则继续执行
这里则将原本的class替换成代理后的class
我们查看如何增强的这个类
进入这个enhance方法
这里使用了createClass 我们进入这个newEnhancer方法,这里则创建了一个Cglib的代理对象
然后我们查看
当代理类生成对象是,使用CALLBACK_FILTER进行方法的拦截,这里我们进入
这个BeanMethodInterceptor类
在这个类的这个方法判断当前@Bean的方法是否是已经需要执行一个方法(或者说注入一个Bean),如果执行这个方法上作为参数(即为注入的是个Bean),则走下方,通过BeanFactory进行查找,直接注入,而不用二次创建,也就是上方单元测试通过的原因
Q.E.D.