自动装配,到底是什么,这对于新手来说,比如说我刚入行的时候,总觉得很神秘,百思不得其解。下面二万给大家来剖析一下
一个对象A,对另外对象的引用B,在传统方式上,需要在A中new一个对象B,或者通过构造函数中传入一个new出来的B。
自动装配就是可以简单的理解通过@Autowired,在A中声明一下B就可以直接使用B并不需要new一个B对象。
很多新手也会疑问,我new不new不就一行代码的问题吗,通过自动装配可以达到对象的使用和创建解耦,将对象B的管理交给ioc容器。这里可以自行百度一下。
下面,我们来探究@Autowired的神秘面纱吧
二话不说,先新建配置类,并且进行注解扫描包
@Configuration
@ComponentScan("com.erwan.cap9")
public class Cap9MainConfig {
@Bean("testDao2")
public TestDao testDao2() {
TestDao testDao = new TestDao();
testDao.setNum(2);
return testDao;
}
@Bean
public TestDao testDao() {
TestDao testDao = new TestDao();
return testDao;
}
}
再建一个TestDao
public class TestDao {
public Integer num = 1;
public TestDao() {
}
public Integer getNum() {
return num;
}
public void setNum(final Integer num) {
this.num = num;
}
@Override
public String toString() {
return "OrderDao [num=" + num + "]";
}
}
新建两个服务类TestService1,TestService2,都持有TestDao的引用
@Service
public class TestService {
@Autowired
private TestDao testDao;
public void println() {
System.out.println("TestService ......." + testDao);
}
}
@Service
public class TestService2 {
@Autowired
private TestDao testDao2;//如果ioc容器中没有testDao2,则会注入testDao
public void println() {
System.out.println("TestService2 ......." + testDao2);
}
}
新建测试类
@Test
public void cap9() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Cap9MainConfig.class);
TestService testService1 = context.getBean(TestService.class);
TestService2 testSerice2 = context.getBean(TestService2.class);
testService1.println();
testSerice2.println();
}
OrderDao [num=1]
OrderDao [num=2]
TestService .......OrderDao [num=1]
TestService2 .......OrderDao [num=2]
通过测试结果可以看到,当注册的名称为testDao2时,TestService2中持有的实例是配置类里面beanName为testDao2的实例
接下来 我们在TestDao里面加一个注解,@Primary
@Repository
@Primary
public class TestDao {
public Integer num = 1;
public TestDao() {
}
public Integer getNum() {
return num;
}
public void setNum(final Integer num) {
this.num = num;
}
@Override
public String toString() {
return "OrderDao [num=" + num + "]";
}
}
测试结果如下
OrderDao [num=1]
OrderDao [num=2]
TestService .......OrderDao [num=1]
TestService2 .......OrderDao [num=1]
从结果上面来看,只要在TestDao类上加了@Primary这个注解,就是以这个为准。
接下来我们在TestService1中加入 @Qualifier("testDao2")
@Service
public class TestService {
@Autowired
@Qualifier("testDao2")
private TestDao testDao;
public void println() {
System.out.println("TestService ......." + testDao);
}
}
执行测试类
TestService .......OrderDao [num=2]
TestService .......OrderDao [num=1]
从结果分析,当你指定了哪个bean实例注入,它就以该实例为准,@Primary并不生效。
所以总结一下,根据名称注入<@Primary<@Qualifier ,其中需要注意一点的是根据名称(也就是TestService2中的TestDao testDao2)注入testDao2,如果找不到testDao2,则会自动注入testDao
接下来我们继续看@Autowired的required属性
在配置类里面注释掉testDao2的定义,
@Configuration
@ComponentScan("com.erwan.cap9")
public class Cap9MainConfig {
// @Bean("testDao2")
// public TestDao testDao2() {
// TestDao testDao = new TestDao();
// testDao.setNum(2);
// return testDao;
// }
@Bean
public TestDao testDao() {
TestDao testDao = new TestDao();
return testDao;
}
}
然后执行测试类,会报错。因为@Autowired默认required是true,如果在ioc容器里面找bean(testDao2)找不到会报错
我们把TestService里面的@Autowired(required=false)
@Service
public class TestService {
@Autowired(required = false)
@Qualifier("testDao2")
private TestDao testDao;
public void println() {
System.out.println("TestService ......." + testDao);
}
}
测试结果如下
TestService .......null
TestService .......OrderDao [num=1]
所以Autowired中的required=false的作用不言而喻,就是为了在ioc找不到依赖的bean(testDao2)时,会注入一个空,并不会报错.
接下来我们看看@Autowired可以使用的地方,在@Autowired的地方按F3,可以看到如下
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
@Target中指定的包括构造函数,方法,参数,字段等
这里我们做个简单的实验
新建配置类,把水果类都扫描到ioc容器中
@ComponentScan("com.erwan.cap10.bean")
public class Cap10MainConfig {
}
新建下面水果类 fruit,banana,apple,pears ,并且在类上都加入@Component
@Component
public class Fruit {
private Banana banana;
private Apple apple;
private Pear pear;
private Plum plum;
@Override
public String toString() {
return "Fruit [banana=" + banana + ", apple=" + apple + ", pear=" + pear + ", plum=" + plum + "]";
}
}
@Component
public class Apple {
}
@Component
public class Banana {
}
@Component
public class Pear {
}
新建一个测试类
@Test
public void cap10() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Cap10MainConfig.class);
Pear pear = context.getBean(Pear.class);
Fruit fruit = context.getBean(Fruit.class);
System.out.println(pear);
System.out.println(fruit);
}
测试结果如下
com.erwan.cap10.bean.Pear@cc43f62
Fruit [banana=null, apple=null, pear=null]
从结果上来看,ioc确实把单例的bean在启动的时候就创建了,但是在Fruit中的所依赖的属性都是空的值。
现在我们通过@Autowired来看看,修改Fruit类
@Component
public class Fruit {
@Autowired
private Banana banana;
private Apple apple;
private Pear pear;
Fruit() {
}
@Autowired
Fruit(final Apple apple) {
this.apple = apple;
}
@Autowired
public void setPear(final Pear pear) {
this.pear = pear;
}
@Override
public String toString() {
return "Fruit [banana=" + banana + ", apple=" + apple + ", pear=" + pear + "]";
}
}
启动测试类,结果如下
com.erwan.cap10.bean.Pear@3e08ff24
Fruit [banana=com.erwan.cap10.bean.Banana@4d1c005e, apple=com.erwan.cap10.bean.Apple@8462f31, pear=com.erwan.cap10.bean.Pear@3e08ff24]
最后我们来看看 和Autowired相类似的注解
@Autowired可以自动注入,@Resource(JSR250) 和@Inject(JSR330) 这两个注解也是可行的
这里就不演示了,说一下这三者的区别
注:@Resource与@Autowired的区别如下:
@Resource和Autowired一样可以装配bean
@Resource缺点: 不能支持@Primary功能,不能支持@Autowired(required = false)的功能
@Resource(name="testDao2") 可以通过名称来注入指定bean
@Qualifier("testDao2")对@Resource并不起作用
注:@Inject与@Autowired的区别如下:
@Inject和Autowired一样可以装配bean, 并支持@Primary功能, 可用于非spring框架.
@Inject缺点: 但不能支持@Autowired(required = false)的功能,需要引入第三方包javax.inject