@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
@AliasFor(
annotation = Component.class
)
String value() default "";
}
可以看到在@Configuration注解中是包含@Component注解的,被@Configuration修饰的类被定义为一个Spring容器(应用上下文)
@Configuration就相当于Spring配置文件中的标签,里面可以配置bean
@Bean相当于Spring配置文件中的标签可以在Spring容器中注入一个bean
优先取注解中 name 指定的名字做为 bean name。
如果注解中没有指定的话,就取 methodName 做为 bean name。
@Configuration
public class TestConfiguration {
@Bean
public TestBean testBean() {
return new TestBean();
}
}
上述代码相当于实例化一个TestBean并交给Spring容器管理
ps:
@Bean注解在返回实例的方法上,如果未通过@Bean指定bean的名称,则默认与方法名相同
@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
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");
}
}
结果是同一个car
@Qualifier限定哪个bean应该被自动注入。当Spring无法判断出哪个bean应该被注入时,@Qualifier注解有助于消除歧义bean的自动注入。参见下面的例子
public class Staff{
@Autowired
private User user;
}
我们有两个bean定义为User类的实例。
<beanid="staff"class="com.test.Staff"/>
<beanid="user1"class="com.test.User">
<property name="name"value="zhangsan"/></bean>
<beanid="user2"class="com.test.User">
<property name="name"value="lisi"/></bean>
Spring 知道哪个bean应该自动注入?不。当您运行上面的例子时,抛出如下异常:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [com.test.User] is defined: expected single matching bean but found2: [user1, user2]
要解决以上问题,你需要使用@Quanlifier注解告诉Spring 哪个bean应该被autowired的。
public class Staff
{
@Autowired
@Qualifier("user1")
private User user;
}
MyTestConfig
import com.orm.mybatis.dynamic.entity.Car;
import com.orm.mybatis.dynamic.entity.Driver;
import org.springframework.beans.factory.annotation.Qualifier;
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;
}
// @Bean("carInjection") 一样的 默认方法名
@Bean
public Car carInjection(Car car){//没有@Qualifier则默认注入名称为car的Bean
return car;
}
// @Bean("carQualifierInjection") 一样的 默认方法名
@Bean
public Car carQualifierInjection(@Qualifier("car") Car car){ //注入Bean名称为car
return car;
}
}
BeanTest
public class BeanTest extends AndrewApplicationTests {
@Autowired
public Driver driver;
@Autowired
public Car car;
@Autowired
public Car carInjection;
@Autowired
public Car carQualifierInjection;
@Test
public void test2(){
Car car1 = car;
Car car2 = driver.getCar();
Car car3 = carInjection;
Car car4 = carQualifierInjection;
System.out.println(car1);
System.out.println(car2);
System.out.println(car3);
System.out.println(car4);
System.out.println(car1==car2&&car2==car3&&car3==car4);
}
}
com.orm.mybatis.dynamic.entity.Car@67d32a54
com.orm.mybatis.dynamic.entity.Car@67d32a54
com.orm.mybatis.dynamic.entity.Car@67d32a54
com.orm.mybatis.dynamic.entity.Car@67d32a54
true