@Bean 修饰的方法表示初始化一个对象并交由Spring IOC去管理,@Bean 只能和@Component @Repository @Controller @Service @Configration 配合使用.
@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里声明了三个对象的初始化内容,如果换成XML配置,则等同于
<bean id="aDemoTest" class="demo.Bean.ADemo">
<property name="id" value="0719"></property>
<property name="name" value="ADemo.Name"></property>
</bean>
非常普通的将两个属性赋值.
其中ADemo和BDemo都是如此,而CDemo则引入了BDemo对象,从BDemo中的值中获取内容,赋值给CDemo.
从这里可以看出,@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;
}
稍加修改,以下代码中bDemo对象则是直接调用bDemoTest方法获得,但是这里就会出现问题,这个bDemo对象是重新new出来的,并非ioc容器中的对象,所以bDemo不是单例.
@Bean
public CDemo cDemoTest(){
CDemo cDemo=new CDemo();
BDemo bDemo=bDemoTest();
cDemo.setId(bDemo.getId());
cDemo.setName(bDemo.getName());
return cDemo;
}
那么该如何解决这个问题呢?
答案很简单
使用@Configuration注解替换掉@Component
不配置@Configuration: 当内部方法 bean发⽣彼此依赖的时候会导致多例
配置之后:
1,会将配置的类(Beans)由普通类型转变为cglib代理类型
2,将将配置的类(Beans)的beanDefinitioin配置类属性赋值为full类型的(不配置的是lite)