@Autowired
自动装配(Autowired) 是Spring 提供的一个注解,其实在JDK 中也提供了两个注解来实现类似的功能:@Resource @Inject 。目的是为了实现依赖注入(Dependence Injection)【在系统运行中,动态的向某个对象提供它所需要的其他对象】 , 在讲到 IOC(Inversion of Control,控制反转) 时的核心知识点 。
开始之前我先引用 大牛 开涛老师 对 IOC、DI 的讲解吧
IOC(Inversion of Control):IOC并不是一种技术,他只是一种设计思想。意味着你在 Spring 创建好的对象交给容器控制,而不是如传统方式,由你自己直接控制。那么 IOC 设计思想中,是谁控制了谁?什么是反转?
谁控制谁?IOC 容器控制了容器对象,创建的容器都将自动交给IOC容器进行控制。控制它们的外部资源获取等。
什么是反转?容器帮忙创建以及注入对象。
目录
在分析源码之前我们先看一下他的定义以及工作机制
@Autowired 注解定义
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
boolean required() default true;
}
可以看出 Autowired 是作用与运行时: @Retention(RetentionPolicy.RUNTIME)
可以作用的范围:构造器(ElementType.CONSTRUCTOR)、方法(ElementType.METHOD)、参数(ElementType.PARAMETER)、属性(ElementType.FILED)、注解类(ElementType.ANNOTATION_TYPE)
工作机制展示
这里我们实现将动物注入给人,为人类服务
首先定义两个接口:Person Animal
Person
public interface Person{
void service();
void setAnimal(Animal animal);
}
Animal
public interface Animal{
void use();
}
然后我们开始创建实现类
BussinessPerson
@Component
public class BussinessPerson implatements Person{
/**注入动物*/
@Autowired
private Animal animal;
@Overide
public void service(){
this.animal.use();
}
@Overide
public void setAnimal( Animal animal){
this.animal = animal;
}
}
Dog
@Component
public class Dog implaments Animal{
@Overide
public void use(){
System.out.println("狗["+Dog.class.getSimpleName()+"]是来看门的");
}
}
此时我们创建一个测试Test
/*
* AppConfig.java
* @ComponentScan(basePackage={"人和动物实现类的包"})
* @Configuration
* public class AppConfig(){}
*
*/
@Test
public void test01(){
ApplicationContext applicationContext = new AnnocationConfigApplicationContext(AppConfig.class);
Person person = applicationCOntext.getBean(BussinessPerson.class);
person.service();
}
可以看到我们此时并没有主动去创建动物对象,但是控制台输出了这么一行
狗[Dog]是来看门的
这是为什么呢?为什么连名字都有了?
名字:默认由实体类的类名,可以在注入的时候为其设置名字(BussinessPeron内的属性字段):
@Qualifier("dog1")
private Animal animal;
然后你就会发现控制台输出就变为了 狗[gog1]是来看门的
为什么自动就选择了狗呢?
其实 @Autowired 是默认按类型 注入的,此时你在Dog的旁边创建一个 Cat,这时就会报错了,因为不知道你要注入的到底是cat 还是dog。在按类型不能注入的时候,会选择按名称注入,将上面属性名从 (BussinessPeron内的属性字段) animal 改为 dog 或 cat 就会注入 Dog 或 Cat
Cat
@Component
public class Cat implements Animal{
public void use() {
System.out.println("猫["+ Cat.class.getSimpleName()+"]是来摸的");
}
}
报错信息:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'bussinessPerson': Unsatisfied dependency expressed through field 'animal'; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'cn.dcpnet.attest.Animal' available: expected single matching bean but found 2: cat,dog
可以看到容器并不知道你要哪一个动物,此时我们将BussinessPerson中属性上加入
private Animal dog;//这里注入的是dog
private Animal cat;//这里注入的是cat
@Qualifier("cat")
private Animal dog;//这里注入的是cat
如果你的名字是 dog ,就会看到控制台输出 狗[Dog]是来看门的 ,如果你的名字是cat ,那么你将看到的是 猫[Cat]是来摸的
/**注入动物*/
@Autowired
@Qualifier("cat")
private Animal dog;
输出
猫[Cat]是来摸的
Process finished with exit code 0
当然你也可以通过配置@Primary 来标志首选注解,如果在 cat上配置了,那么在不指定的情况下都是注解的 cat:
@Primary
@Component
public class Cat implements Animal{
//....
}
/*
*BussinessPerson 中保持最开始不变
* /**注入动物*/
* @Autowired
* private Animal animal;
*/
输出
猫[Cat]是来摸的
Process finished with exit code 0
还记得源码里面右一句 boolean required() default true; 吗,这是你告诉容器,require为true的时候必须找到对应对象,而为false的时候,没有找到注入也不会报错,当然你用到了这个东西就会抛出空指针异常。
源码分析
在Autowired 注解类源码上看到有这么一句话
@see AutowiredAnnotationBeanPostProcessor
这个是处理Autowired注解的Bean后置处理器
正在编写中。。