先来看几个问题
-
通过注解的方式注入依赖对象,介绍一下你知道的几种方式
-
@Autowired和@Resource有何区别
-
说一下@Autowired查找候选者的过程
-
说一下@Resource查找候选者的过程
-
@Qulifier有哪些用法?
-
@Qulifier加在类上面是干什么用的?
-
@Primary是做什么的?
-
泛型注入用过么?
这些问题如果你都ok,那么恭喜你,很厉害。
本文内容
-
介绍spring中通过注解实现依赖注入的所有方式
-
@Autowired注解
-
@Qualifier注解
-
@Resource注解
-
@Primary注解
-
@Bean中注入的几种方式
-
将指定类型的所有bean,注入到集合中
-
将指定类型的所有bean,注入到map中
-
注入泛型
-
依赖注入源码方面的一些介绍
本文内容比较多,所有知识点均有详细案例,大家一定要敲一遍,加深理解。
@Autowired:注入依赖对象
作用
实现依赖注入,spring容器会对bean中所有字段、方法进行遍历,标注有@Autowired注解的,都会进行注入。
看一下其定义:
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
/**
* Declares whether the annotated dependency is required.
* <p>Defaults to {
@code true}.
*/
boolean required() default true;
}
可以用在构造器、方法、方法参数、字段、注解上。
参数:
required:标注的对象是否必须注入,可能这个对象在容器中不存在,如果为true的时候,找不到匹配的候选者就会报错,为false的时候,找不到也没关系 。
@Autowire查找候选者的过程
查找过程有点复杂,看不懂的可以先跳过,先看后面案例,本文看完之后,可以回头再来看这个过程。
@Autowired标注在字段上面:假定字段类型为一个自定义的普通的类型,候选者查找过程如下
@Autowired标注在方法上或者方法参数上面:假定参数类型为为一个自定义的普通的类型,候选者查找过程如下:
上图中深色的表示方法注入和字段注入查找过程的不同点。
上图中展示的是方法中只有一个参数的情况,如果有多个参数,就重复上面的过程,直到找到所有需要注入的参数。
将指定类型的所有bean注入到Collection中
如果被注入的对象是Collection类型的,可以指定泛型的类型,然后会按照上面的方式查找所有满足泛型类型所有的bean
将指定类型的所有bean注入到Map中
如果被注入的对象是Map类型的,可以指定泛型的类型,key通常为String类型,value为需要查找的bean的类型,然后会按照上面方式查找所有注入value类型的bean,将bean的name作为key,bean对象作为value,放在HashMap中,然后注入。
@Autowired查找候选者可以简化为下面这样
按类型找->通过限定符@Qualifier过滤->@Primary->@Priority->根据名称找(字段名称或者方法名称)
概括为:先按类型找,然后按名称找
案例1:@Autowired标注在构造器上,通过构造器注入依赖对象
Service1
package com.javacode2018.lesson001.demo26.test0;
import org.springframework.stereotype.Component;
@Component
public class Service1 {
}
Service2
package com.javacode2018.lesson001.demo26.test0;
import org.springframework.stereotype.Component;
@Component
public class Service2 {
private Service1 service1;
public Service2() { //@1
System.out.println(this.getClass() + "无参构造器");
}
public Service2(Service1 service1) { //@2
System.out.println(this.getClass() + "有参构造器");
this.service1 = service1;
}
@Override
public String toString() { //@2
return "Service2{" +
"service1=" + service1 +
'}';
}
}
Service2中依赖于Service1,有2个构造方法
@1:无参构造器
@2:有参构造器,可以通过这个传入依赖的Service1
@3:重写了toString方法,一会打印测试的时候方便查看
来个总的配置文件
package com.javacode2018.lesson001.demo26.test0;
import org.springframework.context.annotation.ComponentScan;
@ComponentScan //@1
public class MainConfig0 {
}
@1:会自动扫描当前类所在的包,会将Service1和Service2注册到容器。
来个测试用例
package com.javacode2018.lesson001.demo26;
import com.javacode2018.lesson001.demo26.test0.MainConfig0;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class InjectTest {
@Test
public void test0() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig0.class);
for (String beanName : context.getBeanDefinitionNames()) {
System.out.println(String.format("%s->%s", beanName, context.getBean(beanName)));
}
}
}
main方法中启动容器,加载MainConfig0配置类,然后输出容器中所有的bean
运行部分输出
class com.javacode2018.lesson001.demo26.test0.Service2无参构造器
service1->com.javacode2018.lesson001.demo26.test0.Service1@4a94ee4
service2->Service2{service1=null}
输出中可以看出调用了Service2的无参构造器,service2中的service1为null
通过@Autowired指定注入的构造器
在Service2有参有参构造器上面加上@Autowired注解,如下:
@Autowired
public Service2(Service1 service1) {
System.out.println(this.getClass() + "有参构造器");
this.service1 = service1;
}
再次运行test0()
class com.javacode2018.lesson001.demo26.test0.Service2有参构造器
service1->com.javacode2018.lesson001.demo26.test0.Service1@4ec4f3a0
service2->Service2{service1=com.javacode2018.lesson001.demo26.test0.Service1@4ec4f3a0}
Service2有参构造器被调用了,service2中的service1有值了。
案例2:@Autowired标注在方法上,通过方法注入依赖的对象
Service1
package com.javacode2018.lesson001.demo26.test1;
import org.springframework.stereotype.Component;
@Component
public class Service1 {
}
Service2
package com.javacode2018.lesson001.demo26.test1;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class Service2 {
private Service1 service1;
@Autowired
public void injectService1(Service1 service1) { //@1
System.out.println(this.getClass().getName() + ".injectService1()");
this.service1 = service1;
}
@Override
public String toString() {
return "Service2{" +
"service1=" + service1 +
'}';
}
}
@1:方法上标注了@Autowired,spring容器会调用这个方法,从容器中查找Service1类型的bean,然后注入。
来个总的配置文件
package com.javacode2018.lesson001.demo26.test1;
import org.springframework.context.annotation.ComponentScan;
@ComponentScan
public class MainConfig1 {
}
来个测试用例
InjectTest中加个方法
@Test
public void test1() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig1.class);
for (String beanName : context.getBeanDefinitionNames()) {
System.out.println(String.format("%s->%s", beanName, context.getBean(beanName)));
}
}
运行输出
com.javacode2018.lesson001.demo26.test1.Service2.injectService1()
service1->com.javacode2018.lesson001.demo26.test1.Service1@9597028
service2->Service2{service1=com.javacode2018.lesson001.demo26.test1.Service1@9597028}
通过injectService1方法成功注入service1
案例3:@Autowired标注在setter方法上,通过setter方法注入
上面2种通过构造器,和通过普通的一个方法注入,不是很常见,可以将@Autowired标注在set方法上面,来注入指定的对象
Service1
package com.javacode2018.lesson001.demo26.test2;
import org.springframework.stereotype.Component;
@Component
public class Service1 {
}
Service2
package com.javacode2018.lesson001.demo26.test2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class Service2 {
private Service1 service1;
@Autowired
public void setService1(Service1 service1) { //@1
System.out.println(this.getClass().getName() + ".setService1方法");
this.service1 = service1;
}
@Override
public String toString() {
return "Service2{" +
"service1=" + service1 +
'}';
}
}
@1:标准的set方法,方法上使用了 @Autowired,会通过这个方法注入Service1类型的bean对象。
来个总的配置文件
package com.javacode2018.lesson001.demo26.test2;
import org.springframework.context.annotation.ComponentScan;
@ComponentScan
public class MainConfig2 {
}
来个测试用例
@Test
public void test2() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig2.class);
for (String beanName : context.getBeanDefinitionNames()) {
System.out.println(String.format("%s->%s", beanName, context.getBean(beanName)));
}
}
运行输出
com.javacode2018.lesson001.demo26.test2.Service2.setService1方法
service1->com.javacode2018.lesson001.demo26.test2.Service1@6069db50
service2->Service2{service1=com.javacode2018.lesson001.demo26.test2.Service1@6069db50}
案例4:@Autowired标注在方法参数上
Service1
package com.javacode2018.lesson001.demo26.test3;
import org.springframework.stereotype.Component;
@Component
public class Service1 {
}
Service2
package com.javacode2018.lesson001.demo26.test3;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class Service2 {
private Service1 service1;
@Autowired
public void injectService1(Service1 service1, String name) { //@1
System.out.println(String.format("%s.injectService1(),{service1=%s,name=%s}", this.getClass().getName(), service1, name));
this.service1 = service1;