本文是学习过程中的笔记,原作者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;
}

结果
image-1652279111516
image-1652539293327
这里显示@Configuration注解的类是个被cglib代理的类,而@Component的类是个普通的实例化的类

这里编写一个单元测试测试下方法是否有被代理

@SpringBootTest
class MainTest {
  @Test
  void test() {
    assertThat(school.getTeacher()).isEqualTo(teacher);
  }

  @Autowired private School school;
  @Autowired private Teacher teacher;
}

当MyConfig上添加的@Configuration时候,运行这个测试
image-1652277236236
这里测试通过,说明teacher选取的是同一个对象,接着我们换成@Component继续进行测试

@Component
public class MyConfig {
  @Bean
  public Teacher teacher() {
    return new Teacher();
  }

  @Bean
  public School school() {
    return new School(teacher());
  }
}

接着继续运行单元测试
image-1652277439158
运行失败,这说明School实例化时所取得teacher与@Bean注解得teacher并不是一个对象

由于@Configuration注解的类是代理类,所以这里需要去源代码中一坛究竟
这里我们选择ConfigurationClassPostProcessor这个后置处理类来查看源代码
image-1652279932516
这个方法即为处理@Configuration注解类中@Bean注解的方法
首先使用了for循环判断类中@Bean的方法,将其放入一个configBeanDefs的LinkedHashMap中去
image-1652280072826
然后判断集合中元素是否为空 如果为空则return方法,否则继续执行
image-1652280456228
这里则将原本的class替换成代理后的class

我们查看如何增强的这个类
进入这个enhance方法
image-1652280850621
这里使用了createClass 我们进入这个newEnhancer方法,这里则创建了一个Cglib的代理对象
image-1652280995553
然后我们查看
image-1652281162062
当代理类生成对象是,使用CALLBACK_FILTER进行方法的拦截,这里我们进入
这个BeanMethodInterceptor类
image-1652281439242
在这个类的这个方法判断当前@Bean的方法是否是已经需要执行一个方法(或者说注入一个Bean),如果执行这个方法上作为参数(即为注入的是个Bean),则走下方,通过BeanFactory进行查找,直接注入,而不用二次创建,也就是上方单元测试通过的原因
image-1652281564716

Q.E.D.