@Configuration详解
一、@Configuration
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface Configuration { @AliasFor( annotation = Component.class ) String value() default ""; }
可以看到在@Configuration
注解中是包含@Component
注解的,被@Configuration
修饰的类被定义为一个Spring容器(应用上下文)
@Configuration
就相当于Spring配置文件中的<beans />
标签,里面可以配置bean
二、@Bean
@Bean相当于Spring配置文件中的<bean />
标签可以在Spring容器中注入一个bean
@Configuration public class TestConfiguration { @Bean public TestBean testBean() { return new TestBean(); } }
上述代码相当于实例化一个TestBean并交给Spring容器管理
ps:
1、@Bean注解在返回实例的方法上,如果未通过@Bean指定bean的名称,则默认与方法名相同
2、@Bean注解默认作用域为单例singleton作用域
,可通过@Scope(“prototype”)设置为多例
三、依赖注入
@Configuration public class TestConfiguration { @Bean public TestBean testBean() { return new TestBean(); } @Bean public DIBean diBean() { return new DIBean(testBean()); } }
如上述代码,通过在@Bean方法中调用其他@Bean注解的方法来实现依赖注入
ps:
当需要强制指定实例化bean的顺序
,可以通过@Order或@DependsOn注解来实现
除此之外我们还能使用@Component声明Spring Bean
@Configuration和@Component区别
public class Car { private int id; private String name; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } } public class Driver { private int id; private String name; private Car car; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Car getCar() { return car; } public void setCar(Car car) { this.car = car; } }
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class MyTestConfig { @Bean public Driver driver() { Driver driver = new Driver(); driver.setId(1); driver.setName("driver"); driver.setCar(car()); return driver; } @Bean public Car car() { Car car = new Car(); car.setId(1); car.setName("car"); return car; } }
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Component public class MyTestConfig { @Bean public Driver driver() { Driver driver = new Driver(); driver.setId(1); driver.setName("driver"); driver.setCar(car()); return driver; } @Bean public Car car() { Car car = new Car(); car.setId(1); car.setName("car"); return car; } }
上面两段代码除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如下所示:
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.stereotype.Component; //@Configuration @Component public class MyTestConfig2 { @Autowired Car car; @Bean public Driver driver() { Driver driver = new Driver(); driver.setId(1); driver.setName("driver"); driver.setCar(car); return driver; } @Bean public Car car() { Car car = new Car(); car.setId(1); car.setName("car"); return car; } }
验证
import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class TestMain { public static void main(String[] args) { // TODO Auto-generated method stub // @Configuration注解的spring容器加载方式,用AnnotationConfigApplicationContext替换ClassPathXmlApplicationContext ApplicationContext context = new AnnotationConfigApplicationContext(MyTestConfig.class); // 获取bean Driver driver = (Driver) context.getBean("driver"); // 获取bean Car car = (Car) context.getBean("car"); boolean result = driver.getCar() == car; System.out.println(result ? "同一个car" : "不同的car"); } }