Java充电社
专辑
博文
联系我
本人继续续收门徒,亲手指导
Spring教程
-> 注解实现依赖注入(@Autowired、@Resource、@Primary、@Qulifier)
1、Spring是什么?我们为什么要学习它?
2、控制反转(IoC)与依赖注入(DI)
3、Spring容器基本使用及原理
4、xml中bean定义详解
5、容器创建bean实例有多少种?
6、bean作用域scope详解
7、依赖注入之手动注入
8、依赖注入之自动注入(autowire)详解
9、depend-on干什么的?
10、primary可以解决什么问题?
11、bean中的autowire-candidate属性又是干什么的?
12、lazy-init:bean延迟初始化
13、使用继承简化bean配置(abstract & parent)
14、lookup-method和replaced-method比较陌生,怎么玩的?
15、代理详解(java动态代理&CGLIB代理)
16、深入理解java注解(预备知识)
17、@Configration、@Bean注解详解
18、@ComponentScan、@ComponentScans详解
19、@Import 注解详解
20、@Conditional通过条件来控制bean的注册
21、注解实现依赖注入(@Autowired、@Resource、@Primary、@Qulifier)
22、@Scope、@DependsOn、@ImportResource、@Lazy
23、Bean生命周期详解
24、父子容器
25、@PropertySource、@Value注解及动态刷新实现
26、国际化详解
27、事件详解
28、循环bean详解
29、BeanFactory扩展(BeanFactoryPostProcessor、BeanDefinitionRegistryPostProcessor)
30、jdk动态代理和cglib代理
31、Aop概念详解
32、AOP核心源码、原理详解
33、ProxyFactoryBean创建AOP代理
34、@Aspect中@Pointcut 12种用法
35、@Aspect中5中通知详解
36、@EnableAspectJAutoProxy、@Aspect中通知顺序详解
37、@EnableAsync & @Async 实现方法异步调用
38、@Scheduled & @EnableScheduling定时器详解
39、强大的Spel表达式
40、缓存使用(@EnableCaching、@Cacheable、@CachePut、@CacheEvict、@Caching、@CacheConfig)
41、@EnableCaching集成redis缓存
42、JdbcTemplate实现增删改查如此简单?
43、Spring中编程式事务怎么用的?
44、Spring声明事务怎么用的?
45、详解Spring事务中7种传播行为
46、Spring如何管理多数据源事务?
47、Spring事务源码解析
48、@Transaction 事务源码解析
49、实战篇:手把手带你实现事务消息!
50、Spring事务拦截器顺序如何控制?
51、Spring事务失效常见的几种情况
52、Spring实现数据库读写分离
53、Spring集成MyBatis
54、集成junit
55、Spring上下文生命周期
56、面试官:循环依赖不用三级缓存可以么?
57、Spring常用工具类
上一篇:@Conditional通过条件来控制bean的注册
下一篇:@Scope、@DependsOn、@ImportResource、@Lazy
<div style="display:none"></div> ## 先来看几个问题 1. 通过注解的方式注入依赖对象,介绍一下你知道的几种方式 2. @Autowired和@Resource有何区别 3. 说一下@Autowired查找候选者的过程 4. 说一下@Resource查找候选者的过程 5. @Qulifier有哪些用法? 6. @Qulifier加在类上面是干什么用的? 7. @Primary是做什么的? 8. 泛型注入用过么? **这些问题如果你都ok,那么恭喜你,很厉害。** ## 本文内容 1. 介绍spring中通过注解实现依赖注入的所有方式 - @Autowired注解 - @Qualifier注解 - @Resource注解 - @Primary注解 - @Bean中注入的几种方式 3. 将指定类型的所有bean,注入到集合中 4. 将指定类型的所有bean,注入到map中 5. 注入泛型 6. 依赖注入源码方面的一些介绍 **本文内容比较多,所有知识点均有详细案例,大家一定要敲一遍,加深理解。** ## @Autowired:注入依赖对象 ### 作用 **实现依赖注入,spring容器会对bean中所有字段、方法进行遍历,标注有@Autowired注解的,都会进行注入。** 看一下其定义: ```java @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标注在字段上面:假定字段类型为一个自定义的普通的类型,候选者查找过程如下 ![](https://itsoku.oss-cn-hangzhou.aliyuncs.com/itsoku/blog/article/103/3af48a79-ea90-48d5-9135-bde85a800b38.jpg) #### @Autowired标注在方法上或者方法参数上面:假定参数类型为为一个自定义的普通的类型,候选者查找过程如下: ![](https://itsoku.oss-cn-hangzhou.aliyuncs.com/itsoku/blog/article/103/a028c875-f470-4fdf-abfa-63b1b2aa06ae.jpg) 上图中深色的表示方法注入和字段注入查找过程的不同点。 上图中展示的是方法中只有一个参数的情况,如果有多个参数,就重复上面的过程,直到找到所有需要注入的参数。 #### 将指定类型的所有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 ```java package com.javacode2018.lesson001.demo26.test0; import org.springframework.stereotype.Component; @Component public class Service1 { } ``` #### Service2 ```java 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方法,一会打印测试的时候方便查看 #### 来个总的配置文件 ```java package com.javacode2018.lesson001.demo26.test0; import org.springframework.context.annotation.ComponentScan; @ComponentScan //@1 public class MainConfig0 { } ``` > @1:会自动扫描当前类所在的包,会将Service1和Service2注册到容器。 #### 来个测试用例 ```java 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注解,如下: ```java @Autowired public Service2(Service1 service1) { System.out.println(this.getClass() + "有参构造器"); this.service1 = service1; } ``` #### 再次运行test0() ```java 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 ```java 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,然后注入。 #### 来个总的配置文件 ```java package com.javacode2018.lesson001.demo26.test1; import org.springframework.context.annotation.ComponentScan; @ComponentScan public class MainConfig1 { } ``` #### 来个测试用例 InjectTest中加个方法 ```java @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))); } } ``` #### 运行输出 ```java 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 ```java package com.javacode2018.lesson001.demo26.test2; import org.springframework.stereotype.Component; @Component public class Service1 { } ``` #### Service2 ```java 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对象。 #### 来个总的配置文件 ```java package com.javacode2018.lesson001.demo26.test2; import org.springframework.context.annotation.ComponentScan; @ComponentScan public class MainConfig2 { } ``` #### 来个测试用例 ```java @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))); } } ``` #### 运行输出 ```java 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 ```java package com.javacode2018.lesson001.demo26.test3; import org.springframework.stereotype.Component; @Component public class Service1 { } ``` #### Service2 ```java 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; } @Override public String toString() { return "Service2{" + "service1=" + service1 + '}'; } } ``` > @1:方法上标注了@Autowired,表示会将这个方法作为注入方法,这个方法有2个参数,spring查找这2个参数对应的bean,然后注入。 > > 第一个参数对应的bean是存在的,第二个是一个String类型的,我们并没有定义String类型bean,一会看看效果 #### 来个总的配置文件 ```java package com.javacode2018.lesson001.demo26.test3; import org.springframework.context.annotation.ComponentScan; @ComponentScan public class MainConfig3 { } ``` #### 来个测试用例 ```java @Test public void test3() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig3.class); for (String beanName : context.getBeanDefinitionNames()) { System.out.println(String.format("%s->%s", beanName, context.getBean(beanName))); } } ``` #### 运行输出 ```java 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对象,调整一下代码,如下: ```java @Autowired public void injectService1(Service1 service1, @Autowired(required = false) String name) { //@1 System.out.println(String.format("%s.injectService1(),{service1=%s,name=%s}", this.getClass().getName(), service1, name)); this.service1 = service1; } ``` > 此时方法的第一个参数被方法上面的@Autowired约束 > > 第二个参数受@Autowired(required = false)约束 #### 再次运行输出 ```java com.javacode2018.lesson001.demo26.test3.Service2.injectService1(),{service1=com.javacode2018.lesson001.demo26.test3.Service1@59309333,name=null} service1->com.javacode2018.lesson001.demo26.test3.Service1@59309333 service2->Service2{service1=com.javacode2018.lesson001.demo26.test3.Service1@59309333} ``` > 注入成功了,service1有值,name为null ### 案例5:@Autowired用在字段上 #### Service1 ```java package com.javacode2018.lesson001.demo26.test4; import org.springframework.stereotype.Component; @Component public class Service1 { } ``` #### Service2 ```java package com.javacode2018.lesson001.demo26.test4; import org.springframework.stereotype.Component; @Component public class Service2 { } ``` #### Service3 ```java package com.javacode2018.lesson001.demo26.test4; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class Service3 { @Autowired private Service1 service1;//@1 @Autowired private Service2 service2;//@2 @Override public String toString() { return "Service3{" + "service1=" + service1 + ", service2=" + service2 + '}'; } } ``` > @1和@2:定义了2个字段,上面都标注了@Autowired,spring会去容器中按照类型查找这2种类型的bean,然后设置给这2个属性。 #### 来个总的配置文件 ```java package com.javacode2018.lesson001.demo26.test4; import org.springframework.context.annotation.ComponentScan; @ComponentScan public class MainConfig4 { } ``` #### 来个测试用例 ```java @Test public void test4() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig4.class); for (String beanName : context.getBeanDefinitionNames()) { System.out.println(String.format("%s->%s", beanName, context.getBean(beanName))); } } ``` #### 运行输出 ```java service1->com.javacode2018.lesson001.demo26.test4.Service1@7e07db1f service2->com.javacode2018.lesson001.demo26.test4.Service2@1189dd52 service3->Service3{service1=com.javacode2018.lesson001.demo26.test4.Service1@7e07db1f, service2=com.javacode2018.lesson001.demo26.test4.Service2@1189dd52} ``` > service3中标注@Autowired的2个属性都有值了,都被注入成功了。 ### 案例6:@Autowire标注字段,多个候选者的时候,按字段名称注入 #### IService接口 ```java package com.javacode2018.lesson001.demo26.test5; public interface IService { } ``` #### 接口来2个实现 2个实现类上都标注了@Component注解,都会被注册到容器中。 ##### Service0 ```java package com.javacode2018.lesson001.demo26.test5; import org.springframework.stereotype.Component; @Component public class Service0 implements IService { } ``` ##### Service1 ```java package com.javacode2018.lesson001.demo26.test5; import org.springframework.stereotype.Component; @Component public class Service1 implements IService { } ``` #### 来个Service2 ```java package com.javacode2018.lesson001.demo26.test5; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class Service2 { @Autowired private IService service1; //@1 @Override public String toString() { return "Service2{" + "service1=" + service1 + '}'; } } ``` > @1:标注了@Autowired注解,需要注入类型为IService类型的bean,满足这种类型的有2个:service0和service1 > > 按照上面介绍的候选者查找过程,最后会注入和字段名称一样的bean,即:service1 #### 来个总的配置类,负责扫描当前包中的组件 ```java package com.javacode2018.lesson001.demo26.test5; import org.springframework.context.annotation.ComponentScan; @ComponentScan public class MainConfig5 { } ``` #### 来个测试用例 ```java @Test public void test5() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig5.class); for (String beanName : context.getBeanDefinitionNames()) { System.out.println(String.format("%s->%s", beanName, context.getBean(beanName))); } } ``` #### 运行输出 ```java service0->com.javacode2018.lesson001.demo26.test5.Service0@36902638 service1->com.javacode2018.lesson001.demo26.test5.Service1@223d2c72 service2->Service2{service1=com.javacode2018.lesson001.demo26.test5.Service1@223d2c72} ``` > 注意最后一行,service2中的service1被注入了bean:service1 ### 案例7:将指定类型的所有bean,注入到Collection、Map中 #### 注入到Collection中 **被注入的类型为Collection类型或者Collection子接口类型,注意必须是接口类型**,如: ```java Collection<IService> List<IService> Set<IService> ``` **会在容器中找到所有IService类型的bean,放到这个集合中**。 #### 注入到Map中 **被注入的类型为Map类型或者Map子接口类型,注意必须是接口类型**,如: ```java Map<String,IService> ``` **会在容器中找到所有IService类型的bean,放到这个Map中,key为bean的名称,value为bean对象**。 来看案例代码。 #### 来个接口 ```java package com.javacode2018.lesson001.demo26.test6; public interface IService { } ``` #### 来2个实现类,标注@Component注解 ##### Service0 ```java package com.javacode2018.lesson001.demo26.test6; import org.springframework.stereotype.Component; @Component public class Service0 implements IService { } ``` ##### Service1 ```java package com.javacode2018.lesson001.demo26.test6; import org.springframework.stereotype.Component; @Component public class Service1 implements IService { } ``` #### 再来个类Service2 ```java package com.javacode2018.lesson001.demo26.test6; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.util.List; import java.util.Map; @Component public class Service2 { @Autowired private List<IService> services; @Autowired private Map<String, IService> serviceMap; @Override public String toString() { return "Service2{\n" + "services=" + services + ", \n serviceMap=" + serviceMap + '}'; } } ``` > @1:注入IService类型的所有bean > > @2:注入一个map #### 来个总的配置类 ```java package com.javacode2018.lesson001.demo26.test6; import org.springframework.context.annotation.ComponentScan; @ComponentScan public class MainConfig6 { } ``` #### 来个测试用例 ```java @Test public void test6() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig6.class); for (String beanName : context.getBeanDefinitionNames()) { System.out.println(String.format("%s->%s", beanName, context.getBean(beanName))); } } ``` #### 运行输出 ```java service0->com.javacode2018.lesson001.demo26.test6.Service0@1189dd52 service1->com.javacode2018.lesson001.demo26.test6.Service1@36bc55de service2->Service2{ services=[com.javacode2018.lesson001.demo26.test6.Service0@1189dd52, com.javacode2018.lesson001.demo26.test6.Service1@36bc55de], serviceMap={service0=com.javacode2018.lesson001.demo26.test6.Service0@1189dd52, service1=com.javacode2018.lesson001.demo26.test6.Service1@36bc55de}} ``` > 注意看一下上面services和serviceMap的值。 ### @Autowired源码 spring使用下面这个类处理@Autowired注解 ```java org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor ``` ## @Resource:注意依赖对象 ### 作用 **和@Autowired注解类似,也是用来注入依赖的对象的,spring容器会对bean中所有字段、方法进行遍历,标注有@Resource注解的,都会进行注入。** 看一下这个注解定义: ```java javax.annotation.Resource @Target({TYPE, FIELD, METHOD}) @Retention(RUNTIME) public @interface Resource { String name() default ""; ..其他不常用的参数省略 } ``` > 这个注解是javax中定义的,并不是spring中定义的注解。 > > 从定义上可以见,这个注解可以用在任何类型上面、字段、方法上面。 > > 注意点: > > **用在方法上的时候,方法参数只能有一个。** ### @Resource查找候选者的过程 **查找过程有点复杂,看不懂的可以先跳过,先看后面案例,本文看完之后,可以回头再来看这个过程。** #### @Resource标注在字段上面:假定字段类型为一个自定义的普通的类型,候选者查找过程如下 ![](https://itsoku.oss-cn-hangzhou.aliyuncs.com/itsoku/blog/article/103/f3f8a3e6-d915-4886-a03c-39747c045bf2.jpg) #### @Autowired标注在方法上或者方法参数上面:假定参数类型为为一个自定义的普通的类型,候选者查找过程如下: ![](https://itsoku.oss-cn-hangzhou.aliyuncs.com/itsoku/blog/article/103/6346a4e4-747d-4b8d-bdba-a45123611256.jpg) #### 将指定类型的所有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接口 ```java package com.javacode2018.lesson001.demo26.test7; public interface IService { } ``` #### 2个实现类 ##### Service0 ```java package com.javacode2018.lesson001.demo26.test7; import org.springframework.stereotype.Component; @Component public class Service0 implements IService { } ``` > @Component标注的bean名称默认为service0 ##### Service1 ```java package com.javacode2018.lesson001.demo26.test7; import org.springframework.stereotype.Component; @Component public class Service1 implements IService { } ``` > @Component标注的bean名称默认为service1 再来一个类 ```java package com.javacode2018.lesson001.demo26.test7; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import javax.annotation.Resource; import java.util.List; import java.util.Map; @Component public class Service2 { @Resource private IService service1;//@1 @Override public String toString() { return "Service2{" + "service1=" + service1 + '}'; } } ``` > @1:字段名称为service1,按照字段名称查找bean,会找到Service1 #### 来个配置类 ```java package com.javacode2018.lesson001.demo26.test7; import org.springframework.context.annotation.ComponentScan; @ComponentScan public class MainConfig7 { } ``` #### 测试用例 ```java @Test public void test7() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig7.class); for (String beanName : context.getBeanDefinitionNames()) { System.out.println(String.format("%s->%s", beanName, context.getBean(beanName))); } } ``` #### 运行输出 ```java service0->com.javacode2018.lesson001.demo26.test7.Service0@222545dc service1->com.javacode2018.lesson001.demo26.test7.Service1@5c5eefef service2->Service2{service1=com.javacode2018.lesson001.demo26.test7.Service1@5c5eefef} ``` > 最后一行可以看出注入了service1 #### 如果将Service2中的代码调整一下 ```java @Resource private IService service0; ``` > 此时会注入service0这个bean 同样@Resource可以用在方法上,也可以将所有类型的bean注入到Collection、Map中,这里就不演示了,重点了解一下候选者查找的过程,使用上就比较简单了,@Resource的其他案例,大家可以自己写写练练。 下面来说另外几个注解,也是比较重要的。 ### @Resource源码 spring使用下面这个类处理@Resource注解 ```java org.springframework.context.annotation.CommonAnnotationBeanPostProcessor ``` ## @Qualifier:限定符 ### 作用 这个单词的意思是:限定符。 **可以在依赖注入查找候选者的过程中对候选者进行过滤。** 看一下其定义: ```java @Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented public @interface Qualifier { String value() default ""; } ``` > 可以用在字段、方法、参数、任意类型、注解上面 > > 有一个参数value 还是来看案例,通过案例理解更容易。 ### 案例1:用在类上 用在类上,你可以理解为给通过@Qulifier给这个bean打了一个标签。 #### 先来一个接口 ```java package com.javacode2018.lesson001.demo26.test8; public interface IService { } ``` #### 来3个实现类 **前2个@Qulifier的value为tag1,第3个实现类为tag2** #### Service1 ```java package com.javacode2018.lesson001.demo26.test8; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; @Component @Qualifier("tag1") //@1 public class Service1 implements IService { } ``` > @1:tag1 Service2 ```java package com.javacode2018.lesson001.demo26.test8; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; @Component @Qualifier("tag1") public class Service2 implements IService { } ``` > @1:tag1 #### Service3 ```java package com.javacode2018.lesson001.demo26.test8; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; @Component @Qualifier("tag2")//@1 public class Service3 implements IService { } ``` > @1:tag2 #### 来一个类,来注入上面几个bean ```java package com.javacode2018.lesson001.demo26.test8; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; import java.util.Map; @Component public class InjectService { @Autowired @Qualifier("tag1") //@1 private Map<String, IService> serviceMap1; @Autowired @Qualifier("tag2") //@2 private Map<String, IService> serviceMap2; @Override public String toString() { return "InjectService{" + "serviceMap1=" + serviceMap1 + ", serviceMap2=" + serviceMap2 + '}'; } } ``` > @1:限定符的值为tag1,此时会将类上限定符为tag1的所有bean注入进来 > > @2:限定符的值为tag2,此时会将类上限定符为tag2的所有bean注入进来 #### 来个配置类 ```java package com.javacode2018.lesson001.demo26.test8; import org.springframework.context.annotation.ComponentScan; @ComponentScan public class MainConfig8 { } ``` #### 测试用例 ```java @Test public void test8() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig8.class); for (String beanName : context.getBeanDefinitionNames()) { System.out.println(String.format("%s->%s", beanName, context.getBean(beanName))); } } ``` 运行输出 ```java 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}} service1->com.javacode2018.lesson001.demo26.test8.Service1@9597028 service2->com.javacode2018.lesson001.demo26.test8.Service2@6069db50 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的名称 看案例代码 #### 来个接口 ```java package com.javacode2018.lesson001.demo26.test9; public interface IService { } ``` #### 有2个实现类 2个实现类上面没有使用@Qulifier注解了 ##### Service1 ```java package com.javacode2018.lesson001.demo26.test9; import org.springframework.stereotype.Component; @Component public class Service1 implements IService { } ``` ##### Service2 ```java package com.javacode2018.lesson001.demo26.test9; import org.springframework.stereotype.Component; @Component public class Service2 implements IService { } ``` > 我们可以知道上面2个bean的名称分别为:service1、service2 #### 来个类,注入IService类型的bean ```java package com.javacode2018.lesson001.demo26.test9; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; @Component public class InjectService { @Autowired @Qualifier("service2") //@1 private IService service; @Override public String toString() { return "InjectService{" + "service=" + service + '}'; } } ``` **@1:这里限定符的值为service2,容器中IService类型的bean有2个[service1和service2],当类上没有标注@Qualifier注解的时候,可以理解为:bean的名称就是限定符的值,所以@1这里会匹配到service2** #### 来个配置类 ```java package com.javacode2018.lesson001.demo26.test9; import org.springframework.context.annotation.ComponentScan; import org.springframework.stereotype.Component; @ComponentScan public class MainConfig9 { } ``` #### 来个测试用例 ```java @Test public void test9() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig9.class); for (String beanName : context.getBeanDefinitionNames()) { System.out.println(String.format("%s->%s", beanName, context.getBean(beanName))); } } ``` #### 运行输出 ``` injectService->InjectService{service=com.javacode2018.lesson001.demo26.test9.Service2@223d2c72} service1->com.javacode2018.lesson001.demo26.test9.Service1@8f4ea7c service2->com.javacode2018.lesson001.demo26.test9.Service2@223d2c72 ``` > 从第一行可以看出注入了service1 ### 案例3:用在方法参数上 #### 代码 ```java package com.javacode2018.lesson001.demo26.test10; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; @Component public class InjectService { private IService s1; private IService s2; @Autowired public void injectBean(@Qualifier("service2") IService s1, @Qualifier("service1") IService s2) { //@1 this.s1 = s1; this.s2 = s2; } @Override public String toString() { return "InjectService{" + "s1=" + s1 + ", s2=" + s2 + '}'; } } ``` > @1:方法上标注了@Autowired注解,说明会被注入依赖,2个参数上分别使用了限定符来指定具体需要注入哪个bean #### 测试用例 ```java @Test public void test10() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig10.class); for (String beanName : context.getBeanDefinitionNames()) { System.out.println(String.format("%s->%s", beanName, context.getBean(beanName))); } } ``` #### 运行输出 ```java injectService->InjectService{s1=com.javacode2018.lesson001.demo26.test10.Service2@55183b20, s2=com.javacode2018.lesson001.demo26.test10.Service1@4f83df68} service1->com.javacode2018.lesson001.demo26.test10.Service1@4f83df68 service2->com.javacode2018.lesson001.demo26.test10.Service2@55183b20 ``` > 第一行中的 > > s1:service2 > > s2:service1 ### 案例4:用在setter方法上 不管是用在setter方法还是普通方法上面,都是一样的效果 #### 代码 ```java package com.javacode2018.lesson001.demo26.test11; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; @Component public class InjectService { private IService s1; private IService s2; @Autowired @Qualifier("service2") public void setS1(IService s1) { this.s1 = s1; } @Autowired @Qualifier("service2") public void setS2(IService s2) { this.s2 = s2; } @Override public String toString() { return "InjectService{" + "s1=" + s1 + ", s2=" + s2 + '}'; } } ``` > 上面2个setter方法上都有@Autowired注解,并且结合了@Qulifier注解来限定需要注入哪个bean #### 测试用例 ```java @Test public void test11() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig11.class); for (String beanName : context.getBeanDefinitionNames()) { System.out.println(String.format("%s->%s", beanName, context.getBean(beanName))); } } ``` 运行输出 ```java injectService->InjectService{s1=com.javacode2018.lesson001.demo26.test11.Service2@35e2d654, s2=com.javacode2018.lesson001.demo26.test11.Service2@35e2d654} service1->com.javacode2018.lesson001.demo26.test11.Service1@1bd4fdd service2->com.javacode2018.lesson001.demo26.test11.Service2@35e2d654 ``` > 输出中可以看出:s1为service2,s2为service1 ## @Primary:设置为主要候选者 注入依赖的过程中,当有多个候选者的时候,可以指定哪个候选者为主要的候选者。 看一下其定义 ```java @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Primary { } ``` > 可以用在类上或者方法上面。 > > 通常定义bean常见的有2种方式: > > 方式1:在类上标注@Component注解,此时可以配合@Primary,标注这个bean为主要候选者 > > 方式2:在配置文件中使用@Bean注解标注方法,来注册bean,可以在@Bean标注的方法上加上@Primary,标注这个bean为主要候选bean。 看案例。 ### 案例1:用在类上 #### 来个接口 ```java package com.javacode2018.lesson001.demo26.test12; public interface IService { } ``` #### 2个实现类 ##### Service1 ``` package com.javacode2018.lesson001.demo26.test12; import org.springframework.stereotype.Component; @Component public class Service1 implements IService { } ``` ##### Service2 ```java package com.javacode2018.lesson001.demo26.test12; import org.springframework.context.annotation.Primary; import org.springframework.stereotype.Component; @Component @Primary public class Service2 implements IService { } ``` > Service2上面使用了@Primary,表示这是个主要的候选者 #### 再来个类,注入IService类型的bean ```java package com.javacode2018.lesson001.demo26.test12; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class InjectService { @Autowired private IService service1; //@1 @Override public String toString() { return "InjectService{" + "service1=" + service1 + '}'; } } ``` > @1:容器中IService类型的bean有2个,但是service2为主要的候选者,所以此处会注入service2 #### 总的配置类 ```java package com.javacode2018.lesson001.demo26.test12; import org.springframework.context.annotation.ComponentScan; @ComponentScan public class MainConfig12 { } ``` #### 测试用例 ``` @Test public void test12() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig12.class); for (String beanName : context.getBeanDefinitionNames()) { System.out.println(String.format("%s->%s", beanName, context.getBean(beanName))); } } ``` #### 运行输出 ```java injectService->InjectService{service1=com.javacode2018.lesson001.demo26.test12.Service2@49ec71f8} service1->com.javacode2018.lesson001.demo26.test12.Service1@1d2adfbe service2->com.javacode2018.lesson001.demo26.test12.Service2@49ec71f8 ``` ### 案例2:用在方法上,结合@Bean使用 #### 来个接口 ``` package com.javacode2018.lesson001.demo26.test13; public interface IService { } ``` #### 2个实现类 ##### Service1 ``` package com.javacode2018.lesson001.demo26.test13; public class Service1 implements IService { } ``` #### Service2 ```java package com.javacode2018.lesson001.demo26.test13; public class Service2 implements IService { } ``` #### InjectService ```java package com.javacode2018.lesson001.demo26.test13; import org.springframework.beans.factory.annotation.Autowired; public class InjectService { @Autowired private IService service1;//@1 @Override public String toString() { return "InjectService{" + "service1=" + service1 + '}'; } } ``` > 使用了@Autowired,需要注入 #### 来个配置类,通过@Bean定义上面3个类型的bean ```java package com.javacode2018.lesson001.demo26.test13; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; @Configuration public class MainConfig13 { @Bean public IService service1() { return new Service1(); } @Bean @Primary //@1 public IService service2() { return new Service2(); } @Bean public InjectService injectService() { return new InjectService(); } } ``` > 上面是一个配置类,定义了3个bean > > @1:这个bean被标注为主要的候选者 #### 来个测试用例 ```java @Test public void test13() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig13.class); for (String beanName : context.getBeanDefinitionNames()) { System.out.println(String.format("%s->%s", beanName, context.getBean(beanName))); } } ``` #### 运行输出 ```java service1->com.javacode2018.lesson001.demo26.test13.Service1@6913c1fb service2->com.javacode2018.lesson001.demo26.test13.Service2@66d18979 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 ```java package com.javacode2018.lesson001.demo26.test14; public class Service1 { } ``` #### Service2 ```java package com.javacode2018.lesson001.demo26.test14; public class Service2 { } ``` #### Service3 ```java package com.javacode2018.lesson001.demo26.test14; public class Service3 { private Service1 service1; private Service2 service2; public Service1 getService1() { return service1; } public void setService1(Service1 service1) { this.service1 = service1; } public Service2 getService2() { return service2; } public void setService2(Service2 service2) { this.service2 = service2; } @Override public String toString() { return "Service3{" + "service1=" + service1 + ", service2=" + service2 + '}'; } } ``` > 上面类中会用到service1和service2,提供了对应的setter方法,一会我们通过setter方法注入依赖对象 #### 来个配置类,通过@Bean的方式创建上面对象 ```java package com.javacode2018.lesson001.demo26.test14; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class MainConfig14 { @Bean public Service1 service1() { return new Service1(); } @Bean public Service2 service2() { return new Service2(); } @Bean public Service3 service3() { Service3 service3 = new Service3(); //@0 service3.setService1(this.service1()); //@1 service3.setService2(this.service2()); //@2 return service3; } } ``` > 上面代码中通过@Bean定义了3个bean > > Service3中需要用到Service1和Service2,注意@1和@2直接调用当前方法获取另外2个bean,注入到service3中 #### 测试用例 ```java @Test public void test14() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig14.class); for (String beanName : context.getBeanDefinitionNames()) { System.out.println(String.format("%s->%s", beanName, context.getBean(beanName))); } } ``` #### 运行输出 ```java service1->com.javacode2018.lesson001.demo26.test14.Service1@41a2befb service2->com.javacode2018.lesson001.demo26.test14.Service2@6c40365c service3->Service3{service1=com.javacode2018.lesson001.demo26.test14.Service1@41a2befb, service2=com.javacode2018.lesson001.demo26.test14.Service2@6c40365c} ``` ### 方式2:@Autowired、@Resource的方式 这种方式就不讲了直接在需要注入的对象上面加上这2个注解的任意一个就行了,可以参考文章前面的部分。 ### 方式3:@Bean标注的方法使用参数来进行注入 ```java package com.javacode2018.lesson001.demo26.test15; import com.javacode2018.lesson001.demo26.test14.Service1; import com.javacode2018.lesson001.demo26.test14.Service2; import com.javacode2018.lesson001.demo26.test14.Service3; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class MainConfig15 { @Bean public Service1 service1() { return new Service1(); } @Bean public Service2 service2() { return new Service2(); } @Bean public Service3 service3(Service1 s1, Service2 s2) { //@0 Service3 service3 = new Service3(); service3.setService1(s1); //@1 service3.setService2(s2); //@2 return service3; } } ``` > @0:这个地方是关键,方法上标注了@Bean,并且方法中是有参数的,spring调用这个方法创建bean的时候,会将参数中的两个参数注入进来。 > > 注入对象的查找逻辑可以参考上面@Autowired标注方法时查找候选者的逻辑。 来个测试用例 ```java @Test public void test15() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig15.class); for (String beanName : context.getBeanDefinitionNames()) { System.out.println(String.format("%s->%s", beanName, context.getBean(beanName))); } } ``` 运行输出 ```java service1->com.javacode2018.lesson001.demo26.test14.Service1@4009e306 service2->com.javacode2018.lesson001.demo26.test14.Service2@43c1b556 service3->Service3{service1=com.javacode2018.lesson001.demo26.test14.Service1@4009e306, service2=com.javacode2018.lesson001.demo26.test14.Service2@43c1b556} ``` 同样注入成功了。 ### 其他 #### @Bean标注的方法参数上使用@Autowired注解 ```java @Bean public Service3 service3_0(Service1 s1, @Autowired(required = false) Service2 s2) { //@0 Service3 service3 = new Service3(); service3.setService1(s1); //@1 service3.setService2(s2); //@2 return service3; } ``` > @0:方法由2个参数,第二个参数上标注了@Autowired(required = false),说明第二个参数候选者不是必须的,找不到会注入一个null对象;第一个参数候选者是必须的,找不到会抛出异常 #### @Bean结合@Qualifier ```java package com.javacode2018.lesson001.demo26.test17; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.Map; @Configuration public class MainConfig17 { @Bean @Qualifier("tag1") //@1 public Service1 service1() { return new Service1(); } @Bean @Qualifier("tag1") //@2 public Service2 service2() { return new Service2(); } @Bean @Qualifier("tag2") //@3 public Service3 service3() { return new Service3(); } @Bean public InjectService injectService(@Qualifier("tag1") Map<String, IService> map1) { //@4 InjectService injectService = new InjectService(); injectService.setServiceMap1(map1); return injectService; } } ``` > Service1,Service2,Service3都实现了IService接口 > > @1,@2,@3这3个方法上面使用了@Bean注解,用来定义3个bean,这3个方法上还是用了@Qualifier注解,用来给这些bean定义标签,service1()方法类似于下面的写法: ```java @Compontent @Qualifier("tag1") public class Service1 implements IService{ } ``` > 再回到MainConfig17中的@4:参数中需要注入Map<String, IService>,会查找IService类型的bean,容器中有3个,但是这个参数前面加上了@Qualifier限定符,值为tag1,所以会通过这个过滤,最后满足的候选者为:[service1,service] 对应测试用例 ```java @Test public void test17() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig17.class); for (String beanName : context.getBeanDefinitionNames()) { System.out.println(String.format("%s->%s", beanName, context.getBean(beanName))); } } ``` 运行输出 ```java service1->com.javacode2018.lesson001.demo26.test17.Service1@1190200a service2->com.javacode2018.lesson001.demo26.test17.Service2@6a2f6f80 service3->com.javacode2018.lesson001.demo26.test17.Service3@45b4c3a9 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 ```java package com.javacode2018.lesson001.demo26.test18; public class UserModel { } ``` #### OrderModel ```java package com.javacode2018.lesson001.demo26.test18; public class OrderModel { } ``` 记住上面2个普通的类UserModel和OrderModel,一会下面会用到。 ### 来个泛型接口 ```java package com.javacode2018.lesson001.demo26.test18; public interface IDao<T> { } ``` > 上面是个泛型类,类名后面后尖括号 ### 来2个实现类 两个实现类都会标注@Compontent,交给spring容器管理 #### UserDao ```java package com.javacode2018.lesson001.demo26.test18; import org.springframework.stereotype.Component; @Component public class UserDao implements IDao<UserModel> { //@1 } ``` > @1:指定了IDao后面泛型的类型为UserModel #### OrderDao ```java package com.javacode2018.lesson001.demo26.test18; import org.springframework.stereotype.Component; @Component public class OrderDao implements IDao<OrderModel> {//@1 } ``` > @1:指定了IDao后面泛型的类型为OrderModel ### 在来个泛型类型 ```java package com.javacode2018.lesson001.demo26.test18; import org.springframework.beans.factory.annotation.Autowired; public class BaseService<T> { @Autowired private IDao<T> dao; //@1 public IDao<T> getDao() { return dao; } public void setDao(IDao<T> dao) { this.dao = dao; } } ``` > BaseService同样是个泛型类 > > @1:这个地方要注意了,上面使用了@Autowired,来注入IDao对象 ### BaseService来2个子类 两个子类都会标注@Compontent,交给spring容器管理 #### UserService ```java package com.javacode2018.lesson001.demo26.test18; import org.springframework.stereotype.Component; @Component public class UserService extends BaseService<UserModel> {//@1 } ``` > @1:指定了BaseService后面泛型的类型为UserModel #### OrderService ```java package com.javacode2018.lesson001.demo26.test18; import org.springframework.stereotype.Component; @Component public class OrderService extends BaseService<OrderModel> {//@1 } ``` > @1:指定了BaseService后面泛型的类型为OrderModel **UserService和OrderService继承了BaseService,所以一会BaseService中的dao属性会被注入,一会我们关注一下dao这个属性的值,会是什么样的** ### 来个总的配置类 ```java package com.javacode2018.lesson001.demo26.test18; import org.springframework.context.annotation.ComponentScan; @ComponentScan public class MainConfig18 { } ``` > 上面有@CompontentScan注解,会自动扫描当前包中的所有类,并进行自动注入 ### 来个测试用例 ```java @Test public void test18() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig18.class); System.out.println(context.getBean(UserService.class).getDao()); System.out.println(context.getBean(OrderService.class).getDao()); } ``` > 上面代码中会将两个service中的dao输出,我们来看一下效果 ### 运行输出 ```java com.javacode2018.lesson001.demo26.test18.UserDao@6adbc9d 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. **主要还是掌握候选者的查找过程,过程熟悉了,其他的都是小意思,回头再去看看上面的几个查找的流程图。** ## 案例源码 ```java https://gitee.com/javacode2018/spring-series ``` **路人甲java所有案例代码以后都会放到这个上面,大家watch一下,可以持续关注动态。** <a style="display:none" target="_blank" href="https://mp.weixin.qq.com/s/_S1DD2JADnXvpexxaBwLLg" style="color:red; font-size:20px; font-weight:bold">继续收门徒,亲手带,月薪 4W 以下的可以来找我</a> ## 最新资料 1. <a href="https://mp.weixin.qq.com/s?__biz=MzkzOTI3Nzc0Mg==&mid=2247484964&idx=2&sn=c81bce2f26015ee0f9632ddc6c67df03&scene=21#wechat_redirect" target="_blank">尚硅谷 Java 学科全套教程(总 207.77GB)</a> 2. <a href="https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247484192&idx=1&sn=505f2faaa4cc911f553850667749bcbb&scene=21#wechat_redirect" target="_blank">2021 最新版 Java 微服务学习线路图 + 视频</a> 3. <a href="https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247484573&idx=1&sn=7f3d83892186c16c57bc0b99f03f1ffd&scene=21#wechat_redirect" target="_blank">阿里技术大佬整理的《Spring 学习笔记.pdf》</a> 4. <a href="https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247484544&idx=2&sn=c1dfe907cfaa5b9ae8e66fc247ccbe84&scene=21#wechat_redirect" target="_blank">阿里大佬的《MySQL 学习笔记高清.pdf》</a> 5. <a href="https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247485167&idx=1&sn=48d75c8e93e748235a3547f34921dfb7&scene=21#wechat_redirect" target="_blank">2021 版 java 高并发常见面试题汇总.pdf</a> 6. <a href="https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247485664&idx=1&sn=435f9f515a8f881642820d7790ad20ce&scene=21#wechat_redirect" target="_blank">Idea 快捷键大全.pdf</a> ![](https://itsoku.oss-cn-hangzhou.aliyuncs.com/itsoku/blog/article/1/2883e86e-3eff-404a-8943-0066e5e2b454.png)
#custom-toc-container