小言_互联网的博客

你期望月薪4万,出门右拐,不送,这几个点,你也就是个初级的水平

455人阅读  评论(0)

先来看几个问题

  1. 通过注解的方式注入依赖对象,介绍一下你知道的几种方式

  2. @Autowired和@Resource有何区别

  3. 说一下@Autowired查找候选者的过程

  4. 说一下@Resource查找候选者的过程

  5. @Qulifier有哪些用法?

  6. @Qulifier加在类上面是干什么用的?

  7. @Primary是做什么的?

  8. 泛型注入用过么?

这些问题如果你都ok,那么恭喜你,很厉害。

本文内容

  1. 介绍spring中通过注解实现依赖注入的所有方式

  • @Autowired注解

  • @Qualifier注解

  • @Resource注解

  • @Primary注解

  • @Bean中注入的几种方式

  1. 将指定类型的所有bean,注入到集合中

  2. 将指定类型的所有bean,注入到map中

  3. 注入泛型

  4. 依赖注入源码方面的一些介绍

本文内容比较多,所有知识点均有详细案例,大家一定要敲一遍,加深理解。

@Autowired:注入依赖对象

作用

实现依赖注入,spring容器会对bean中所有字段、方法进行遍历,标注有@Autowired注解的,都会进行注入。

看一下其定义:


   
  1. @Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. public @ interface Autowired {
  5.      /**
  6.      * Declares whether the annotated dependency is required.
  7.      * <p>Defaults to {@code true}.
  8.      */
  9.     boolean required()  default  true;
  10. }

可以用在构造器、方法、方法参数、字段、注解上。

参数:

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


   
  1. package com.javacode2018.lesson001.demo26.test0;
  2. import org.springframework.stereotype.Component;
  3. @Component
  4. public class Service1 {
  5. }

Service2


   
  1. package com.javacode2018.lesson001.demo26.test0;
  2. import org.springframework.stereotype.Component;
  3. @Component
  4. public class Service2 {
  5.     private Service1 service1;
  6.     public Service2() {  //@1
  7.         System.out. println(this.getClass() +  "无参构造器");
  8.     }
  9.     public Service2(Service1 service1) {  //@2
  10.         System.out. println(this.getClass() +  "有参构造器");
  11.         this.service1 = service1;
  12.     }
  13.     @Override
  14.     public String toString() {  //@2
  15.          return  "Service2{" +
  16.                  "service1=" + service1 +
  17.                  '}';
  18.     }
  19. }

Service2中依赖于Service1,有2个构造方法

@1:无参构造器

@2:有参构造器,可以通过这个传入依赖的Service1

@3:重写了toString方法,一会打印测试的时候方便查看

来个总的配置文件


   
  1. package com.javacode2018.lesson001.demo26.test0;
  2. import org.springframework.context.annotation.ComponentScan;
  3. @ComponentScan  //@1
  4. public class MainConfig0 {
  5. }

@1:会自动扫描当前类所在的包,会将Service1和Service2注册到容器。

来个测试用例


   
  1. package com.javacode2018.lesson001.demo26;
  2. import com.javacode2018.lesson001.demo26.test0.MainConfig0;
  3. import org.junit.Test;
  4. import org.springframework.context.annotation.AnnotationConfigApplicationContext;
  5. public class InjectTest {
  6.     @Test
  7.     public void test0() {
  8.         AnnotationConfigApplicationContext context =  new AnnotationConfigApplicationContext(MainConfig0.class);
  9.          for (String beanName : context.getBeanDefinitionNames()) {
  10.             System.out. println(String.format( "%s->%s", beanName, context.getBean(beanName)));
  11.         }
  12.     }
  13. }

main方法中启动容器,加载MainConfig0配置类,然后输出容器中所有的bean

运行部分输出


   
  1. class com.javacode2018.lesson001.demo26.test0.Service2无参构造器
  2. service1->com.javacode2018.lesson001.demo26.test0.Service1@ 4a94ee4
  3. service2->Service2{service1=null}

输出中可以看出调用了Service2的无参构造器,service2中的service1为null

通过@Autowired指定注入的构造器

在Service2有参有参构造器上面加上@Autowired注解,如下:


   
  1. @Autowired
  2. public Service2(Service1 service1) {
  3.     System.out. println(this.getClass() +  "有参构造器");
  4.     this.service1 = service1;
  5. }

再次运行test0()


   
  1. class com.javacode2018.lesson001.demo26.test0.Service2有参构造器
  2. service1->com.javacode2018.lesson001.demo26.test0.Service1@ 4ec4f3a0
  3. service2->Service2{service1=com.javacode2018.lesson001.demo26.test0.Service1@ 4ec4f3a0}

Service2有参构造器被调用了,service2中的service1有值了。

案例2:@Autowired标注在方法上,通过方法注入依赖的对象

Service1


   
  1. package com.javacode2018.lesson001.demo26.test1;
  2. import org.springframework.stereotype.Component;
  3. @Component
  4. public class Service1 {
  5. }

Service2


   
  1. package com.javacode2018.lesson001.demo26.test1;
  2. import org.springframework.beans.factory.annotation.Autowired;
  3. import org.springframework.stereotype.Component;
  4. @Component
  5. public class Service2 {
  6.     private Service1 service1;
  7.     @Autowired
  8.     public void injectService1(Service1 service1) {  //@1
  9.         System.out. println(this.getClass().getName() +  ".injectService1()");
  10.         this.service1 = service1;
  11.     }
  12.     @Override
  13.     public String toString() {
  14.          return  "Service2{" +
  15.                  "service1=" + service1 +
  16.                  '}';
  17.     }
  18. }

@1:方法上标注了@Autowired,spring容器会调用这个方法,从容器中查找Service1类型的bean,然后注入。

来个总的配置文件


   
  1. package com.javacode2018.lesson001.demo26.test1;
  2. import org.springframework.context.annotation.ComponentScan;
  3. @ComponentScan
  4. public class MainConfig1 {
  5. }

来个测试用例

InjectTest中加个方法


   
  1. @Test
  2. public void test1() {
  3.     AnnotationConfigApplicationContext context =  new AnnotationConfigApplicationContext(MainConfig1.class);
  4.      for (String beanName : context.getBeanDefinitionNames()) {
  5.         System.out. println(String.format( "%s->%s", beanName, context.getBean(beanName)));
  6.     }
  7. }

运行输出


   
  1. com.javacode2018.lesson001.demo26.test1.Service2.injectService1()
  2. service1->com.javacode2018.lesson001.demo26.test1.Service1@ 9597028
  3. service2->Service2{service1=com.javacode2018.lesson001.demo26.test1.Service1@ 9597028}

通过injectService1方法成功注入service1

案例3:@Autowired标注在setter方法上,通过setter方法注入

上面2种通过构造器,和通过普通的一个方法注入,不是很常见,可以将@Autowired标注在set方法上面,来注入指定的对象

Service1


   
  1. package com.javacode2018.lesson001.demo26.test2;
  2. import org.springframework.stereotype.Component;
  3. @Component
  4. public class Service1 {
  5. }

Service2


   
  1. package com.javacode2018.lesson001.demo26.test2;
  2. import org.springframework.beans.factory.annotation.Autowired;
  3. import org.springframework.stereotype.Component;
  4. @Component
  5. public class Service2 {
  6.     private Service1 service1;
  7.     @Autowired
  8.     public void setService1(Service1 service1) {  //@1
  9.         System.out. println(this.getClass().getName() +  ".setService1方法");
  10.         this.service1 = service1;
  11.     }
  12.     @Override
  13.     public String toString() {
  14.          return  "Service2{" +
  15.                  "service1=" + service1 +
  16.                  '}';
  17.     }
  18. }

@1:标准的set方法,方法上使用了 @Autowired,会通过这个方法注入Service1类型的bean对象。

来个总的配置文件


   
  1. package com.javacode2018.lesson001.demo26.test2;
  2. import org.springframework.context.annotation.ComponentScan;
  3. @ComponentScan
  4. public class MainConfig2 {
  5. }

来个测试用例


   
  1. @Test
  2. public void test2() {
  3.     AnnotationConfigApplicationContext context =  new AnnotationConfigApplicationContext(MainConfig2.class);
  4.      for (String beanName : context.getBeanDefinitionNames()) {
  5.         System.out. println(String.format( "%s->%s", beanName, context.getBean(beanName)));
  6.     }
  7. }

运行输出


   
  1. com.javacode2018.lesson001.demo26.test2.Service2.setService1方法
  2. service1->com.javacode2018.lesson001.demo26.test2.Service1@ 6069db50
  3. service2->Service2{service1=com.javacode2018.lesson001.demo26.test2.Service1@ 6069db50}

案例4:@Autowired标注在方法参数上

Service1


   
  1. package com.javacode2018.lesson001.demo26.test3;
  2. import org.springframework.stereotype.Component;
  3. @Component
  4. public class Service1 {
  5. }

Service2


   
  1. package com.javacode2018.lesson001.demo26.test3;
  2. import org.springframework.beans.factory.annotation.Autowired;
  3. import org.springframework.stereotype.Component;
  4. @Component
  5. public class Service2 {
  6.     private Service1 service1;
  7.     @Autowired
  8.     public void injectService1(Service1 service1, String name) {  //@1
  9.         System.out. println(String.format( "%s.injectService1(),{service1=%s,name=%s}", this.getClass().getName(), service1, name));
  10.         this.service1 = service1;
  11.     }
  12.     @Override
  13.     public String toString() {
  14.          return  "Service2{" +
  15.                  "service1=" + service1 +
  16.                  '}';
  17.     }
  18. }

@1:方法上标注了@Autowired,表示会将这个方法作为注入方法,这个方法有2个参数,spring查找这2个参数对应的bean,然后注入。

第一个参数对应的bean是存在的,第二个是一个String类型的,我们并没有定义String类型bean,一会看看效果

来个总的配置文件


   
  1. package com.javacode2018.lesson001.demo26.test3;
  2. import org.springframework.context.annotation.ComponentScan;
  3. @ComponentScan
  4. public class MainConfig3 {
  5. }

来个测试用例


   
  1. @Test
  2. public void test3() {
  3.     AnnotationConfigApplicationContext context =  new AnnotationConfigApplicationContext(MainConfig3.class);
  4.      for (String beanName : context.getBeanDefinitionNames()) {
  5.         System.out. println(String.format( "%s->%s", beanName, context.getBean(beanName)));
  6.     }
  7. }

运行输出

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'service2': Unsatisfied dependency expressed through method 'injectService1' parameter 1; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'java.lang.String' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}

报错了,从错误信息中可以看出,通过injectService1方法注入的时候,第二个参数为String类型,spring从容器中没有找到String类型的候选bean,所以报错了。

我们可以这么做

多个参数的时候,方法上面的@Autowire默认对方法中所有参数起效,如果我们想对某个参数进行特定的配置,可以在参数上加上@Autowired,这个配置会覆盖方法上面的@Autowired配置。

在第二个参数上面加上@Autowired,设置required为false:表示这个bean不是强制注入的,能找到就注入,找不到就注入一个null对象,调整一下代码,如下:


   
  1. @Autowired
  2. public void injectService1(Service1 service1, @Autowired(required =  false) String name) {  //@1
  3.     System.out. println(String.format( "%s.injectService1(),{service1=%s,name=%s}", this.getClass().getName(), service1, name));
  4.     this.service1 = service1;
  5. }

此时方法的第一个参数被方法上面的@Autowired约束

第二个参数受@Autowired(required = false)约束

再次运行输出


   
  1. com.javacode2018.lesson001.demo26.test3.Service2.injectService1(),{service1=com.javacode2018.lesson001.demo26.test3.Service1@ 59309333,name=null}
  2. service1->com.javacode2018.lesson001.demo26.test3.Service1@ 59309333
  3. service2->Service2{service1=com.javacode2018.lesson001.demo26.test3.Service1@ 59309333}

注入成功了,service1有值,name为null

案例5:@Autowired用在字段上

Service1


   
  1. package com.javacode2018.lesson001.demo26.test4;
  2. import org.springframework.stereotype.Component;
  3. @Component
  4. public class Service1 {
  5. }

Service2


   
  1. package com.javacode2018.lesson001.demo26.test4;
  2. import org.springframework.stereotype.Component;
  3. @Component
  4. public class Service2 {
  5. }

Service3


   
  1. package com.javacode2018.lesson001.demo26.test4;
  2. import org.springframework.beans.factory.annotation.Autowired;
  3. import org.springframework.stereotype.Component;
  4. @Component
  5. public class Service3 {
  6.     @Autowired
  7.     private Service1 service1; //@1
  8.     @Autowired
  9.     private Service2 service2; //@2
  10.     @Override
  11.     public String toString() {
  12.          return  "Service3{" +
  13.                  "service1=" + service1 +
  14.                  ", service2=" + service2 +
  15.                  '}';
  16.     }
  17. }

@1和@2:定义了2个字段,上面都标注了@Autowired,spring会去容器中按照类型查找这2种类型的bean,然后设置给这2个属性。

来个总的配置文件


   
  1. package com.javacode2018.lesson001.demo26.test4;
  2. import org.springframework.context.annotation.ComponentScan;
  3. @ComponentScan
  4. public class MainConfig4 {
  5. }

来个测试用例


   
  1. @Test
  2. public void test4() {
  3.     AnnotationConfigApplicationContext context =  new AnnotationConfigApplicationContext(MainConfig4.class);
  4.      for (String beanName : context.getBeanDefinitionNames()) {
  5.         System.out. println(String.format( "%s->%s", beanName, context.getBean(beanName)));
  6.     }
  7. }

运行输出


   
  1. service1->com.javacode2018.lesson001.demo26.test4.Service1@ 7e07db1f
  2. service2->com.javacode2018.lesson001.demo26.test4.Service2@ 1189dd52
  3. service3->Service3{service1=com.javacode2018.lesson001.demo26.test4.Service1@ 7e07db1f, service2=com.javacode2018.lesson001.demo26.test4.Service2@ 1189dd52}

service3中标注@Autowired的2个属性都有值了,都被注入成功了。

案例6:@Autowire标注字段,多个候选者的时候,按字段名称注入

IService接口


   
  1. package com.javacode2018.lesson001.demo26.test5;
  2. public  interface IService {
  3. }

接口来2个实现

2个实现类上都标注了@Component注解,都会被注册到容器中。

Service0

   
  1. package com.javacode2018.lesson001.demo26.test5;
  2. import org.springframework.stereotype.Component;
  3. @Component
  4. public class Service0 implements IService {
  5. }
Service1

   
  1. package com.javacode2018.lesson001.demo26.test5;
  2. import org.springframework.stereotype.Component;
  3. @Component
  4. public class Service1 implements IService {
  5. }

来个Service2


   
  1. package com.javacode2018.lesson001.demo26.test5;
  2. import org.springframework.beans.factory.annotation.Autowired;
  3. import org.springframework.stereotype.Component;
  4. @Component
  5. public class Service2 {
  6.     @Autowired
  7.     private IService service1;  //@1
  8.     @Override
  9.     public String toString() {
  10.          return  "Service2{" +
  11.                  "service1=" + service1 +
  12.                  '}';
  13.     }
  14. }

@1:标注了@Autowired注解,需要注入类型为IService类型的bean,满足这种类型的有2个:service0和service1

按照上面介绍的候选者查找过程,最后会注入和字段名称一样的bean,即:service1

来个总的配置类,负责扫描当前包中的组件


   
  1. package com.javacode2018.lesson001.demo26.test5;
  2. import org.springframework.context.annotation.ComponentScan;
  3. @ComponentScan
  4. public class MainConfig5 {
  5. }

来个测试用例


   
  1. @Test
  2. public void test5() {
  3.     AnnotationConfigApplicationContext context =  new AnnotationConfigApplicationContext(MainConfig5.class);
  4.      for (String beanName : context.getBeanDefinitionNames()) {
  5.         System.out. println(String.format( "%s->%s", beanName, context.getBean(beanName)));
  6.     }
  7. }

运行输出


   
  1. service0->com.javacode2018.lesson001.demo26.test5.Service0@ 36902638
  2. service1->com.javacode2018.lesson001.demo26.test5.Service1@ 223d2c72
  3. service2->Service2{service1=com.javacode2018.lesson001.demo26.test5.Service1@ 223d2c72}

注意最后一行,service2中的service1被注入了bean:service1

案例7:将指定类型的所有bean,注入到Collection、Map中

注入到Collection中

被注入的类型为Collection类型或者Collection子接口类型,注意必须是接口类型,如:


   
  1. Collection<IService>
  2. List<IService>
  3. Set<IService>

会在容器中找到所有IService类型的bean,放到这个集合中

注入到Map中

被注入的类型为Map类型或者Map子接口类型,注意必须是接口类型,如:

Map<String,IService>

会在容器中找到所有IService类型的bean,放到这个Map中,key为bean的名称,value为bean对象

来看案例代码。

来个接口


   
  1. package com.javacode2018.lesson001.demo26.test6;
  2. public  interface IService {
  3. }

来2个实现类,标注@Component注解

Service0

   
  1. package com.javacode2018.lesson001.demo26.test6;
  2. import org.springframework.stereotype.Component;
  3. @Component
  4. public class Service0 implements IService {
  5. }
Service1

   
  1. package com.javacode2018.lesson001.demo26.test6;
  2. import org.springframework.stereotype.Component;
  3. @Component
  4. public class Service1 implements IService {
  5. }

再来个类Service2


   
  1. package com.javacode2018.lesson001.demo26.test6;
  2. import org.springframework.beans.factory.annotation.Autowired;
  3. import org.springframework.stereotype.Component;
  4. import java.util.List;
  5. import java.util.Map;
  6. @Component
  7. public class Service2 {
  8.     @Autowired
  9.     private List<IService> services;
  10.     @Autowired
  11.     private Map<String, IService> serviceMap;
  12.     @Override
  13.     public String toString() {
  14.          return  "Service2{\n" +
  15.                  "services=" + services +
  16.                  ", \n serviceMap=" + serviceMap +
  17.                  '}';
  18.     }
  19. }

@1:注入IService类型的所有bean

@2:注入一个map

来个总的配置类


   
  1. package com.javacode2018.lesson001.demo26.test6;
  2. import org.springframework.context.annotation.ComponentScan;
  3. @ComponentScan
  4. public class MainConfig6 {
  5. }

来个测试用例


   
  1. @Test
  2. public void test6() {
  3.     AnnotationConfigApplicationContext context =  new AnnotationConfigApplicationContext(MainConfig6.class);
  4.      for (String beanName : context.getBeanDefinitionNames()) {
  5.         System.out. println(String.format( "%s->%s", beanName, context.getBean(beanName)));
  6.     }
  7. }

运行输出


   
  1. service0->com.javacode2018.lesson001.demo26.test6.Service0@ 1189dd52
  2. service1->com.javacode2018.lesson001.demo26.test6.Service1@ 36bc55de
  3. service2->Service2{
  4. services=[com.javacode2018.lesson001.demo26.test6.Service0@ 1189dd52, com.javacode2018.lesson001.demo26.test6.Service1@ 36bc55de], 
  5.  serviceMap={service0=com.javacode2018.lesson001.demo26.test6.Service0@ 1189dd52, service1=com.javacode2018.lesson001.demo26.test6.Service1@ 36bc55de}}

注意看一下上面services和serviceMap的值。

@Autowired源码

spring使用下面这个类处理@Autowired注解

org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor

@Resource:注意依赖对象

作用

和@Autowired注解类似,也是用来注入依赖的对象的,spring容器会对bean中所有字段、方法进行遍历,标注有@Resource注解的,都会进行注入。

看一下这个注解定义:


   
  1. javax.annotation.Resource
  2. @Target({TYPE, FIELD, METHOD})
  3. @Retention(RUNTIME)
  4. public @ interface Resource {
  5.     String name()  default  "";
  6.     ..其他不常用的参数省略
  7. }

这个注解是javax中定义的,并不是spring中定义的注解。

从定义上可以见,这个注解可以用在任何类型上面、字段、方法上面。

注意点:

用在方法上的时候,方法参数只能有一个。

@Resource查找候选者的过程

查找过程有点复杂,看不懂的可以先跳过,先看后面案例,本文看完之后,可以回头再来看这个过程。

@Resource标注在字段上面:假定字段类型为一个自定义的普通的类型,候选者查找过程如下

@Autowired标注在方法上或者方法参数上面:假定参数类型为为一个自定义的普通的类型,候选者查找过程如下:

将指定类型的所有bean注入到Collection中

如果被注入的对象是Collection类型的,可以指定泛型的类型,然后会按照上面的方式查找所有满足泛型类型所有的bean

将指定类型的所有bean注入到Map中

如果被注入的对象是Map类型的,可以指定泛型的类型,key通常为String类型,value为需要查找的bean的类型,然后会按照上面方式查找所有注入value类型的bean,将bean的name作为key,bean对象作为value,放在HashMap中,然后注入。

@Resource查找候选者可以简化为

先按Resource的name值作为bean名称找->按名称(字段名称、方法名称、set属性名称)找->按类型找->通过限定符@Qualifier过滤->@Primary->@Priority->根据名称找(字段名称或者方法参数名称)

概括为:先按名称找,然后按类型找

案例1:将@Resource标注在字段上

IService接口


   
  1. package com.javacode2018.lesson001.demo26.test7;
  2. public  interface IService {
  3. }

2个实现类

Service0

   
  1. package com.javacode2018.lesson001.demo26.test7;
  2. import org.springframework.stereotype.Component;
  3. @Component
  4. public class Service0 implements IService {
  5. }

@Component标注的bean名称默认为service0

Service1

   
  1. package com.javacode2018.lesson001.demo26.test7;
  2. import org.springframework.stereotype.Component;
  3. @Component
  4. public class Service1 implements IService {
  5. }

@Component标注的bean名称默认为service1

再来一个类


   
  1. package com.javacode2018.lesson001.demo26.test7;
  2. import org.springframework.beans.factory.annotation.Autowired;
  3. import org.springframework.stereotype.Component;
  4. import javax.annotation.Resource;
  5. import java.util.List;
  6. import java.util.Map;
  7. @Component
  8. public class Service2 {
  9.     @Resource
  10.     private IService service1; //@1
  11.     @Override
  12.     public String toString() {
  13.          return  "Service2{" +
  14.                  "service1=" + service1 +
  15.                  '}';
  16.     }
  17. }

@1:字段名称为service1,按照字段名称查找bean,会找到Service1

来个配置类


   
  1. package com.javacode2018.lesson001.demo26.test7;
  2. import org.springframework.context.annotation.ComponentScan;
  3. @ComponentScan
  4. public class MainConfig7 {
  5. }

测试用例


   
  1. @Test
  2. public void test7() {
  3.     AnnotationConfigApplicationContext context =  new AnnotationConfigApplicationContext(MainConfig7.class);
  4.      for (String beanName : context.getBeanDefinitionNames()) {
  5.         System.out. println(String.format( "%s->%s", beanName, context.getBean(beanName)));
  6.     }
  7. }

运行输出


   
  1. service0->com.javacode2018.lesson001.demo26.test7.Service0@ 222545dc
  2. service1->com.javacode2018.lesson001.demo26.test7.Service1@ 5c5eefef
  3. service2->Service2{service1=com.javacode2018.lesson001.demo26.test7.Service1@ 5c5eefef}

最后一行可以看出注入了service1

如果将Service2中的代码调整一下


   
  1. @Resource
  2. private IService service0;

此时会注入service0这个bean

同样@Resource可以用在方法上,也可以将所有类型的bean注入到Collection、Map中,这里就不演示了,重点了解一下候选者查找的过程,使用上就比较简单了,@Resource的其他案例,大家可以自己写写练练。

下面来说另外几个注解,也是比较重要的。

@Resource源码

spring使用下面这个类处理@Resource注解

org.springframework.context.annotation.CommonAnnotationBeanPostProcessor

@Qualifier:限定符

作用

这个单词的意思是:限定符。

可以在依赖注入查找候选者的过程中对候选者进行过滤。

看一下其定义:


   
  1. @Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Inherited
  4. @Documented
  5. public @ interface Qualifier {
  6.     String value()  default  "";
  7. }

可以用在字段、方法、参数、任意类型、注解上面

有一个参数value

还是来看案例,通过案例理解更容易。

案例1:用在类上

用在类上,你可以理解为给通过@Qulifier给这个bean打了一个标签。

先来一个接口


   
  1. package com.javacode2018.lesson001.demo26.test8;
  2. public  interface IService {
  3. }

来3个实现类

前2个@Qulifier的value为tag1,第3个实现类为tag2

Service1


   
  1. package com.javacode2018.lesson001.demo26.test8;
  2. import org.springframework.beans.factory.annotation.Qualifier;
  3. import org.springframework.stereotype.Component;
  4. @Component
  5. @Qualifier( "tag1"//@1
  6. public class Service1 implements IService {
  7. }

@1:tag1

Service2


   
  1. package com.javacode2018.lesson001.demo26.test8;
  2. import org.springframework.beans.factory.annotation.Qualifier;
  3. import org.springframework.stereotype.Component;
  4. @Component
  5. @Qualifier( "tag1")
  6. public class Service2 implements IService {
  7. }

@1:tag1

Service3


   
  1. package com.javacode2018.lesson001.demo26.test8;
  2. import org.springframework.beans.factory.annotation.Qualifier;
  3. import org.springframework.stereotype.Component;
  4. @Component
  5. @Qualifier( "tag2") //@1
  6. public class Service3 implements IService {
  7. }

@1:tag2

来一个类,来注入上面几个bean


   
  1. package com.javacode2018.lesson001.demo26.test8;
  2. import org.springframework.beans.factory.annotation.Autowired;
  3. import org.springframework.beans.factory.annotation.Qualifier;
  4. import org.springframework.stereotype.Component;
  5. import java.util.Map;
  6. @Component
  7. public class InjectService {
  8.     @Autowired
  9.     @Qualifier( "tag1"//@1
  10.     private Map<String, IService> serviceMap1;
  11.     @Autowired
  12.     @Qualifier( "tag2"//@2
  13.     private Map<String, IService> serviceMap2;
  14.     @Override
  15.     public String toString() {
  16.          return  "InjectService{" +
  17.                  "serviceMap1=" + serviceMap1 +
  18.                  ", serviceMap2=" + serviceMap2 +
  19.                  '}';
  20.     }
  21. }

@1:限定符的值为tag1,此时会将类上限定符为tag1的所有bean注入进来

@2:限定符的值为tag2,此时会将类上限定符为tag2的所有bean注入进来

来个配置类


   
  1. package com.javacode2018.lesson001.demo26.test8;
  2. import org.springframework.context.annotation.ComponentScan;
  3. @ComponentScan
  4. public class MainConfig8 {
  5. }

测试用例


   
  1. @Test
  2. public void test8() {
  3.     AnnotationConfigApplicationContext context =  new AnnotationConfigApplicationContext(MainConfig8.class);
  4.      for (String beanName : context.getBeanDefinitionNames()) {
  5.         System.out. println(String.format( "%s->%s", beanName, context.getBean(beanName)));
  6.     }
  7. }

运行输出


   
  1. injectService->InjectService{serviceMap1={service1=com.javacode2018.lesson001.demo26.test8.Service1@ 9597028, service2=com.javacode2018.lesson001.demo26.test8.Service2@ 6069db50}, serviceMap2={service3=com.javacode2018.lesson001.demo26.test8.Service3@ 4efbca5a}}
  2. service1->com.javacode2018.lesson001.demo26.test8.Service1@ 9597028
  3. service2->com.javacode2018.lesson001.demo26.test8.Service2@ 6069db50
  4. service3->com.javacode2018.lesson001.demo26.test8.Service3@ 4efbca5a

注意第一行的输出,看一下serviceMap1和serviceMap2的值。

serviceMap1注入了@Qulifier的value为tag1的所有IService类型的bean

serviceMap1注入了@Qulifier的value为tag2的所有IService类型的bean

实现了bean分组的效果。

案例2:@Autowired结合@Qulifier指定注入的bean

被注入的类型有多个的时候,可以使用@Qulifier来指定需要注入那个bean,将@Qulifier的value设置为需要注入bean的名称

看案例代码

来个接口


   
  1. package com.javacode2018.lesson001.demo26.test9;
  2. public  interface IService {
  3. }

有2个实现类

2个实现类上面没有使用@Qulifier注解了

Service1

   
  1. package com.javacode2018.lesson001.demo26.test9;
  2. import org.springframework.stereotype.Component;
  3. @Component
  4. public class Service1 implements IService {
  5. }
Service2

   
  1. package com.javacode2018.lesson001.demo26.test9;
  2. import org.springframework.stereotype.Component;
  3. @Component
  4. public class Service2 implements IService {
  5. }

我们可以知道上面2个bean的名称分别为:service1、service2

来个类,注入IService类型的bean


   
  1. package com.javacode2018.lesson001.demo26.test9;
  2. import org.springframework.beans.factory.annotation.Autowired;
  3. import org.springframework.beans.factory.annotation.Qualifier;
  4. import org.springframework.stereotype.Component;
  5. @Component
  6. public class InjectService {
  7.     @Autowired
  8.     @Qualifier( "service2"//@1
  9.     private IService service;
  10.     @Override
  11.     public String toString() {
  12.          return  "InjectService{" +
  13.                  "service=" + service +
  14.                  '}';
  15.     }
  16. }

@1:这里限定符的值为service2,容器中IService类型的bean有2个[service1和service2],当类上没有标注@Qualifier注解的时候,可以理解为:bean的名称就是限定符的值,所以@1这里会匹配到service2

来个配置类


   
  1. package com.javacode2018.lesson001.demo26.test9;
  2. import org.springframework.context.annotation.ComponentScan;
  3. import org.springframework.stereotype.Component;
  4. @ComponentScan
  5. public class MainConfig9 {
  6. }

来个测试用例


   
  1. @Test
  2. public void test9() {
  3.     AnnotationConfigApplicationContext context =  new AnnotationConfigApplicationContext(MainConfig9.class);
  4.      for (String beanName : context.getBeanDefinitionNames()) {
  5.         System.out. println(String.format( "%s->%s", beanName, context.getBean(beanName)));
  6.     }
  7. }

运行输出


   
  1. injectService->InjectService{service=com.javacode2018.lesson001.demo26.test9.Service2@ 223d2c72}
  2. service1->com.javacode2018.lesson001.demo26.test9.Service1@ 8f4ea7c
  3. service2->com.javacode2018.lesson001.demo26.test9.Service2@ 223d2c72

从第一行可以看出注入了service1

案例3:用在方法参数上

代码


   
  1. package com.javacode2018.lesson001.demo26.test10;
  2. import org.springframework.beans.factory.annotation.Autowired;
  3. import org.springframework.beans.factory.annotation.Qualifier;
  4. import org.springframework.stereotype.Component;
  5. @Component
  6. public class InjectService {
  7.     private IService s1;
  8.     private IService s2;
  9.     @Autowired
  10.     public void injectBean(@Qualifier( "service2") IService s1, @Qualifier( "service1") IService s2) {  //@1
  11.         this.s1 = s1;
  12.         this.s2 = s2;
  13.     }
  14.     @Override
  15.     public String toString() {
  16.          return  "InjectService{" +
  17.                  "s1=" + s1 +
  18.                  ", s2=" + s2 +
  19.                  '}';
  20.     }
  21. }

@1:方法上标注了@Autowired注解,说明会被注入依赖,2个参数上分别使用了限定符来指定具体需要注入哪个bean

测试用例


   
  1. @Test
  2. public void test10() {
  3.     AnnotationConfigApplicationContext context =  new AnnotationConfigApplicationContext(MainConfig10.class);
  4.      for (String beanName : context.getBeanDefinitionNames()) {
  5.         System.out. println(String.format( "%s->%s", beanName, context.getBean(beanName)));
  6.     }
  7. }

运行输出


   
  1. injectService->InjectService{s1=com.javacode2018.lesson001.demo26.test10.Service2@ 55183b20, s2=com.javacode2018.lesson001.demo26.test10.Service1@ 4f83df68}
  2. service1->com.javacode2018.lesson001.demo26.test10.Service1@ 4f83df68
  3. service2->com.javacode2018.lesson001.demo26.test10.Service2@ 55183b20

第一行中的

s1:service2

s2:service1

案例4:用在setter方法上

不管是用在setter方法还是普通方法上面,都是一样的效果

代码


   
  1. package com.javacode2018.lesson001.demo26.test11;
  2. import org.springframework.beans.factory.annotation.Autowired;
  3. import org.springframework.beans.factory.annotation.Qualifier;
  4. import org.springframework.stereotype.Component;
  5. @Component
  6. public class InjectService {
  7.     private IService s1;
  8.     private IService s2;
  9.     @Autowired
  10.     @Qualifier( "service2")
  11.     public void setS1(IService s1) {
  12.         this.s1 = s1;
  13.     }
  14.     @Autowired
  15.     @Qualifier( "service2")
  16.     public void setS2(IService s2) {
  17.         this.s2 = s2;
  18.     }
  19.     @Override
  20.     public String toString() {
  21.          return  "InjectService{" +
  22.                  "s1=" + s1 +
  23.                  ", s2=" + s2 +
  24.                  '}';
  25.     }
  26. }

上面2个setter方法上都有@Autowired注解,并且结合了@Qulifier注解来限定需要注入哪个bean

测试用例


   
  1. @Test
  2. public void test11() {
  3.     AnnotationConfigApplicationContext context =  new AnnotationConfigApplicationContext(MainConfig11.class);
  4.      for (String beanName : context.getBeanDefinitionNames()) {
  5.         System.out. println(String.format( "%s->%s", beanName, context.getBean(beanName)));
  6.     }
  7. }

运行输出


   
  1. injectService->InjectService{s1=com.javacode2018.lesson001.demo26.test11.Service2@ 35e2d654, s2=com.javacode2018.lesson001.demo26.test11.Service2@ 35e2d654}
  2. service1->com.javacode2018.lesson001.demo26.test11.Service1@ 1bd4fdd
  3. service2->com.javacode2018.lesson001.demo26.test11.Service2@ 35e2d654

输出中可以看出:s1为service2,s2为service1

@Primary:设置为主要候选者

注入依赖的过程中,当有多个候选者的时候,可以指定哪个候选者为主要的候选者。

看一下其定义


   
  1. @Target({ElementType.TYPE, ElementType.METHOD})
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. public @ interface Primary {
  5. }

可以用在类上或者方法上面。

通常定义bean常见的有2种方式:

方式1:在类上标注@Component注解,此时可以配合@Primary,标注这个bean为主要候选者

方式2:在配置文件中使用@Bean注解标注方法,来注册bean,可以在@Bean标注的方法上加上@Primary,标注这个bean为主要候选bean。

看案例。

案例1:用在类上

来个接口


   
  1. package com.javacode2018.lesson001.demo26.test12;
  2. public  interface IService {
  3. }

2个实现类

Service1

   
  1. package com.javacode2018.lesson001.demo26.test12;
  2. import org.springframework.stereotype.Component;
  3. @Component
  4. public class Service1 implements IService {
  5. }
Service2

   
  1. package com.javacode2018.lesson001.demo26.test12;
  2. import org.springframework.context.annotation.Primary;
  3. import org.springframework.stereotype.Component;
  4. @Component
  5. @Primary
  6. public class Service2 implements IService {
  7. }

Service2上面使用了@Primary,表示这是个主要的候选者

再来个类,注入IService类型的bean


   
  1. package com.javacode2018.lesson001.demo26.test12;
  2. import org.springframework.beans.factory.annotation.Autowired;
  3. import org.springframework.stereotype.Component;
  4. @Component
  5. public class InjectService {
  6.     @Autowired
  7.     private IService service1;  //@1
  8.     @Override
  9.     public String toString() {
  10.          return  "InjectService{" +
  11.                  "service1=" + service1 +
  12.                  '}';
  13.     }
  14. }

@1:容器中IService类型的bean有2个,但是service2为主要的候选者,所以此处会注入service2

总的配置类


   
  1. package com.javacode2018.lesson001.demo26.test12;
  2. import org.springframework.context.annotation.ComponentScan;
  3. @ComponentScan
  4. public class MainConfig12 {
  5. }

测试用例


   
  1. @Test
  2. public void test12() {
  3.     AnnotationConfigApplicationContext context =  new AnnotationConfigApplicationContext(MainConfig12.class);
  4.      for (String beanName : context.getBeanDefinitionNames()) {
  5.         System.out. println(String.format( "%s->%s", beanName, context.getBean(beanName)));
  6.     }
  7. }

运行输出


   
  1. injectService->InjectService{service1=com.javacode2018.lesson001.demo26.test12.Service2@ 49ec71f8}
  2. service1->com.javacode2018.lesson001.demo26.test12.Service1@ 1d2adfbe
  3. service2->com.javacode2018.lesson001.demo26.test12.Service2@ 49ec71f8

案例2:用在方法上,结合@Bean使用

来个接口


   
  1. package com.javacode2018.lesson001.demo26.test13;
  2. public  interface IService {
  3. }

2个实现类

Service1

   
  1. package com.javacode2018.lesson001.demo26.test13;
  2. public class Service1 implements IService {
  3. }

Service2


   
  1. package com.javacode2018.lesson001.demo26.test13;
  2. public class Service2 implements IService {
  3. }

InjectService


   
  1. package com.javacode2018.lesson001.demo26.test13;
  2. import org.springframework.beans.factory.annotation.Autowired;
  3. public class InjectService {
  4.     @Autowired
  5.     private IService service1; //@1
  6.     @Override
  7.     public String toString() {
  8.          return  "InjectService{" +
  9.                  "service1=" + service1 +
  10.                  '}';
  11.     }
  12. }

使用了@Autowired,需要注入

来个配置类,通过@Bean定义上面3个类型的bean


   
  1. package com.javacode2018.lesson001.demo26.test13;
  2. import org.springframework.context.annotation.Bean;
  3. import org.springframework.context.annotation.Configuration;
  4. import org.springframework.context.annotation.Primary;
  5. @Configuration
  6. public class MainConfig13 {
  7.     @Bean
  8.     public IService service1() {
  9.          return  new Service1();
  10.     }
  11.     @Bean
  12.     @Primary  //@1
  13.     public IService service2() {
  14.          return  new Service2();
  15.     }
  16.     @Bean
  17.     public InjectService injectService() {
  18.          return  new InjectService();
  19.     }
  20. }

上面是一个配置类,定义了3个bean

@1:这个bean被标注为主要的候选者

来个测试用例


   
  1. @Test
  2. public void test13() {
  3.     AnnotationConfigApplicationContext context =  new AnnotationConfigApplicationContext(MainConfig13.class);
  4.      for (String beanName : context.getBeanDefinitionNames()) {
  5.         System.out. println(String.format( "%s->%s", beanName, context.getBean(beanName)));
  6.     }
  7. }

运行输出


   
  1. service1->com.javacode2018.lesson001.demo26.test13.Service1@ 6913c1fb
  2. service2->com.javacode2018.lesson001.demo26.test13.Service2@ 66d18979
  3. injectService->InjectService{service1=com.javacode2018.lesson001.demo26.test13.Service2@ 66d18979}

注意最后一行,service1注入的是service2这个bean

@Bean定义bean时注入依赖的几种方式

常见3种方式

  1. 硬编码方式

  2. @Autowired、@Resource的方式

  3. @Bean标注的方法参数的方式

方式1:硬编码方式

来3个类

Service1


   
  1. package com.javacode2018.lesson001.demo26.test14;
  2. public class Service1 {
  3. }

Service2


   
  1. package com.javacode2018.lesson001.demo26.test14;
  2. public class Service2 {
  3. }

Service3


   
  1. package com.javacode2018.lesson001.demo26.test14;
  2. public class Service3 {
  3.     private Service1 service1;
  4.     private Service2 service2;
  5.     public Service1 getService1() {
  6.          return service1;
  7.     }
  8.     public void setService1(Service1 service1) {
  9.         this.service1 = service1;
  10.     }
  11.     public Service2 getService2() {
  12.          return service2;
  13.     }
  14.     public void setService2(Service2 service2) {
  15.         this.service2 = service2;
  16.     }
  17.     @Override
  18.     public String toString() {
  19.          return  "Service3{" +
  20.                  "service1=" + service1 +
  21.                  ", service2=" + service2 +
  22.                  '}';
  23.     }
  24. }

上面类中会用到service1和service2,提供了对应的setter方法,一会我们通过setter方法注入依赖对象

来个配置类,通过@Bean的方式创建上面对象


   
  1. package com.javacode2018.lesson001.demo26.test14;
  2. import org.springframework.context.annotation.Bean;
  3. import org.springframework.context.annotation.Configuration;
  4. @Configuration
  5. public class MainConfig14 {
  6.     @Bean
  7.     public Service1 service1() {
  8.          return  new Service1();
  9.     }
  10.     @Bean
  11.     public Service2 service2() {
  12.          return  new Service2();
  13.     }
  14.     @Bean
  15.     public Service3 service3() {
  16.         Service3 service3 =  new Service3();  //@0
  17.         service3.setService1(this.service1());  //@1
  18.         service3.setService2(this.service2());  //@2
  19.          return service3;
  20.     }
  21. }

上面代码中通过@Bean定义了3个bean

Service3中需要用到Service1和Service2,注意@1和@2直接调用当前方法获取另外2个bean,注入到service3中

测试用例


   
  1. @Test
  2. public void test14() {
  3.     AnnotationConfigApplicationContext context =  new AnnotationConfigApplicationContext(MainConfig14.class);
  4.      for (String beanName : context.getBeanDefinitionNames()) {
  5.         System.out. println(String.format( "%s->%s", beanName, context.getBean(beanName)));
  6.     }
  7. }

运行输出


   
  1. service1->com.javacode2018.lesson001.demo26.test14.Service1@ 41a2befb
  2. service2->com.javacode2018.lesson001.demo26.test14.Service2@ 6c40365c
  3. service3->Service3{service1=com.javacode2018.lesson001.demo26.test14.Service1@ 41a2befb, service2=com.javacode2018.lesson001.demo26.test14.Service2@ 6c40365c}

方式2:@Autowired、@Resource的方式

这种方式就不讲了直接在需要注入的对象上面加上这2个注解的任意一个就行了,可以参考文章前面的部分。

方式3:@Bean标注的方法使用参数来进行注入


   
  1. package com.javacode2018.lesson001.demo26.test15;
  2. import com.javacode2018.lesson001.demo26.test14.Service1;
  3. import com.javacode2018.lesson001.demo26.test14.Service2;
  4. import com.javacode2018.lesson001.demo26.test14.Service3;
  5. import org.springframework.context.annotation.Bean;
  6. import org.springframework.context.annotation.Configuration;
  7. @Configuration
  8. public class MainConfig15 {
  9.     @Bean
  10.     public Service1 service1() {
  11.          return  new Service1();
  12.     }
  13.     @Bean
  14.     public Service2 service2() {
  15.          return  new Service2();
  16.     }
  17.     @Bean
  18.     public Service3 service3(Service1 s1, Service2 s2) {  //@0
  19.         Service3 service3 =  new Service3();
  20.         service3.setService1(s1);  //@1
  21.         service3.setService2(s2);  //@2
  22.          return service3;
  23.     }
  24. }

@0:这个地方是关键,方法上标注了@Bean,并且方法中是有参数的,spring调用这个方法创建bean的时候,会将参数中的两个参数注入进来。

注入对象的查找逻辑可以参考上面@Autowired标注方法时查找候选者的逻辑。

来个测试用例


   
  1. @Test
  2. public void test15() {
  3.     AnnotationConfigApplicationContext context =  new AnnotationConfigApplicationContext(MainConfig15.class);
  4.      for (String beanName : context.getBeanDefinitionNames()) {
  5.         System.out. println(String.format( "%s->%s", beanName, context.getBean(beanName)));
  6.     }
  7. }

运行输出


   
  1. service1->com.javacode2018.lesson001.demo26.test14.Service1@ 4009e306
  2. service2->com.javacode2018.lesson001.demo26.test14.Service2@ 43c1b556
  3. service3->Service3{service1=com.javacode2018.lesson001.demo26.test14.Service1@ 4009e306, service2=com.javacode2018.lesson001.demo26.test14.Service2@ 43c1b556}

同样注入成功了。

其他

@Bean标注的方法参数上使用@Autowired注解


   
  1. @Bean
  2. public Service3 service3_0(Service1 s1, @Autowired(required =  false) Service2 s2) {  //@0
  3.     Service3 service3 =  new Service3();
  4.     service3.setService1(s1);  //@1
  5.     service3.setService2(s2);  //@2
  6.      return service3;
  7. }

@0:方法由2个参数,第二个参数上标注了@Autowired(required = false),说明第二个参数候选者不是必须的,找不到会注入一个null对象;第一个参数候选者是必须的,找不到会抛出异常

@Bean结合@Qualifier


   
  1. package com.javacode2018.lesson001.demo26.test17;
  2. import org.springframework.beans.factory.annotation.Qualifier;
  3. import org.springframework.context.annotation.Bean;
  4. import org.springframework.context.annotation.Configuration;
  5. import java.util.Map;
  6. @Configuration
  7. public class MainConfig17 {
  8.     @Bean
  9.     @Qualifier( "tag1"//@1
  10.     public Service1 service1() {
  11.          return  new Service1();
  12.     }
  13.     @Bean
  14.     @Qualifier( "tag1"//@2
  15.     public Service2 service2() {
  16.          return  new Service2();
  17.     }
  18.     @Bean
  19.     @Qualifier( "tag2"//@3
  20.     public Service3 service3() {
  21.          return  new Service3();
  22.     }
  23.     @Bean
  24.     public InjectService injectService(@Qualifier( "tag1") Map<String, IService> map1) {  //@4
  25.         InjectService injectService =  new InjectService();
  26.         injectService.setServiceMap1(map1);
  27.          return injectService;
  28.     }
  29. }

Service1,Service2,Service3都实现了IService接口

@1,@2,@3这3个方法上面使用了@Bean注解,用来定义3个bean,这3个方法上还是用了@Qualifier注解,用来给这些bean定义标签,service1()方法类似于下面的写法:


   
  1. @Compontent
  2. @Qualifier( "tag1")
  3. public class Service1 implements IService{
  4. }

再回到MainConfig17中的@4:参数中需要注入Map<String, IService>,会查找IService类型的bean,容器中有3个,但是这个参数前面加上了@Qualifier限定符,值为tag1,所以会通过这个过滤,最后满足的候选者为:[service1,service]

对应测试用例


   
  1. @Test
  2. public void test17() {
  3.     AnnotationConfigApplicationContext context =  new AnnotationConfigApplicationContext(MainConfig17.class);
  4.      for (String beanName : context.getBeanDefinitionNames()) {
  5.         System.out. println(String.format( "%s->%s", beanName, context.getBean(beanName)));
  6.     }
  7. }

运行输出


   
  1. service1->com.javacode2018.lesson001.demo26.test17.Service1@ 1190200a
  2. service2->com.javacode2018.lesson001.demo26.test17.Service2@ 6a2f6f80
  3. service3->com.javacode2018.lesson001.demo26.test17.Service3@ 45b4c3a9
  4. injectService->InjectService{serviceMap1={service1=com.javacode2018.lesson001.demo26.test17.Service1@ 1190200a, service2=com.javacode2018.lesson001.demo26.test17.Service2@ 6a2f6f80}, serviceMap2=null}

注意最后一行serviceMap1,注入了service1和service2

泛型注入

先来2个普通的类

UserModel


   
  1. package com.javacode2018.lesson001.demo26.test18;
  2. public class UserModel {
  3. }

OrderModel


   
  1. package com.javacode2018.lesson001.demo26.test18;
  2. public class OrderModel {
  3. }

记住上面2个普通的类UserModel和OrderModel,一会下面会用到。

来个泛型接口


   
  1. package com.javacode2018.lesson001.demo26.test18;
  2. public  interface IDao<T> {
  3. }

上面是个泛型类,类名后面后尖括号

来2个实现类

两个实现类都会标注@Compontent,交给spring容器管理

UserDao


   
  1. package com.javacode2018.lesson001.demo26.test18;
  2. import org.springframework.stereotype.Component;
  3. @Component
  4. public class UserDao implements IDao<UserModel> {  //@1
  5. }

@1:指定了IDao后面泛型的类型为UserModel

OrderDao


   
  1. package com.javacode2018.lesson001.demo26.test18;
  2. import org.springframework.stereotype.Component;
  3. @Component
  4. public class OrderDao implements IDao<OrderModel> { //@1
  5. }

@1:指定了IDao后面泛型的类型为OrderModel

在来个泛型类型


   
  1. package com.javacode2018.lesson001.demo26.test18;
  2. import org.springframework.beans.factory.annotation.Autowired;
  3. public class BaseService<T> {
  4.     @Autowired
  5.     private IDao<T> dao;  //@1
  6.     public IDao<T> getDao() {
  7.          return dao;
  8.     }
  9.     public void setDao(IDao<T> dao) {
  10.         this.dao = dao;
  11.     }
  12. }

BaseService同样是个泛型类

@1:这个地方要注意了,上面使用了@Autowired,来注入IDao对象

BaseService来2个子类

两个子类都会标注@Compontent,交给spring容器管理

UserService


   
  1. package com.javacode2018.lesson001.demo26.test18;
  2. import org.springframework.stereotype.Component;
  3. @Component
  4. public class UserService extends BaseService<UserModel> { //@1
  5. }

@1:指定了BaseService后面泛型的类型为UserModel

OrderService


   
  1. package com.javacode2018.lesson001.demo26.test18;
  2. import org.springframework.stereotype.Component;
  3. @Component
  4. public class OrderService extends BaseService<OrderModel> { //@1
  5. }

@1:指定了BaseService后面泛型的类型为OrderModel

UserService和OrderService继承了BaseService,所以一会BaseService中的dao属性会被注入,一会我们关注一下dao这个属性的值,会是什么样的

来个总的配置类


   
  1. package com.javacode2018.lesson001.demo26.test18;
  2. import org.springframework.context.annotation.ComponentScan;
  3. @ComponentScan
  4. public class MainConfig18 {
  5. }

上面有@CompontentScan注解,会自动扫描当前包中的所有类,并进行自动注入

来个测试用例


   
  1. @Test
  2. public void test18() {
  3.     AnnotationConfigApplicationContext context =  new AnnotationConfigApplicationContext(MainConfig18.class);
  4.     System.out. println(context.getBean(UserService.class).getDao());
  5.     System.out. println(context.getBean(OrderService.class).getDao());
  6. }

上面代码中会将两个service中的dao输出,我们来看一下效果

运行输出


   
  1. com.javacode2018.lesson001.demo26.test18.UserDao@ 6adbc9d
  2. com.javacode2018.lesson001.demo26.test18.OrderDao@ 4550bb58

结果就是重点了,dao属性并没有指定具体需要注入那个bean,此时是根据尖括号中的泛型类型来匹配的,这个功能也是相当厉害的。

总结

这篇文中内容比较多,每个案例大家都要去敲一遍,不清楚的,可以留言,或者直接微信中@我

  1. 需要掌握@Autowired注解和@Resource注解中候选者查找的过程

  2. @Autowired:先通过类型找,然后通过名称找

  3. @Resource:先通过名称找,然后通过类型找

  4. @Autowired和@Resource,建议开发中使用@Autowired来实现依赖注入,spring的注解用起来更名正言顺一些

  5. @Qulifier:限定符,可以用在类上;也可以用在依赖注入的地方,可以对候选者的查找进行过滤

  6. @Primary:多个候选者的时候,可以标注某个候选者为主要的候选者

  7. @Bean中注入依赖的3种方式需要掌握

  8. 掌握泛型注入的使用

  9. 主要还是掌握候选者的查找过程,过程熟悉了,其他的都是小意思,回头再去看看上面的几个查找的流程图。

案例源码

https://gitee.com/javacode2018/spring-series

路人甲java所有案例代码以后都会放到这个上面,大家watch一下,可以持续关注动态。

Spring系列

  1. Spring系列第1篇:为何要学spring?

  2. Spring系列第2篇:控制反转(IoC)与依赖注入(DI)

  3. Spring系列第3篇:Spring容器基本使用及原理

  4. Spring系列第4篇:xml中bean定义详解(-)

  5. Spring系列第5篇:创建bean实例这些方式你们都知道?

  6. Spring系列第6篇:玩转bean scope,避免跳坑里!

  7. Spring系列第7篇:依赖注入之手动注入

  8. Spring系列第8篇:自动注入(autowire)详解,高手在于坚持

  9. Spring系列第9篇:depend-on到底是干什么的?

  10. Spring系列第10篇:primary可以解决什么问题?

  11. Spring系列第11篇:bean中的autowire-candidate又是干什么的?

  12. Spring系列第12篇:lazy-init:bean延迟初始化

  13. Spring系列第13篇:使用继承简化bean配置(abstract & parent)

  14. Spring系列第14篇:lookup-method和replaced-method比较陌生,怎么玩的?

  15. Spring系列第15篇:代理详解(Java动态代理&cglib代理)?

  16. Spring系列第16篇:深入理解java注解及spring对注解的增强(预备知识)

  17. Spring系列第17篇:@Configration和@Bean注解详解(bean批量注册)

  18. Spring系列第18篇:@ComponentScan、@ComponentScans详解(bean批量注册)

  19. Spring系列第18篇:@import详解(bean批量注册)

  20. Spring系列第20篇:@Conditional通过条件来控制bean的注册

更多好文章

  1. Java高并发系列(共34篇)

  2. MySql高手系列(共27篇)

  3. Maven高手系列(共10篇)

  4. Mybatis系列(共12篇)

  5. 聊聊db和缓存一致性常见的实现方式

  6. 接口幂等性这么重要,它是什么?怎么实现?

  7. 泛型,有点难度,会让很多人懵逼,那是因为你没有看这篇文章!

感谢大家的阅读,也欢迎您把这篇文章分享给更多的朋友一起阅读!谢谢!

路人甲java

▲长按图片识别二维码关注

路人甲Java:工作10年的前阿里P7分享Java、算法、数据库方面的技术干货!坚信用技术改变命运,让家人过上更体面的生活!


转载:https://blog.csdn.net/likun557/article/details/105154280
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场