Spring实战拆书--SpringBean
代码源码地址:https://github.com/wujiachengsh/springbeandemo
概述:本章将讲解spring对于bean的管理方案。
目录:
- 准备工作
- 自动装配
- 处理装配歧义性
- bean的作用域
- 注入式声明bean
代码环境:
- sts
- jdk1.8
- spring4
1.准备工作
请在github上下载源码结合文章阅读,效果更佳
在创建了springboot项目后,我们首先需要开启组件扫描,如下代码所示。
@configuration //扫描指定包目录 @componentscan(basepackages="com.wjc") public class beanconfig { }
声明一个测试bean的接口,全文的主要内容都是通过此接口的实现类完成的
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可以测试装配是否完成
@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)
代码源码地址:https://github.com/wujiachengsh/springbeandemo
试想如果我有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 = "杜鹃"; } }
我们将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
可以看到成功的声明了对象。
本文章为在下在查看spring实战一书和工作上发生过的问题结合来完成的。希望各位看官指点错误和不合理的地方。
代码源码地址:https://github.com/wujiachengsh/springbeandemo
上一篇: 剑指offer第二天
下一篇: C#图片处理3种高级应用