1.准备工作

请在github上下载源码结合文章阅读,效果更佳

在创建了SpringBoot项目后,我们首先需要开启组件扫描,如下代码所示。

 
@Configuration //扫描指定包目录 @ComponentScan(basePackages="com.wjc") public class BeanConfig { } 

声明一个测试Bean的接口,全文的主要内容都是通过此接口的实现类完成的

如果想学习Java工程化、高性能及分布式、深入浅出。微服务、Spring,MyBatis,Netty源码分析的朋友可以加我的Java高级交流:854630135,群里有阿里大牛直播讲解技术,以及Java大型互联网技术的视频免费分享给大家。

 
package com.wjc.spring.bean; public interface Bird { void fly(); void feed(); void twitter(); void changeTwiter(); } 

2.自动装配

自动装配是最常见的Bean装配形式。

我们首先写一个Bird接口的实现类来展示自动装配,只需一个“@Component”注解即可完成。

 
package com.wjc.spring.bean.impl; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; import com.wjc.spring.bean.Bird; //这是知更鸟 @Component public class Robin implements Bird { private String flyStr ="知更鸟起飞"; private String feedStr = "不想吃东西"; private String twiterStr = "啊啊啊"; @Override public void fly() { System.out.println(flyStr); } @Override public void feed() { System.out.println(feedStr); } @Override public void twitter() { System.out.println(twiterStr); } @Override public void changeTwiter() { } } 

在测试时,我们只需要使用“@Autowired”注解,就可以拿到对应的对象了

通过Junit可以测试装配是否完成

如果想学习Java工程化、高性能及分布式、深入浅出。微服务、Spring,MyBatis,Netty源码分析的朋友可以加我的Java高级交流:854630135,群里有阿里大牛直播讲解技术,以及Java大型互联网技术的视频免费分享给大家。

 
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes=BeanConfig.class) public class BeanTest { @Autowired private Bird bird; //测试1,查看是否自动装配了知更鸟 //此时bean.impl只有robin @Test public void BeanTest1() { assertNotNull(bird); } } 

3.处理自动装配的歧义性(@Qualifier)

试想如果我有2个Bird接口的实现类,spring在装配时是否会因为不知道具体需要哪个实现类而报错?

此时声明一个“Parrot”,也实现bird接口,运行test方法会如何?

 
package com.wjc.spring.bean.impl; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; import com.wjc.spring.bean.Bird; //这是鹦鹉 @Component public class Parrot implements Bird { private String flyStr ="鹦鹉起飞"; private String feedStr = "啥都吃"; private String twiterStr = "说人话"; @Override public void fly() { System.out.println(flyStr); } @Override public void feed() { System.out.println(feedStr); } @Override public void twitter() { System.out.println(twiterStr); } @Override public void changeTwiter() { twiterStr = "你好你好"; } } 

运行结果如下:

 
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'com.wjc.spring.test.BeanTest': Unsatisfied dependency expressed through field 'bird'; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.wjc.spring.bean.Bird' available: expected single matching bean but found 5: parrot,quail,robin,Cuckoo1,Cuckoo2 at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:596) 

可以看到,由于Spring并不知道应该将哪一个实现类注入到bird中,报出了 “UnsatisfiedDependencyException”,我们可以通过注解“@Qualifier("parrot")”来解决此问题

 
//这是鹦鹉 @Component @Qualifier("parrot") public class Parrot implements Bird { 

在获取实现类时使用如下方式,即可获取到自己想要的对象实例了

 
@Autowired @Qualifier("parrot") private Bird parrot; //添加@Qualifier("parrot")来解决声明问题 @Test public void BeanTest3() { // 此时鹦鹉添加了@Primary parrot.fly(); assertNotNull(parrot); } 

4.Bean的作用域

已知Spring默认是单例模式,但在多线程高并发的情况下,单例模式其实未必是最佳选择,如果线程A将Bean赋了值,而此时线程B拿取了被A赋值的对象,并返回了对应的结果,此时是不是会出现B返回了预料之外的结果?

本文简单讨论一下原型模式下Bean的传递,和会发生的问题,具体的各自作用域请百度“spring作用域”

已知Spring作用域如下:singleton / prototype / request / session /global session

我们来看一下如下代码,一个原型模式的对象

 
package com.wjc.spring.bean.impl; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; import com.wjc.spring.bean.Bird; //这是鹌鹑 //这个使用原型模式 @Component @Qualifier("Quail") @Scope("prototype") public class Quail implements Bird { private String flyStr ="鹌鹑起飞"; private String feedStr = "鹌鹑想吃啥就吃啥"; private String twiterStr = "鹌鹑不知道怎么叫"; @Override public void fly() { // TODO Auto-generated method stub System.out.println(flyStr); } @Override public void feed() { // TODO Auto-generated method stub System.out.println(feedStr); } @Override public void twitter() { // TODO Auto-generated method stub System.out.println(twiterStr); } public void changeTwiter() { twiterStr = "我大鹌鹑今天就是饿死。。。。"; } } 

看下在TEST时他的表现如何:

 
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes=BeanConfig.class) public class BeanTest3 { @Autowired @Qualifier("Quail") private Bird bird; @Autowired @Qualifier("Quail") private Bird bird2; //测试原型模式与单例的区别 @Test public void BeanTest1() { bird.twitter(); bird.changeTwiter(); bird.twitter(); bird2.twitter(); bird2.changeTwiter(); bird2.twitter(); } } 

运行结果:

 
鹌鹑不知道怎么叫 我大鹌鹑今天就是饿死。。。。 鹌鹑不知道怎么叫 我大鹌鹑今天就是饿死。。。。 

spring确实将此Bean对象变成了原型模式。那么作用域是否就这么简单的完成了?

我们看一下如下代码

 
@Service public class BirdServiceImpl implements BirdService { @Autowired @Qualifier("Quail") private Bird bird; public void ScopTest() { bird.twitter(); bird.changeTwiter(); bird.twitter(); } } 

运行测试类:

 
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes=BeanConfig.class) public class ServiceTest { @Autowired private BirdService birdService; @Autowired private BirdService birdService2; //测试在Service上添加和不添加@Qualifier("Quail")时调用的Bean的区别 @Test public void ServiceTest2() { birdService.ScopTest(); birdService2.ScopTest(); } } 

运行结果:

 
鹌鹑不知道怎么叫 我大鹌鹑今天就是饿死。。。。 我大鹌鹑今天就是饿死。。。。 我大鹌鹑今天就是饿死。。。。 

????????原型模式失效了????

为什么会发生这种情况?因为在此场景下,“BirdServiceImpl”是单例模式的,对Bean的操作不可避免的变成了单例的,如果添加如下代码结果就会完全不一样

 
@Service @Scope("prototype") public class BirdServiceImpl implements BirdService { @Autowired @Qualifier("Quail") private Bird bird; public void ScopTest() { bird.twitter(); bird.changeTwiter(); bird.twitter(); } } 

再次运行时:

 
鹌鹑不知道怎么叫 我大鹌鹑今天就是饿死。。。。 鹌鹑不知道怎么叫 我大鹌鹑今天就是饿死。。。。 

假设“ServiceTest”方法为Control层,“BirdServiceImpl”方法为Service层,“Quail”为Bean,在实际应用时,应该考虑Scop注解是否会可以成功生效。

如下为测试后的结果

 
//当Service上有@Scope("prototype"),Bean上有@Scope("prototype")时 返回不同对象 //当Service上有@Scope("prototype"),Bean上无@Scope("prototype")时 返回相同对象 //当Service上无@Scope("prototype"),Bean上有@Scope("prototype")时 返回相同对象 //当Service上无@Scope("prototype"),Bean上无@Scope("prototype")时 返回相同对象 

5.注入式声明Bean

在上述代码中,我都是通过硬编码的形式在输入一些内容的,那么能否通过读取配置文件的方式完成输出内容呢?(实际运用场景:获取数据库连接对象Session)

我们首先定义一个对象,可以看到我没有添加任何注解,因为此对象不需要在这里进行装配!

 
package com.wjc.spring.bean.impl; import com.wjc.spring.bean.Bird; //这是杜鹃 public class Cuckoo implements Bird { private String flyStr = "fly" ; private String feedStr = "feed"; private String twiterStr = "twiter"; public Cuckoo(String flyStr, String feedStr, String twiterStr) { super(); this.flyStr = flyStr; this.feedStr = feedStr; this.twiterStr = twiterStr; } @Override public void fly() { // TODO Auto-generated method stub System.out.println(flyStr); } @Override public void feed() { // TODO Auto-generated method stub System.out.println(feedStr); } @Override public void twitter() { // TODO Auto-generated method stub System.out.println(twiterStr); } @Override public void changeTwiter() { // TODO Auto-generated method stub twiterStr = "杜鹃"; } } 

如果想学习Java工程化、高性能及分布式、深入浅出。微服务、Spring,MyBatis,Netty源码分析的朋友可以加我的Java高级交流:854630135,群里有阿里大牛直播讲解技术,以及Java大型互联网技术的视频免费分享给大家。

我们将Config改造一下,由他来负责装配对象

 
package com.wjc.spring.config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; import org.springframework.core.env.Environment; import com.wjc.spring.bean.impl.Cuckoo; @Configuration //扫描指定包目录 @ComponentScan(basePackages="com.wjc") @PropertySource("classpath:Cuckoo.properties") public class BeanConfig { //开启组件扫描 //获取资源 @Autowired private Environment env; //通过配置文件装配Cuckoo @Bean(name="Cuckoo1") public Cuckoo getbird() { return new Cuckoo(env.getProperty("flyStr","fly"), env.getProperty("feedStr","feed"), env.getProperty("twiterStr","twiter")); //return new Cuckoo("fly","feed", "twiter"); } @Bean(name="Cuckoo2") public Cuckoo getbird2() { return new Cuckoo("fly","feed", "twiter"); } } 

可以看到我声明了2个"Cuckoo"对象实例,分别叫“Cuckoo1”,“Cuckoo2”

使用Test方法来执行一下

 
package com.wjc.spring.test; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import com.wjc.spring.bean.impl.Cuckoo; import com.wjc.spring.config.BeanConfig; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes=BeanConfig.class) public class BeanTest4 { @Autowired @Qualifier("Cuckoo2") private Cuckoo Cuckoo; @Autowired @Qualifier("Cuckoo1") private Cuckoo Cuckoo1; //测试通过配置文件装配Bean @Test public void BeanTest1() { Cuckoo1.fly(); Cuckoo1.feed(); Cuckoo1.twitter(); Cuckoo.fly(); Cuckoo.feed(); Cuckoo.twitter(); } } 

执行结果

cuckoo fly cuckoo feed cuckoo twiter fly feed twiter 

可以看到成功的声明了对象。

欢迎工作一到八年的Java工程师朋友们加入Java高级交流:854630135

本群提供免费的学习指导 架构资料 以及免费的解答

不懂得问题都可以在本群提出来 之后还会有直播平台和讲师直接交流噢

03-05 13:08