关于 Configuration 和 Component 注解的区别

@Configuration 和 @Component 注解的区别

  • 注解代码层面分析
  • 两者差别
    • 官方文档
    • 案例分析
    • Configuration 注解的 Full 模式和 Lite 模式
      • FULL 模式
      • LITE 模式
      • 特别说明

注解代码层面分析

Component 源码:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface Component {
	String value() default "";
}

Configuration源码:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {

	@AliasFor(annotation = Component.class)
	String value() default "";

	boolean proxyBeanMethods() default true;

}

定义来看, @Configuration 注解本质上还是 @Component,因此 @ComponentScan 能扫描到@Configuration 注解的类。

两者差别

官方文档

@Component和@Configuration都作为配置类,官方文档的差别:

@Component注解类中使用@Bean注解和在@Configuration中使用是不同的。
在@Component注解注册到 Spring 中的 Bean 是不会使用 CGLIB进行增强;
而@Configuration 注解注册到 Spring 中的 Bean 是一个 CGLIB 代理的 Bean

案例分析

通过如下案例进行分析:
分别向 Spring 容器中注入两个 Bean,MyConfig01 和 MyConfig02;

@Configuration
public class MyConfig01 {}
@Component
public class MyConfig02 {}

其中,MyConfig01 上添加的是 @Configuration 注解而 MyConfig02 上添加的则是 @Component 注解;
进行测试:

@SpringBootTest
public class DiffTest {
    @Test
    public void test1() {
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(DiffApplication.class);

        MyConfig01 myConfig01 = ctx.getBean("myConfig01", MyConfig01.class);
        MyConfig02 myConfig02 = ctx.getBean("myConfig02", MyConfig02.class);

        System.out.println("myConfig01 = " + myConfig01);
        System.out.println("myConfig02 = " + myConfig02);
    }
}

最终得到结果如下:

myConfig01 = com.zbbmeta.config.MyConfig01$$EnhancerBySpringCGLIB$$2944909f@2e0fdbe9
myConfig02 = com.zbbmeta.config.MyConfig02@16a3cc88

从上面这段代码中,我们可以得出来两个结论

  • @Configuration 注解也是 Spring 组件注解的一种,通过普通的 Bean 扫描也可以扫描到 @Configuration
  • @Configuration 注解注册到 Spring 中的 Bean 是一个 CGLIB 代理的 Bean,而不是原始 Bean,这一点和 @Component 不一样,@Component 注册到 Spring 容器中的还是原始 Bean。

Configuration 注解的 Full 模式和 Lite 模式

Spring对于配置类来讲,其实是有分类的,大体可以分为两类:

  • FULL模式: Full 模式最大的特点是会给配置类通过 CGLIB 生成一个代理,Configuration就是FULL类型;
  • LITE模式: Lite 模式,这种模式可以认为是一种精简模式,@Component就是Lite类型。

FULL 模式

Full 模式最大的特点是会给配置类通过 CGLIB 生成一个代理 ,Configuration就是FULL类型。
代码层面进行分析:

@Configuration
public class MyConfig01 {

    @Bean
    Dog dog(){
        return new Dog();
    }
    
    @Bean
    Person person(){
        Person person = new Person();
        //注意,这里是将上面注册到Spring中的dog, set到person
        person.setDog(dog());
        return person;
    }
}

测试:

@Test
  public void test1() {
      AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(DiffApplication.class);

      Person person = ctx.getBean("person", Person.class);
      Dog dog = ctx.getBean("dog", Dog.class);
      boolean result = person.getDog() == dog;
      System.out.println(result ? "同一个dog" : "不同的dog");

  }

测试结果:

同一个dog

Full 模式下,person() 方法中调用 dog() 方法的时候,调用的是一个代理对象中的 dog 方法。
在这个代理对象的 dog 方法中,先去检查 Spring 容器中是否存在 Dog 对象,如果存在,则直接使用 Spring 容器中的 dog 对象,就不会真正去执行 dog 方法而获取到一个新的 dog 对象了,如果 Spring 容器中不存在 dog 对象,才会创建新的 dog 对象出来。
总结:在 Full 模式下,person 中的 dog 对象和 dog 方法注册到 Spring 容器的 dog 对象是同一个
注意:Full 模式下 @Bean 注解标记的方法不能是 final 或者 private 类型,因为 final 或者 private 类型的方法无法被重写,也就没法生成代理对象。

LITE 模式

Lite 模式,这种模式可以认为是一种精简模式,@Component就是Lite类型。
将MyConfig01配置类上的注解变为@Component,其它不变:

@Component
public class MyConfig01 {
	...
}

测试结果:

不同的dog

总结:LITE模式下Spring 容器中拿到的就是原始的对象,而不是一个被代理过的对象。

特别说明

@Configuration注解如果设置了 proxyBeanMethods 属性为 false,就是 Lite 模式了

原文:https://mp.weixin.qq.com/s/RDanWIcrfZl1E9wfRFpXmg

你可能感兴趣的:(java,spring,boot)