Sprign In Action<二>
来源:互联网 发布:unity php 编辑:程序博客网 时间:2024/06/16 14:48
二、DI-依赖注入(IOC-控制反转)
创建应用对象之间协作关系的行为叫做装配,这就是依赖注入的本质。
1.Spring的三种装配机制:
1.XML中显式装配;2.在java中显式装配;3.隐式的bean发现机制和自动装配(通过注解的方式)。
《Spring In Action》建议尽可能的使用自动装配,显式的越少越好,但是当你必须要用到显式装配的时候,尽量使用类型安全且比XML更强大的JavaConfig。只有想使用便利的XML命名空间和JavaConfig没有和XML相同的实现时才会使用XML显式装配。
1).自动装配Bean
CD接口
package action.spring.study.second;public interface CompactDisc { void play();}
CD接口的实现类
package action.spring.study.second;import org.springframework.stereotype.Component;@Componentpublic class SgtPeppers implements CompactDisc {private String title="SgtPepper";private String artist="Play SgtPepper Song";@Overridepublic void play() { System.out.println("Playing"+title+"contain"+artist);}}@Component会表名SgtPeppers是组件类并提醒Spring创建Bean,但是Spring的组件扫描默认是不打开的,所以需要我们显式配置一下。下列代码定义了Spring的装配规则,@Configuration的使用暂不考虑,@ComponentScan这个注解能够在Spring中启动组件扫描。如果没有其他设置的话会默认扫描与配置类相同的包,查找@Component注解的类,发现SgtPeppers类被@Component注解Spring会为其自动创建bean。
package action.spring.study.second;import org.springframework.context.annotation.ComponentScan;import org.springframework.context.annotation.Configuration;@Configuration@ComponentScanpublic class CDPlayerConfig {}
如果钟爱XML配置方式可以在XML配置文件中添加Spring context命名空间
<context:component-scan base-package="action.spring.study.second">
测试@ComponentScan扫描后创建bean成功并且不为空,下面是一个简单的JUnit测试代码:
package action.spring.study.second;import static org.junit.Assert.*;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.test.context.ContextConfiguration;import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration(classes=CDPlayerConfig.class)public class CDPlayerTest { @Autowired private CompactDisc cd; @Test public void cdShouldNotBeNull(){ assertNotNull(cd); }}
下面我们将深入讨论@Component和@ComponentScan的详细使用和作用:
Spring应用上下文会给每一个bean一个ID,以上例子中我们没有明确给SgtPeppers指定一个ID,Spring应用上下文默认取名类sgtPeppers(类名第一个字母小写作为ID)。如果想为SgtPeppers设定不同的ID这么写:@Component("sgtPeppersNewName"),还可以用@Named替代@Component。写法和@Component类似:@Named("sgtPeppersNewName")。两者之间存在细微的差异,但是大部分场景可以替换。
@ComponentScan如何不设置其他属性它默认是扫描配置类所在的包,如果为了设置不同的基础包或者有时候我们会将配置类
单独放到一个包中我们需要指定扫描基础包。@ComponentScan("base-package")或者@ComponentScan(basePackages={"base-package1,"base-package2"}),但是这种方式不安全,如果重构代码指定的扫描包可能发生错误,所以经常@ComponentScan(base-package={基础包下的类名1.class,基础包下的类名2.class})。一般的处理方式我们会在基础包中创建空标记接口(就是接口里什么也没有)用来扫描。
在应用程序中如果对象独立的,彼此没有依赖,只需要配置组件扫描既可以了。但是很多对象依赖其他对象完成任务,我们需要一种方法将扫描得到的bean和他们的依赖绑定到一起。
借助@Autowired Spring应用上下文可以寻找bean需要的其他bean并实现自动装配。具体代码如下:
package action.spring.study.second;import org.springframework.beans.factory.annotation.Autowired;public class CDPlayer { private CompactDisc cd; public CompactDisc getCd() {return cd; } @Autowired public void setCd(CompactDisc cd) {this.cd = cd; } @Autowired public CDPlayer(CompactDisc cd ){ this.cd=cd; } public void paly(){ cd.play(); }}在构造方法上使用@Autowired和在setter方法上使用@Autowired的效果是一样的。不光如此我们还可以在CDPlayer这个类的其他方法上添加@Autowired,这三种方法都可以实现自动装配,没有任何区别。在启动Spring应用上下文时如果没有对应的Bean装配到CDPlayer的bean中,Spring会抛出相应的异常。所以可以这样写@Autowired(required=false),这样Spring就会在启动Spring应用上下文的时候就会让这个bean处于未装配状态,如果代码没有对其进行null判断就会产生:NullPointerException。如果装配的时候有多个bean满足要求,比如:CompactDisc接口的实现类有好几个,Spring在启动Spring应用上下文的时候就会报异常,具体的解决办法后面会学到,暂且搁置。@Inject是java依赖注入规范,该规范定义了@Named注解。@Inject和@AutoWired注解存在细微的差异,但是大部分场合和替换使用。
验证自动装配:
package action.spring.study.second;import static org.junit.Assert.*;import org.junit.Rule;import org.junit.Test;import org.junit.contrib.java.lang.system.StandardOutputStreamLog;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.test.context.ContextConfiguration;import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration(classes=CDPlayerConfig.class)public class CDPlayerTest2 { @Rule public final StandardOutputStreamLog log = new StandardOutputStreamLog(); @Autowired private MediaPlayer player; @Autowired private CompactDisc cd; @Test public void cdShouldNotBeNull(){ assertNotNull(cd); } @Test public void paly(){ player.play(); assertEquals("PlayingSgtPeppercontainPlay SgtPepper Song",log.getLog()); }}
2).通过Java代码装配Bean
虽然通过组件扫描和自动装配实现Spring的自动化配置是极为推荐的方式,但是有时候这种方式行不通:比如将第三方库的组件装配到你的应用.....而JavaConfig相对于XML来说更强大、类型安全并且重构友好,毕竟它本身就是java代码。虽然JavaConfig本身就是java代码,但是毕竟它是配置代码,它与一般的业务逻辑代码不同,所以通常都是将它单独放在一个包内。
在之前的实例中我们配置了CDPlayerConfig的java代码,创建JavaConfig文件的关键在于@Configuration主键,这个注解表名这个类配置类。但是我们去除@ComponentScan这个注解后我们再运行CDPlayerTest就会报BeanCreationException异常。下面我们就来解决这个问题:
package action.spring.config;import org.springframework.context.annotation.Bean;import action.spring.study.second.CompactDisc;import action.spring.study.second.SgtPeppers;@Configurationpublic class JavaConfig { @Bean(name="sgtPeppersName")public CompactDisc sgtPeppers(){return new SgtPeppers();}@Bean(name="sgtPeppersNewName")public CompactDisc sgtPeppersNewName(){return new SgtPeppers();}}通过@Bean(name="Bean的ID名")来实现JavaConfig的显示装配Bean。CDPlayer依赖CompactDisc类,最简单的方法就是调用JavaConfig配置类中生成CompactDisc类的方法,代码如下:
@Bean(name="cDPlayer")public MediaPlayer cDPlayer(){ return new CDPlayer(sgtPeppers());}
乍一看好像是直接调用的sgtPeppers方法生成的CompactDISC的Bean,实际上因为@Bean的存在所以Spring拦截所以对这个方法的调用,并且确保直接返回该方法创建的bean。@Bean(name="sgtPeppersName")public CompactDisc sgtPeppers(){ return new SgtPeppers();}@Bean(name="cDPlayer")public MediaPlayer cDPlayer(){ return new CDPlayer(sgtPeppers());}@Bean(name="cDPlayer2")public MediaPlayer cDPlayer2(){ return new CDPlayer(sgtPeppers());}
在实际生活里一个CD是不能同时放到两个播放器里的,这也是正常java方法调用的时候的实际意义。但是JavaConfig配置类中默认情况下Spring中的bean都是单例的,所以在这两个CDPlayer bean创建完全相同的SgtPeppers实例。
@Bean(name="cDPlayer")public CDPlayer cDPlayer(){return new CDPlayer(sgtPeppers());}@Bean(name="cDPlayer2")public CDPlayer cDPlayer2(CompactDisc cd){return new CDPlayer(cd);}@Bean(name="cDPlayer3")public CDPlayer cDPlayer3(CompactDisc cd){CDPlayer cdPlayer = new CDPlayer(cd);cdPlayer.setCd(cd);return cdPlayer;}这三种方法都可以为CDPlayer装配所依赖的CompactDisc的bean。
3).通过XML装配bean
俗话说“工欲善其事,必先利其器”,在学习之前我们现在eclipse上安装一个插件:spring-tool-suite。
(1)登陆网站:https://spring.io/tools/sts/all
(2)查看自己的eclipse版本:Help-->About Eclipse-->Installation Details-->Eclipse Platform
(3)将下图中的与eclipse版本对应的spring-tool-suite的网址复制到eclipse的Help-->Install New Software-->work with中并回车,勾选与Spring相关的插件,同时Contact all update。。。这个选项不要勾选。
(4)一路next即可。
使用XML显式装配Bean需要创建一个XML文件,在这个文件的顶部声明多个XML模式文件,这些模式文件定了配置Spring的XML标签。<beans>是配置文件的根元素,我们可以简单的声明一个简单的bean:<bean class="包名.类名"></bean>。如果要定义它的ID:<bean id="ID编号" class="包名.类名"></bean>,如果不定义id的话,默认就是包名.类名。以上面的例子为例使用XML配置文件如何将被依赖对象装配到需要它的bean中:1)使用构造器注入:<bean id="" class=""><constructor-arg ref="被依赖对象的ID名"></bean>在Spring3.0之后可以通过c-命名空间来实现,c:为前缀,-ref为后缀,cd是构造器参数的名字,两者存在微小的差别,前者繁琐但是功能相对多些后者相反。
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:c="http://www.springframework.org/schema/c"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:aop="http://www.springframework.org/schema/aop"xmlns:tx="http://www.springframework.org/schema/tx"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd"><bean id="cDPlayer" c:cd-ref="compactDisc" class="action.spring.study.second.CDPlayer"></bean><bean id="cDPlayer" class="action.spring.study.second.CDPlayer"> <constructor-arg ref="compactDisc"></constructor-arg></bean><bean id="compactDisc" c:title="判断输了" c:artist="输了" class="action.spring.study.second.BlankDisc"></bean></beans>如果默认要将构造器参数设置为空可以这么写<constructor-arg ><null/></constructor-arg>,如果是剩余的一个构造器参数是一个集合可以这么写:
<constructor-arg> <list> <value></value> <value></value> <ref bean="bean的id名字"/> </list></constructor-arg>如果是剩余的一个构造器参数是一个Set可以这么写:
<constructor-arg> <set> <value></value> <value></value> <ref bean="bean的id名字"/> </set></constructor-arg>上面事例中我们讨论的是xml显式装配bean中通过构造器注入,下面我们通过setter注入(也叫属性注入):
<bean id="cDPlayer" class="action.spring.study.second.CDPlayer"> <property name="cd" ref="compactDisc"/></bean>什么时候使用构造器注入?什么时候使用setter注入呢?对于强依赖使用构造器注入,对于可选性依赖使用属性注入。强依赖我的理解就是如果这个bean的某个方法依赖于另外一个bean,那么这个就是强依赖,我们使用构造器注入,反之使用属性注入。对于属性注入我们可以使用p-命名空间,代码如下:
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:p="http://www.springframework.org/schema/p"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:aop="http://www.springframework.org/schema/aop"xmlns:tx="http://www.springframework.org/schema/tx"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd"><bean id="cDPlayer" class="action.spring.study.second.CDPlayer" p:cd-ref="compactDisc" p:title="String类型的属性值"></bean></beans>但是这种方式我们是没法来装配集合的,所以xml提供了util-命名空间,代码如下:
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:c="http://www.springframework.org/schema/c"xmlns:p="http://www.springframework.org/schema/p"xmlns:util="http://www.springframework.org/schema/util"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:aop="http://www.springframework.org/schema/aop"xmlns:tx="http://www.springframework.org/schema/tx"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd"><bean id="compactDisc" c:title="判断输了" c:artist="输了" class="action.spring.study.second.BlankDisc"></bean><bean id="compactDisc2" p:title="title value" p:listValue-ref="firstList" class="action.spring.study.second.BlankDisc"></bean><util:list id="firstList"> <value>one</value> <value>two</value></util:list></beans>4)导入和混合配置
一、在JavaConfig中引入XML配置
假设我们的JavaConfig太笨重,里面定义的bean颇多。我们上面的例子中JavaConfig中只有两个bean,我们幻想已经很多的情况下,一种处理方式是将其中一个bean比如BlankDisc从CDPlayerConfig中分离开定义到自己的JavaConfig中-CDConfig。一种方法就是在CDPlayerConfig中使用@Import注解导入CDConfig。具体使用方法:
@Import(CDConfig.class) public class CDPlayerConfig{ }或者在新的JavaConfig中使用@Import将两个config类导入进来:
@Import({CDConfig.class,CDPlayerConfig.class}) public class NewConfig{ }二、在XML配置中引入JavaConfig
同上,如果我们要拆分XML配置文件我们只需要加入如下标签:<import resource="cd-spring.xml"/>
如果想引入JavaConfig添加如下标签:<bean class="JavaConfig全名"/>
综述:不管使用XML还是JavaConfig通常会创建一个根配置,这样就可以将所有的JavaConfig和XML组合起来。
如果BlankDisc配置在cd-spring.xml中呢?我们可以使用@ImportResource("cd-spring.xml")
- Sprign In Action<二>
- jgraph in action(二)
- 《NHibernate in Action》读书笔记【二】
- Spring in Action读书笔记 (二)
- spring in action解读二
- AJAX IN ACTION 学习笔记(二)
- JUnit in action学习笔记(二)
- iBATIS In Action:什么是iBATIS(二)
- 试译《Ajax in Action》第四部分(二)
- Struts2+Spring+Hibernate In Action(二)
- mybatis入门教程(mybatis in action)(二)
- Solr In Action 中文版 第一章 (二)
- Liferay in action 学习笔记(二)
- Netty in Action (二十五) 自我总结
- In Action
- In Action
- JSF in Action读书笔记(二)自定义UIComponent
- ejb3 in action 学习笔记之二_MDB
- salesforce数据安全篇
- Java 9的14个新特性总结
- 美团推荐算法实践
- 2017.5.9 货车运输 思考记录
- Angular开发(九)-关于响应式基本认识
- Sprign In Action<二>
- SQL中的xp_cmdshell拒绝访问
- hibernate映射many-to-one 属性
- jQuery实现5秒倒计时
- "ToString"方法没有任何重载采用“1个参数”
- GDB Internals.
- Linux ffmpeg视频截图,C中操作JSON数据
- css3d-魔方
- MTK编译差分包问题OSError: [Errno 2] No such file or directory