@Configuration详解

一、@Configuration

  1. @Target({ElementType.TYPE})

  2. @Retention(RetentionPolicy.RUNTIME)

  3. @Documented

  4. @Component

  5. public @interface Configuration {

  6. @AliasFor(

  7. annotation = Component.class

  8. )

  9. String value() default "";

  10. }

可以看到在@Configuration注解中是包含@Component注解的,被@Configuration修饰的类被定义为一个Spring容器(应用上下文)

@Configuration就相当于Spring配置文件中的<beans />标签,里面可以配置bean

二、@Bean

@Bean相当于Spring配置文件中的<bean />标签可以在Spring容器中注入一个bean


  1. @Configuration

  2. public class TestConfiguration {

  3. @Bean

  4. public TestBean testBean() {

  5. return new TestBean();

  6. }

  7. }

上述代码相当于实例化一个TestBean并交给Spring容器管理

ps:

1、@Bean注解在返回实例的方法上,如果未通过@Bean指定bean的名称,则默认与方法名相同

2、@Bean注解默认作用域为单例singleton作用域,可通过@Scope(“prototype”)设置为多例

三、依赖注入

  1. @Configuration

  2. public class TestConfiguration {

  3. @Bean

  4. public TestBean testBean() {

  5. return new TestBean();

  6. }

  7. @Bean

  8. public DIBean diBean() {

  9. return new DIBean(testBean());

  10. }

  11. }

如上述代码,通过在@Bean方法中调用其他@Bean注解的方法来实现依赖注入

ps:

当需要强制指定实例化bean的顺序,可以通过@Order或@DependsOn注解来实现

除此之外我们还能使用@Component声明Spring Bean

@Configuration和@Component区别


  1. public class Car {

  2. private int id;

  3. private String name;

  4. public int getId() {

  5. return id;

  6. }

  7. public void setId(int id) {

  8. this.id = id;

  9. }

  10. public String getName() {

  11. return name;

  12. }

  13. public void setName(String name) {

  14. this.name = name;

  15. }

  16. }

  17. public class Driver {

  18. private int id;

  19. private String name;

  20. private Car car;

  21. public int getId() {

  22. return id;

  23. }

  24. public void setId(int id) {

  25. this.id = id;

  26. }

  27. public String getName() {

  28. return name;

  29. }

  30. public void setName(String name) {

  31. this.name = name;

  32. }

  33. public Car getCar() {

  34. return car;

  35. }

  36. public void setCar(Car car) {

  37. this.car = car;

  38. }

  39. }


  1. import org.springframework.context.annotation.Bean;

  2. import org.springframework.context.annotation.Configuration;

  3. @Configuration

  4. public class MyTestConfig {

  5. @Bean

  6. public Driver driver() {

  7. Driver driver = new Driver();

  8. driver.setId(1);

  9. driver.setName("driver");

  10. driver.setCar(car());

  11. return driver;

  12. }

  13. @Bean

  14. public Car car() {

  15. Car car = new Car();

  16. car.setId(1);

  17. car.setName("car");

  18. return car;

  19. }

  20. }


  1. import org.springframework.context.annotation.Bean;

  2. import org.springframework.context.annotation.Configuration;

  3. @Component

  4. public class MyTestConfig {

  5. @Bean

  6. public Driver driver() {

  7. Driver driver = new Driver();

  8. driver.setId(1);

  9. driver.setName("driver");

  10. driver.setCar(car());

  11. return driver;

  12. }

  13. @Bean

  14. public Car car() {

  15. Car car = new Car();

  16. car.setId(1);

  17. car.setName("car");

  18. return car;

  19. }

  20. }

上面两段代码除MyTestConfig类上的注解不同之外其他都相同,但Spring对两者的处理方式是完全不一样的。

  • 第一段代码会像我们期望的一样正常运行,因为driver()这段代码中driver.setCar(car())方法会由Spring代理执行

    Spring发现方法所请求的Bean已经在容器中,那么就直接返回容器中的Bean。所以全局只有一个Car对象的实例。

  • 第二段代码在执行driver() 时driver.setCar(car())不会被Spring代理,会直接调用car()方法获取一个全新的Car对象实例,所以全局会有多个Car对象的实例

造成这种差异的原因如下:

概括就是 @Configuration 中所有带 @Bean 注解的方法都会被动态代理,因此调用该方法返回的都是同一个实例。

其工作原理是:如果方式是首次被调用那么原始的方法体会被执行并且结果对象会被注册到Spring上下文中,之后所有的对该方法的调用仅仅只是从Spring上下文中取回该对象返回给调用者。

在上面的第二段代码中,driver.setCar(car())只是纯JAVA方式的调用,多次调用该方法返回的是不同的对象实例。

要修正第二段代码中的问题,可以使用@Autowired如下所示:


  1. import org.springframework.beans.factory.annotation.Autowired;

  2. import org.springframework.context.annotation.Bean;

  3. import org.springframework.context.annotation.Configuration;

  4. import org.springframework.stereotype.Component;

  5. //@Configuration

  6. @Component

  7. public class MyTestConfig2 {

  8. @Autowired

  9. Car car;

  10. @Bean

  11. public Driver driver() {

  12. Driver driver = new Driver();

  13. driver.setId(1);

  14. driver.setName("driver");

  15. driver.setCar(car);

  16. return driver;

  17. }

  18. @Bean

  19. public Car car() {

  20. Car car = new Car();

  21. car.setId(1);

  22. car.setName("car");

  23. return car;

  24. }

  25. }

验证


  1. import org.springframework.context.ApplicationContext;

  2. import org.springframework.context.annotation.AnnotationConfigApplicationContext;

  3. public class TestMain {

  4. public static void main(String[] args) {

  5. // TODO Auto-generated method stub

  6. // @Configuration注解的spring容器加载方式,用AnnotationConfigApplicationContext替换ClassPathXmlApplicationContext

  7. ApplicationContext context = new AnnotationConfigApplicationContext(MyTestConfig.class);

  8. // 获取bean

  9. Driver driver = (Driver) context.getBean("driver");

  10. // 获取bean

  11. Car car = (Car) context.getBean("car");

  12. boolean result = driver.getCar() == car;

  13. System.out.println(result ? "同一个car" : "不同的car");

  14. }

  15. }

更详细的探究,请参阅另一篇博客:@Component和@Configuration作为配置类的差别_一号搬砖手的博客-CSDN博客_component和configuration的区别 

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐