【Spring专题】停更一个多月,期间在更新其余专题的内容,很多小伙伴纷纷留言说:冰河,你【Spring专题】是否是停更了啊!其实并无停更,只是中途有不少小伙伴留言说急需学习一些知识技能,以便于跳槽,哈哈,你们都懂得!因此,中途停更了一段时间,写了一些其余专题的文章。如今,继续更新【String专题】。java
关注 冰河技术 微信公众号,订阅更多技术干货!若是文章对你有所帮助,请不要吝惜你的点赞、在看、留言和转发,你的支持是我持续创做的最大动力!git
项目工程源码已经提交到GitHub:https://github.com/sunshinelyz/spring-annotationgithub
@Autowired 注解,能够对类成员变量、方法和构造函数进行标注,完成自动装配的工做。@Autowired 注解能够放在类,接口以及方法上。在使用@Autowired以前,咱们对一个bean配置属性时,是用以下xml文件的形式进行配置的。面试
<property name="属性名" value=" 属性值"/>
@Autowired 注解的源码以下所示。spring
package org.springframework.beans.factory.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Autowired { boolean required() default true; }
@Autowired 注解说明:设计模式
(1)默认优先按照类型去容器中找对应的组件,找到就赋值;bash
(2)若是找到多个相同类型的组件,再将属性名称做为组件的id,到 IOC 容器中进行查找。微信
@Autowired是根据类型进行自动装配的,若是须要按名称进行装配,则须要配合@Qualifier 注解使用。并发
@Qualifier注解源码以下所示。分布式
package org.springframework.beans.factory.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented public @interface Qualifier { String value() default ""; }
在Spring 中使用注解,常使用@Autowired, 默认是根据类型Type来自动注入的。但有些特殊状况,对同一个接口,可能会有几种不一样的实现类,而默认只会采起其中一种实现的状况下, 就可使用@Primary注解来标注优先使用哪个实现类。
@Primary注解的源码以下所示。
package org.springframework.context.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Primary { }
在进行项目实战以前,咱们先来讲说什么是Spring组件的自动装配。Spring组件的自动装配就是:Spring利用依赖注入,也就是咱们一般所说的DI,完成对IOC容器中各个组件的依赖关系赋值。
这里,咱们以以前项目中建立的dao、service和controller为例进行说明。dao、service和controller的初始代码分别以下所示。
package io.mykit.spring.plugins.register.dao; import org.springframework.stereotype.Repository; /** * @author binghe * @version 1.0.0 * @description 测试的dao */ @Repository public class PersonDao { }
package io.mykit.spring.plugins.register.service; import io.mykit.spring.plugins.register.dao.PersonDao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** * @author binghe * @version 1.0.0 * @description 测试的Service */ @Service public class PersonService { @Autowired private PersonDao personDao; }
package io.mykit.spring.plugins.register.controller; import org.springframework.stereotype.Controller; /** * @author binghe * @version 1.0.0 * @description 测试的controller */ @Controller public class PersonController { @Autowired private PersonService personService; }
能够看到,咱们在Service中使用@Autowired注解注入了Dao,在Controller中使用@Autowired注解注入了Service。为了方便测试,咱们在PersonService类中生成一个toString()方法,以下所示。
package io.mykit.spring.plugins.register.service; import io.mykit.spring.plugins.register.dao.PersonDao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** * @author binghe * @version 1.0.0 * @description 测试的Service */ @Service public class PersonService { @Autowired private PersonDao personDao; @Override public String toString() { return personDao.toString(); } }
这里,咱们在PersonService类的toString()方法中直接调用personDao的toString()方法并返回。为了更好的演示效果,咱们在项目的 io.mykit.spring.plugins.register.config
包下建立AutowiredConfig类,以下所示。
package io.mykit.spring.plugins.register.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; /** * @author binghe * @version 1.0.0 * @description 测试自动装配组件的Config配置类 */ @Configuration @ComponentScan(value = { "io.mykit.spring.plugins.register.dao", "io.mykit.spring.plugins.register.service", "io.mykit.spring.plugins.register.controller"}) public class AutowiredConfig { }
接下来,咱们来测试一下上面的程序,咱们在项目的src/test/java目录下的 io.mykit.spring.test
包下建立AutowiredTest类,以下所示。
package io.mykit.spring.test; import io.mykit.spring.plugins.register.config.AutowiredConfig; import io.mykit.spring.plugins.register.service.PersonService; import org.junit.Test; import org.springframework.context.annotation.AnnotationConfigApplicationContext; /** * @author binghe * @version 1.0.0 * @description 测试自动装配 */ public class AutowiredTest { @Test public void testAutowired01(){ //建立IOC容器 AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AutowiredConfig.class); PersonService personService = context.getBean(PersonService.class); System.out.println(personService); context.close(); } }
测试方法比较简单,这里,我就不作过多说明了。接下来,咱们运行AutowiredTest类的testAutowired01()方法,得出的输出结果信息以下所示。
io.mykit.spring.plugins.register.dao.PersonDao@10e92f8f
能够看到,输出了PersonDao信息。
那么问题来了:咱们在PersonService类中输出的PersonDao,和咱们直接在Spring IOC容器中获取的PersonDao是否是同一个对象呢?
咱们能够在AutowiredTest类的testAutowired01()方法中添加获取PersonDao对象的方法,并输出获取到的PersonDao对象,以下所示。
@Test public void testAutowired01(){ //建立IOC容器 AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AutowiredConfig.class); PersonService personService = context.getBean(PersonService.class); System.out.println(personService); PersonDao personDao = context.getBean(PersonDao.class); System.out.println(personDao); context.close(); }
咱们再次运行AutowiredTest类的testAutowired01()方法,输出的结果信息以下所示。
io.mykit.spring.plugins.register.dao.PersonDao@10e92f8f io.mykit.spring.plugins.register.dao.PersonDao@10e92f8f
能够看到,咱们在PersonService类中输出的PersonDao对象和直接从IOC容器中获取的PersonDao对象是同一个对象。
若是在Spring容器中存在对多个PersonDao对象该如何处理呢?
首先,为了更加直观的看到咱们使用@Autowired注解装配的是哪一个PersonDao对象,咱们对PersonDao类进行改造,为其加上一个remark字段,为其赋一个默认值,以下所示。
package io.mykit.spring.plugins.register.dao; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import org.springframework.stereotype.Repository; /** * @author binghe * @version 1.0.0 * @description 测试的dao */ @Data @NoArgsConstructor @AllArgsConstructor @Repository public class PersonDao { private String remark = "1"; }
接下来,咱们就在AutowiredConfig类中注入一个PersonDao对象,而且显示指定PersonDao对象在IOC容器中的bean的名称为personDao2,并为PersonDao对象的remark字段赋值为2,以下所示。
@Bean("personDao2") public PersonDao personDao(){ return new PersonDao("2"); }
目前,在咱们的IOC容器中就会注入两个PersonDao对象。那此时,@Autowired注解装配的是哪一个PersonDao对象呢?
接下来,咱们运行AutowiredTest类的testAutowired01()方法,输出的结果信息以下所示。
PersonDao{remark='1'}
能够看到,结果信息输出了1,说明:@Autowired注解默认优先按照类型去容器中找对应的组件,找到就赋值;若是找到多个相同类型的组件,再将属性名称做为组件的id,到 IOC 容器中进行查找。
那咱们如何让@Autowired装配personDao2呢? 这个问题问的好,其实很简单,咱们将PersonService类中的personDao所有修改成personDao2,以下所示。
package io.mykit.spring.plugins.register.service; import io.mykit.spring.plugins.register.dao.PersonDao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** * @author binghe * @version 1.0.0 * @description 测试的Service */ @Service public class PersonService { @Autowired private PersonDao personDao2; @Override public String toString() { return personDao2.toString(); } }
此时,咱们再次运行AutowiredTest类的testAutowired01()方法,输出的结果信息以下所示。
PersonDao{remark='2'}
能够看到,此时命令行输出了personDao2的信息。
从测试@Autowired注解的结果来看:@Autowired注解默认优先按照类型去容器中找对应的组件,找到就赋值;若是找到多个相同类型的组件,再将属性名称做为组件的id,到 IOC 容器中进行查找。
若是IOC容器中存在多个相同类型的组件时,咱们可不能够显示指定@Autowired注解装配哪一个组件呢?有些小伙伴确定会说:废话!你都这么问了,那确定能够啊!没错,确实能够啊!此时,@Qualifier注解就派上用场了!
在以前的测试案例中,命令行输出了 PersonDao{remark='2'}
说明@Autowired注解装配了personDao2,那咱们如何显示的让@Autowired注解装配personDao呢?
比较简单,咱们只须要在PersonService类上personDao2字段上添加@Qualifier注解,显示指定@Autowired注解装配personDao,以下所示。
@Qualifier("personDao") @Autowired private PersonDao personDao2;
此时,咱们再次运行AutowiredTest类的testAutowired01()方法,输出的结果信息以下所示。
PersonDao{remark='1'}
能够看到,此时尽管字段的名称为personDao2,可是咱们使用了@Qualifier注解显示指定@Autowired注解装配personDao对象,因此,最终的结果输出了personDao对象的信息。
若是IOC容器中无相应的组件,会发生什么状况呢?此时,咱们删除PersonDao类上的@Repository注解,而且删除AutowiredConfig类中的personDao()方法上的@Bean注解,以下所示。
package io.mykit.spring.plugins.register.dao; /** * @author binghe * @version 1.0.0 * @description 测试的dao */ public class PersonDao { private String remark = "1"; public String getRemark() { return remark; } public void setRemark(String remark) { this.remark = remark; } @Override public String toString() { return "PersonDao{" + "remark='" + remark + '\'' + '}'; } }
package io.mykit.spring.plugins.register.config; import io.mykit.spring.plugins.register.dao.PersonDao; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; /** * @author binghe * @version 1.0.0 * @description 测试自动装配组件的Config配置类 */ @Configuration @ComponentScan(value = { "io.mykit.spring.plugins.register.dao", "io.mykit.spring.plugins.register.service", "io.mykit.spring.plugins.register.controller"}) public class AutowiredConfig { public PersonDao personDao(){ PersonDao personDao = new PersonDao(); personDao.setRemark("2"); return personDao; } }
此时IOC容器中再也不有personDao,咱们再次运行AutowiredTest类的testAutowired01()方法,输出的结果信息以下所示。
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'io.mykit.spring.plugins.register.dao.PersonDao' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Qualifier(value=personDao), @org.springframework.beans.factory.annotation.Autowired(required=true)}
能够看到,Spring抛出了异常,未找到相应的bean对象,咱们能不能让Spring不报错呢? 那确定能够啊!Spring的异常信息中都给出了相应的提示。
{@org.springframework.beans.factory.annotation.Qualifier(value=personDao), @org.springframework.beans.factory.annotation.Autowired(required=true)}
解决方案就是在PersonService类的@Autowired添加一个属性required=false
,以下所示。
@Qualifier("personDao") @Autowired(required = false) private PersonDao personDao2;
而且咱们修改下PersonService的toString()方法,以下所示。
@Override public String toString() { return "PersonService{" + "personDao2=" + personDao2 + '}'; }
此时,还须要将AutowiredTest类的testAutowired01()方法中直接从IOC容器中获取personDao的代码删除,以下所示。
@Test public void testAutowired01(){ //建立IOC容器 AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AutowiredConfig.class); PersonService personService = context.getBean(PersonService.class); System.out.println(personService); context.close(); }
此时,咱们再次运行AutowiredTest类的testAutowired01()方法,输出的结果信息以下所示。
PersonService{personDao2=null}
能够看到,当为@Autowired添加属性required=false
后,即便IOC容器中没有对应的对象,Spring也不会抛出异常。此时,装配的对象就为null。
测试完成后,咱们再次为PersonDao类添加@Repository注解,而且为AutowiredConfig类中的personDao()方法添加@Bean注解。
在Spring中,对同一个接口,可能会有几种不一样的实现类,而默认只会采起其中一种实现的状况下, 就可使用@Primary注解来标注优先使用哪个实现类。
首先,咱们在AutowiredConfig类的personDao()方法上添加@Primary注解,此时,咱们须要删除PersonService类中personDao字段上的@Qualifier注解,这是由于@Qualifier注解为显示指定装配哪一个组件,若是使用了@Qualifier注解,不管是否使用了@Primary注解,都会装配@Qualifier注解标注的对象。
设置完成后,咱们再次运行AutowiredTest类的testAutowired01()方法,输出的结果信息以下所示。
PersonService{personDao2=PersonDao{remark='2'}}
能够看到,此时remark的值为2,装配了AutowiredConfig类中注入的personDao。
接下来,咱们为PersonService类中personDao字段再次添加@Qualifier注解,以下所示。
@Qualifier("personDao") @Autowired(required = false) private PersonDao personDao;
此时,咱们再次运行AutowiredTest类的testAutowired01()方法,输出的结果信息以下所示。
PersonService{personDao=PersonDao{remark='1'}}
能够看到,此时,Spring装配了使用@Qualifier标注的personDao。
关注「 冰河技术 」微信公众号,后台回复 “设计模式” 关键字领取《深刻浅出Java 23种设计模式》PDF文档。回复“Java8”关键字领取《Java8新特性教程》PDF文档。回复“限流”关键字获取《亿级流量下的分布式限流解决方案》PDF文档,三本PDF均是由冰河原创并整理的超硬核教程,面试必备!!
好了,今天就聊到这儿吧!别忘了点个赞,给个在看和转发,让更多的人看到,一块儿学习,一块儿进步!!
若是你以为冰河写的还不错,请微信搜索并关注「 冰河技术 」微信公众号,跟冰河学习高并发、分布式、微服务、大数据、互联网和云原生技术,「 冰河技术 」微信公众号更新了大量技术专题,每一篇技术文章干货满满!很多读者已经经过阅读「 冰河技术 」微信公众号文章,吊打面试官,成功跳槽到大厂;也有很多读者实现了技术上的飞跃,成为公司的技术骨干!若是你也想像他们同样提高本身的能力,实现技术能力的飞跃,进大厂,升职加薪,那就关注「 冰河技术 」微信公众号吧,天天更新超硬核技术干货,让你对如何提高技术能力再也不迷茫!