Spring中@Bean与@Autowired的联系使用

2,086 阅读3分钟

@Bean

修饰的方法表示初始化一个对象并交由Spring IOC去管理,@Bean 只能和@Component @Repository @Controller @Service @Configration 配合使用.

@Autowired

@Autowired 可修饰变量和方法,用于完成自动装配(将需要的外部资源注入)

案例

三个POJO(ADemo,BDemo,CDemo)
一个Bean配置方法(Beans)
一个用来测试注入的BeanDemo

@Data
public class ADemo {
 
    private String id;
    private String name;
}
 
@Data
public class BDemo {
 
    private String id;
    private String name;
}
 
@Data
public class CDemo {
 
    private String id;
    private String name;
}
 
 
@Component
public class Beans {
 
    /*
    等同如下配置
        <bean id="aDemoTest" class="demo.Bean.ADemo">
        <property name="id" value="0719"></property>
        <property name="name" value="ADemo.Name"></property>
        </bean>
     */
    @Bean
    public ADemo aDemoTest(){
        ADemo aDemo=new ADemo();
        aDemo.setId("0719");
        aDemo.setName("ADemo.Name");
        return aDemo;
    }
 
    @Bean
    public BDemo bDemoTest(){
        BDemo bDemo=new BDemo();
        bDemo.setId("0719_1");
        bDemo.setName("BDemo.Name");
        return bDemo;
    }
 
    @Bean
    public CDemo cDemoTest(BDemo bDemo){
        CDemo cDemo=new CDemo();
        cDemo.setId(bDemo.getId());
        cDemo.setName(bDemo.getName());
        return cDemo;
    }
 
}
@Component
public class BeanDemo {
 
 
    private BDemo bDemo;
    private CDemo cDemo;
 
    //注释掉会抛出空指针异常
    @Autowired
    private void setBDemo(BDemo bDemo){
        //可以看出单例线程不安全
        bDemo.setName("set方法上添加@Autowired时修改了Name属性");
        this.bDemo=bDemo;
    }
 
    @Autowired
    private void initCDemo(CDemo cDemo){//无论方法名是set还是init或者其他,@Autowired都会检测bean并自动注入
        this.cDemo=cDemo;
    }
 
    public BDemo getbDemo() {
        return bDemo;
    }
 
    public CDemo getcDemo(){
        return cDemo;
    }
 
}

Beans里面的配置等同于

<bean id="aDemoTest" class="demo.Bean.ADemo">
        <property name="id" value="0719"></property>
        <property name="name" value="ADemo.Name"></property>
</bean>

从这里可以看出,@Bean注解的方法可以采用任何必要的java功能来产生bean实例,简言之,只有被@Bean注解了,Spring会尽可能的去生成这个bean,为什么是尽可能而不是必须呢?假设A对象依赖B,B对象依赖C,C对象又回过头依赖A,则会抛出异常.当然这里只是举个简单例子。

运行结果

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(classes={SpringConfig.class})
public class MyTest {
 
    @Autowired
    private ADemo aDemo;
 
    @Autowired
    private BeanDemo beanDemo;
 
    @Autowired
    private CDemo cDemo;
 
 
    @Test
    public void showBean(){
        System.out.println(aDemo.toString());
        System.out.println(beanDemo.getbDemo().toString());
        System.out.println(beanDemo.getcDemo().toString());
        System.out.println(cDemo.toString());
    }
}
ADemo(id=0719, name=ADemo.Name)
BDemo(id=0719_1, name=set方法上添加@Autowired时修改了Name属性)
CDemo(id=0719_1, name=set方法上添加@Autowired时修改了Name属性)
CDemo(id=0719_1, name=set方法上添加@Autowired时修改了Name属性)

可以看到@Autowired注解修饰的方法名称其实无所谓,重要的是将对象注入,这里获取到了BDemo和CDemo的bean对象,并将其赋值给BeanDemo.

从测试打印出来的结果可以看出,spring的Bean是单例,每次获取的值都是同一个.

在Beans里有提到,CDemo对象的值其实是从BDemo中获取过来的,而当bDemo的值在setBDemo方法中被修改之后,CDemo去获取的值也被修改.

补充

    @Bean
    public CDemo cDemoTest(BDemo bDemo){
        CDemo cDemo=new CDemo();
        cDemo.setId(bDemo.getId());
        cDemo.setName(bDemo.getName());
        return cDemo;
    }

等同于下面的代码,bDemo对象都是从IOC容器中获取的

 @Bean
    public CDemo cDemoTest(@Autowired BDemo bDemo){
        CDemo cDemo=new CDemo();
        cDemo.setId(bDemo.getId());
        cDemo.setName(bDemo.getName());
        return cDemo;
    }

当然也可以修改为

    @Bean
    public CDemo cDemoTest(){
        CDemo cDemo=new CDemo();
        BDemo bDemo=bDemoTest();
        cDemo.setId(bDemo.getId());
        cDemo.setName(bDemo.getName());
        return cDemo;
    }

但是问题就是,这里的bDemo对象是新new出来的,并非ioc容器中的对象,所以bDemo不是单例. 解决方法就是:
使用@Configuration注解替换掉@Component
不配置@Configuration: 当内部方法 bean发⽣彼此依赖的时候会导致多例的产生

配置之后:

  • 会将配置的类(Beans)由普通类型转变为cglib代理类型
  • 将将配置的类(Beans)的beanDefinitioin配置类属性赋值为full类型的(不配置的是lite)