继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

IDEA开发环境下的Spring依赖注入详解

恩言
关注TA
已关注
手记 41
粉丝 316
获赞 3231
前言
       这篇文章并不是对Spring内容整体的泛泛而谈,而是以详略得当,风趣幽默的方式将Spring最基础的知识——依赖注入向大家娓娓道来。这篇文章的一个特色是使用当前流行的IDEA作为开发工具,里面详细的讲述了如何利用IDEA简化我们的Spring开发。由于在这里图片上传非常痛苦(我必须把每个图片都一一上传),所以我强烈推荐您联系这个邮箱:879383400@qq.com索要word电子稿或者jar包、源码。我会在第一时间回复您。
      这篇文章的案例及结构参考了Craig Walls的著作《Spring in action》,这是一本非常好的书,如果这篇文章有什么吸引你的地方然而描述的不够详细,相信你总能从Craig的著作中找到你想要的。

第一部分 Spring的核心
1 Spring之旅
1.1 简化java开发
Spring是一个开源框架,最早由Rod Johnson创建,任何java程序都能在简单性、可测试性和松耦合性等方面从Spring中获益。Spring使用Bean表示应用组件(说白了,Bean就是实现了特定功能java类),我们用Spring Bean表示装载在Spring容器中的java Bean。
为降低java开发的复杂性,Spring采取了以下四种关键策略:
1.基于POJO的轻量级和最小入侵性编程
2.通过依赖注入和面向接口实现松耦合
3.基于切面和惯例进行声明式编程
4.通过切面和模板减少样板式代码
1.2 容器
容器是Spring框架的核心。Spring容器创建对象、装配它们、配置它们管理它们的整个生命周期,从创建到销毁。并不存在单一的Spring容器,Spring自带了几种容器实现,可分为两种类型:Bean工厂和应用上下文。其中应用上下文是给予Bean工厂之上构建的,提供例如从属性文件解析文本信息等面向应用的服务,因此应用上下文要比Bean工厂更受欢迎。所以,我们在接下来的学习中只与应用上下文共事。
1.3 IDEA下创建Spring工程与引入相关的工具包
1.3.1 创建Spring项目
File—>New—>Project 新建Spring项目

点击Next,因为我们不需要模板,所以取消掉Creat project from template前的对勾。然后一直点击Next直到Finish为止。这样我们就创建了一个Spring项目。

1.3.2 引入相关工具包
新建一个名为lib的文件夹,将所需要的工具包(请自行从网上下载Spring的相关jar包)放在该文件夹下,然后将lib文件夹放在项目之中。
点击File—>project Structure—>Libraries的“+”号将lib文件夹创建为Library。

在File—>project Structure—>Modules板块引入新创建的Library即可。

1.4 应用上下文
1.4.1 Spring自带的几种应用上下文
(1)ClassPathXmlApplicationContext——从类路径的XML配置文件中加载上下文的定义,把应用上下文定义文件当做类资源。
例如:
ApplicationContext context= new ClassPathApplicationContext(“foo.xml”);
(2)FileSystemXmlApplicationContext——读取文件系统下的XML配置文件并加载上下文定义
例如:
ApplicationContext context= new FileSystemXmlApplicationContext (“c:/foo.xml”);
(3)XmlWebApplicationContext——读取Web应用下的XML配置文件并装载至上下文。
1.4.2 在应用上下文中获取Bean
我们可以通过应用上下文对象的getBean()方法来根据Bean的id获得Spring容器中的Bean(假设我们已经将一些Bean放在了容器中,至于如何放置则是我们下一章要学习的内容)。要注意getBean()返回的是一个Object类型的对象,需要进行转换,例如:
Performer kenny=(Performer)applicationContext.getBean("kenny");
1.4.3 将多个配置文件集成到一个应用上下文
如果项目存在多个Spring XML配置文件,在不同的配置文件中声明的bean又需要相互调用,那么需要将他们封装到同一个上下文中:Spring上下文的构造函数中可以以字符串数组作为参数传入,其中数组的每一个元素就是要加载的配置文件的相应路径。
String[]configFiles={"springIdol2/config/stuffByAnnotation.xml","springIdol2/config/stuffByAnnotation.xml"};
//将需要加载的文件统一成一个数组,所有XML文件中声明的Bean才共用一个spring上下文
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(configFiles);
1.4.4 IDEA对Spring上下文的支持

我们每新建一个Spring XML配置文件,IDEA就会有心的问一下要不要把这个文件加载到我们的Spring上下文中,如果你已经想好在加载配置文件的时候同时加载新创建的这个配置文件,我推荐你选择同意,这样会得到IDEA Spring帮手更多友善的提示。但如果你需要做测试或者其他用途,不想让两个配置文件中的内容相互影响,就不要理会Spring的这个选项,或者选择第二项,否则即使你不加载这个XML文件,IDEA也会认为它在Spring上下文中(这就会导致有的时候程序本身是没有问题的但是IDEA总是提示程序有错)。
2 Spring依赖注入
2.1 装配Bean
在Spring中,对象无需自己负责查找或创建与其相关联的对象,相反容器负责把需要相互协作的对象的引用赋予各个对象。创建应用对象之间协作关系的行为通常被称为装配,这也是依赖注入的本质。
2.1.1 新建Spring配置文件
IDEA会自动为你生成文件头,当我们需要一个新的命名空间时,IDEA也会自动的为你在<beans>内引入相关的声明。

2.1.2 创造一个Bean
1.创造一个简单的Bean(该Bean没有成员变量,只有默认的构造器)
例如:
<bean id="mySong" class="springIdol2.stuffImpl.Sonnet29"/>
2.通过构造器注入(该Bean拥有成员变量并在构造器进行初始化)
例如:
<bean id="jack" class="springIdol.stuffImpl.PoeticJuggler">
<!--如果不手动传参则使用默认的构造方法-->
<!--我们使用value为简单类型的参数赋值-->
<constructor-arg value="15"/>
<!--使用ref为pojo类型的参数赋值-->
<constructor-arg ref="sonnet29"/>
</bean>
注意:前两种方法都是相当于调用类的构造器来创建实例。
3.工厂方法创建Bean(该Bean作为整个项目的单例对象模式,没有公开的构造方法)
例如:
类定义部分
public class Stage {
private Stage(){
System.out.println("你好");
}
private static class StageSingletonHolder{
static Stage instance=new Stage();
}
public static Stage getInstance(){
return StageSingletonHolder.instance;
}
}
xml配置部分
<bean id="theStage" class="springIdol.stuffImpl.Stage" factory-method="getInstance"/>
<bean>元素的factory-method属性,允许我们调用一个指定的静态方法。从而代替构造方法来创建一个类的实例。
2.1.3 Bean的作用域
所有的Spring Bean默认都是单例(这里的单例不等同于单例模式的单例,因为Spring有关单例的概念仅限于Spring上下文范围内),当容器分配一个Bean时,无论是通过装配还是调用容器的getBean()方法,它总是返回Bean的同一个实例。
我们可以通过设置<bean>元素的scope属性(默认值为singleton)为prototype来使我们每次请求时都获得惟一的Bean实例。例如:
<bean id="ticket" class="springIdol.stuffImpl.Ticket" scope="prototype"/>
2.1.4 初始化和销毁Bean
2.1.4.1 init-method方法和destroy-method方法
<bean>的init-method属性指定了在初始化Bean之后要调用的方法,类似的,destory-method属性指定了Bean从容器中移除之前要调用的方法。注意:所要调用的方法并非创造这个bean的方法。
2.1.4.2 默认的init-method方法和destroy-method方法
如果上下文定义中的很多Bean拥有相同名字的初始化和销毁方法,我们可以在<beans>元素中使用default-init-method和default-destroy-method方法为应用上下文所有的Bean设置一个共同的初始化/销毁方法。
2.2 注入Bean属性
通常,JavaBean的属性是私有的,同时拥有一组存取器方法,以setXXX()和getXXX()形式存在。Spring可以借助属性的set方法来配置属性的值,以实现setter方式的注入。注意:这同时意味着如果Bean的定义中某属性不存在setter方法,那么Spring将无法通过这种方式对该属性进行注入。
2.2.1 注入简单值与其他bean的引用
<bean id="kenny" class="springIdol.stuffImpl.Instrumentalist">
<property name="song" value="你是我的家"/>
<!--Spring会根据属性类型自动对传入的值进行转化-->
<property name="age" value="25"/>
<property name="instrument" ref="violin"/>
</bean>

<bean id="violin" class="springIdol.stuffImpl.Violin"/>

2.2.2 注入内部Bean
<!--但这样同一把小提琴会被多位选手所公用,因为spring会将其默认视为单例,当然可以每来一位选手
主办方就为之提供一把新的小提琴(scope="prototype"),但更多演奏者倾向于自己带自己的乐器:
内部类的方式定义bean:-->
<!--内部的bean往往没有id,因为我们永远不会通过名字来引用内部Bean。这也突出了内部Bean的最大缺点
就是无法复用。内部bean只适用于一次注入,而且不能被其他bean引用-->
<!—内部类会影响Spring XML的可读性-->
<bean id="kenny" class="springIdol.stuffImpl.Instrumentalist">
<property name="song" value="你是我的家"/>
<property name="age" value="25"/>
<property name="instrument">
<bean class="springIdol.stuffImpl.Violin"/>
</property>
</bean>

2.2.3 使用Spring的命名空间p装配属性
Spring提供了另一种Bean属性的装配方式——使用命名空间p来装配属性:
<!--还可以通过引入命名空间的方式这样创建我们的演奏者kenny-->
<bean id="kenny" class="springIdol.stuffImpl.Instrumentalist"
p:song="你是我的家"
p:instrument-ref="violin"

</bean>
<!--name-ref告知Spring装配一个引用而不是字面值-->
Spring会自动在<beans>元素中为你引入:
xmlns:p="http://www.springframework.org/schema/p"
选择<property>还是命名空间p完全取决于你,因为它们是完全等价的。
2.2.4 装配集合
2.2.4.1 装配List、Set和Array
<bean id="hank" class="springIdol.stuffImpl.OneManBand">
<property name="instruments">
<!--Bean的属性类型为数组类型或Collection接口的任意实现都可以使用<list></list>元素-->
<!--无论是<list>还是<set>都可以用来装配类型为Collection的任意实现或者数组的属性-->
<list>
<ref bean="violin"/>
<ref bean="cymbal"/>
</list>
</property>
</bean>
2.2.4.2 装配Map集合
<property name="instruments">
<map>
<entry key="VIOLIN" value-ref="violin2"/>
<entry key="CYMBAL" value-ref="cymbal"/>
</map>
</property>
<map>中的<entry>元素由一个键和一个值组成、键和值可以是简单类型也可以是其他Bean的引用。
属性 用途
key 指定map中entry的键为String
key-ref 指定map中entry的键为Spring上下文中其他Bean的引用
value 指定map中entry的值为String
value-ref 指定map中entry的值为Spring上下文中其他Bean的引用
2.2.4.3 装配Properties集合
XML配置文件:
<property name="instruments">
<props>
<prop key="VIOLIN">小提琴</prop>
<prop key="CYMBAL">锣鼓</prop>
</props>
</property>
遍历properties集合:
public void perform() {
Iterator<Map.Entry<Object,Object>> iterator=instruments.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<Object, Object> entry = iterator.next();
Object key = entry.getKey();
Object value = entry.getValue();
System.out.println("key :" + key);
System.out.println("value :" + value);
}
}
2.2.4.4 装配空值
在某些情况下,如果我们不能完全确定属性值会为null,那么最好显式的为该属性装配一个null值。
<property name="instruments">
<null/>
</property>
2.3 使用表达式装配
如果我们为属性装配的值只有在运行期才能知道的情况下,则需要Spring表达式(Spring Expression Language ,SPEL)的帮助。SPEL表达式在#{}中书写。
2.3.1 最简单的字面值
<!—使用表达式装配简单的字面值-->
<property name="age" value="#{1e4}"/>
<property name="song" value="#{'《Greensleeves》'}"/>
2.3.2 引用Bean及Bean的方法、属性
<bean id="carl" class="springIdol.stuffImpl.Instrumentalist">
<property name="song" value="#{kenny.song}"/>
<property name="instrument" value="#{violin}"/>
</bean>
甚至还可以使用Bean的方法获得属性、对获得的属性使用额外的方法进行渲染:
<property name="song" value="#{songSelector.getSong().toUpperCase()}"/>
但是如果songSelector.getSong()返回值为空,由于它要调用toUpperCase()方法,所以就会抛出空指针异常。为了避免这种情况,我们使用“.?”代替“.”表示如果不为空则执行后面的方法。
2.3.3 操作类
在SPEL中,使用T()运算符会调用类作用域的方法和常量
<property name="age" value="#{T(java.lang.Math).random()}"/>
2.3.4 在SpEL值上执行操作
运算符类型 运算符
算术运算 +、-、*、/、%(求余运算)、^(乘方)
关系运算 lt、gt、eq、le(小于等于)、ge(大于等于)
逻辑运算 and、or、not
条件运算 ?:(ternary)、?:(Elvis)
正则表达式 matches
2.3.4.1 算术运算
“+”运算符既可以进行加法运算还可以执行字符串的链接
2.3.4.2 关系运算与逻辑运算
关系运算会返回布尔值;逻辑运算用来组合关系运算
<property name="qualification" value="#{1 eq 1 and 5 gt 4}"/>
2.3.4.3 条件表达式
<property name="instrument" value="#{songSelector.getSong()=='《Hey Jude》'?violin:cymbal}"/>

另一个常见的三元运算符使用场景是检查一个值是否为null。如果为null,则装配
一个默认值。
<property name="song" value="#{kenny.song!=null?kenny.song:'Greensleeves'}"/>
但是kenny.song的引用重复出现了两次,SPEL提供了三目运算符的变体来简化表达式:
<property name="song" value="#{kenny.song?:'Greensleeves'}"/>
2.3.5 在SPEL中筛选集合
<?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:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">

<!-- <util list>创建了一个list类型的Bean-->
<util:list id="cities">
    <bean class="springIdol.stuffImpl.City" p:name="Chicago" p:state="IL" p:population="10"/>
    <bean class="springIdol.stuffImpl.City" p:name="NewYork" p:state="Bl" p:population="100"/>
    <bean class="springIdol.stuffImpl.City" p:name="Jal" p:state="NM" p:population="20"/>
</util:list>

<bean id="city" class="springIdol.Initial.Initial4">
    <!--<property name="chosenCity" value="#{cities[1]}"/>-->
    <property name="chosenCity" value="#{cities[T(java.lang.Math).random()*cities.size()]}"/>
    <!-- .?[]用来判断属性满足某条件
         .![]用来映射集合的属性为一个新的集合-->
    <property name="bigCities" value="#{cities.?[population gt 25 or population lt 15].![name+','+state]}"/>
</bean>

</beans>
3 最小化Spring XML配置
Spring提供了几种技巧可以帮助我们减少XML的配置数量。
1.自动装配:有助于减少甚至消除配置<property>元素和<constructor-arg>元素,让Spring自动识别如何装配Bean的依赖关系。
2.自动检测:比自动装配更进了一步,让Spring能够自动识别哪些类需要被配置成Spring Bean,从而减少对<bean>元素的使用。
当自动装配和自动检测一起使用的时候,他们可以显著地减少Spring的XML配置数量。
3.1 自动装配Bean属性
我们通过设置<bean>的autowire属性来让Spring帮助我们自动装配这个Bean的属性。值得注意的是,Spring为这些属性自动装配的前提是这些属性有对应的setter方法或在构造器中对这些属性进行了初始化,再次强调,如果你想为非构造器里初始化的属性注入值,那么它必须拥有setter方法,即使这些属性是公有的。
自动装配类型 作用
byName 把与Bean属性具有相同名字的(id)的Bean自动装配到Bean的对应属性中,如果未匹配到,则不装配。
byType 把与Bean属性具有相同类型的Bean自动装配到Bean的对应属性中,如果未匹配到,则不装配。
constructor 把与Bean的构造器入参具有相同类型的其他Bean自动装配到Bean构造器的对应入参中。
autodetect 首先尝试使用constructor进行自动装配,如果失败,再尝试使用ByType进行自动装配。
3.1.1 byName自动装配
<bean id="mySong" class="springIdol2.stuffImpl.Sonnet29"/>
<bean id="instrument" class="springIdol2.stuffImpl.Violin"/>
<!--通过autowire属性,Spring将自动为你装配bean-->
<bean id="kenny" class="springIdol2.stuffImpl.Instrumentalist" autowire="byName">
<!--使用自动装配的方式当然不意味着不能手动为某些元素赋值-->
<property name="song" value="《uptown funk》"/>
</bean>
3.1.2 byType自动装配
下面的装配与byName的格式完全相同,就不在举例子了,我们把重点放在他们的区别和优缺点上。
byType装配存在一个局限性:如果Spring寻找到了多个Bean,它们的类型与需要自动装配的属性的类型都匹配(尤其是将属性是父类或借口的实例),Spring不会去擅自猜测那个Bean更适合你,而会选择抛出异常。
Spring为我们提供了两种方法去解决这个问题:
1.自动装配标识一个首选Bean
可以使用<bean>的primary属性来设置首选Bean。默认情况下,所有满足条件候选的Bean这个属性都是true,所以我们需要设置的并不是那个首选bean,而是将所有非首选bean的primary属性设置为false。
<bean id="violin" class="springIdol2.stuffImpl.Violin"/>
<bean id="cymbal" class="springIdol2.stuffImpl.Cymbal" primary="false"/>
2.取消其他Bean的候选资格
如果我们希望直接排除在选择时直接排除某些Bean,可以将这些Bean的autowire-candidate属性设置为false,这将会直接无情地排除这些Bean的候选资格。
<bean id="cymbal" class="springIdol2.stuffImpl.Cymbal" autowire-candidate="false"/>
3.1.3 constructor自动装配
constructor利用构造器为Bean装配属性。constructor和byType具有相同的局限性,同时,如果一个类具有多个构造器,它们都满足自动装配条件时,Spring也不会和你玩猜猜乐的游戏——究竟哪个构造器更适合使用。
3.1.4 autodetect自动装配——最佳装配方式
如果你正在为自动装配Bean,又不知道以哪种方式一筹莫展,现在不必担心了,我们可以设置autowire属性为autodetect。由Spring来决定。Spring会首先尝试使用constructor来自动装配,如果没有发现与构造器相匹配的Bean时,Spring将尝试使用byType自动装配。(当然话说回来了,它还是没有解决有多个候选Bean的问题)
3.2 使用注解装配
从Spring2.5开始,最有趣的一种装配Spring Bean的方式是使用注解自动装配Bean的属性。使用注解自动装配和在XML中使用autowire属性自动装配并没有太大的区别。但是使用注解方式允许更细粒度的自动装配。
Spring容器默认禁用注解装配,所以在使用基于注解的自动装配前,我们需要在Spring配置中启动它。
<!--开启注解模式,当然,在注解模式下采取经典的方式配置bean也是没问题的-->
<context:annotation-config/>
至于命名空间的引入,IDEA会自动为你效劳。
注解方式与XML方式的一个最大的区别在于你需要依次为你想让Spring为你自动注入的属性加上注解(说实话,这一点都不麻烦,特别是在我们学习了马上要讲的自动扫描技术之后。相信我,你会爱上注解这个“新”技术)
3.2.1 @Autowired注解——注入Spring Bean
这里说一句题外话,IDEA的智能代码小帮手患有大小写敏感症,在第一次使用@Autowired注解时,当你写出“@a”不会得到任何提示,而“@A”才可获得弥足珍贵的提示信息。
@Autowired可以标注需要自动装配Bean引用的任何方法前(无论是setter、constructor还是其它自定义的方法)
@Autowired
public void setInstrument(Instrument instrument) {
this.instrument = instrument;
}
@Autowired可以直接标注到成员变量前,这时@Autowired甚至不会受限于private关键字,这意味着我们可以删除它的setter方法(但这不意味着别的方式下public的成员变量就不需要setter方法)。
@Autowired
private Instrument instrument;
当Spring发现我们在适当的位置使用了@Autowired注解,就会尝试着对该属性(或者方法)执行byType自动装配。
@Autowired是这样的方便我们的开发,但它也存在着两个阻碍我们工作场景的问题:如果没有匹配到Bean或者匹配到多个Bean, @Autowired就会遇到一些麻烦。
3.2.1.1 可选的自动装配
默认情况下,@Autowired具有强契约性。当找不到可装配的对象时,@Autowired不像传统方式配置的那些属性是天生的乐观派,它接受不了null这个无稽的事实,就会抛出令人讨厌的NoSuchBeanDefinitionException异常。嗯,他是够任性的。
属性不一定要装配,null值也是可以接受的,在这种情况下,可以设置@Autowired的required属性为false来告诉@Autowired这个属性的自动装配是可选的:
@Autowired(required = false)
private Instrument instrument;
注意,required属性可以用于@Autowired注解所使用的任何地方。但是当使用构造器装配时,只有一个构造器可以将@Autowired的required属性设置为true。此外,当@Autowired标注多个构造器时,Spring会从所有满足装配条件的构造器中选择入参最多的那个构造器。
3.2.1.2 限定歧义性的依赖
另一方面,如果有多个Bean符合条件,也是个麻烦事。我们采取缩小范围的方式来解决这个问题。

  1. 以ID作为条件选择Bean(缩小选择范围至唯一)
    @Autowired(required = false)
    @Qualifier("violin")
    private Instrument instrument;
    这样,Spring就会选择id为violin的Bean来进行注入。
    2.缩小选择范围
    我们也可以在Bean的定义中使用@Qualifier注解来给它限定一个范围
    @Qualifier("stringed")
    public class Violin implements Instrument {
    … …
    }
    或者(stringed代表弦类乐器)
    <bean id="violin" class="springIdol2.stuffImpl.Violin">
    <qualifier value="stringed"/>
    </bean>
    然后在注入时额外使用一个@Qualifier注解
    @Autowired(required = false)
    @Qualifier("stringed")
    private Instrument instrument;
    3.创建自定义限定器
    这个方法实际上就是我们通过模板自定义一个注解来替换@Qualifier(“... …”)(比如我们自定义一个@hello注解替换@Qualifier(“stringed”)),但如果你想了解更多,推荐你去谷歌,这里不再过多讲解。
    3.2.2 @Inject实现基于标准的自动配置
    @Inject与@Autowired用法和作用非常相近,@Inject则是为统一各种依赖注入框架编程模型的规范问题的产物。我们只关心@Inject和@Autowired的区别:
    3.2.2.1 不可修改的强契约性
    @Inject没有required属性,因此@Inject标注的依赖关系必须存在。
    3.2.2.2 引用延迟注入
    与其直接注入一个引用,不如要求@Inject注入一个Provider。Provider接口可以实现Bean引用的延迟注入以及注入Bean的多个实例等功能。这个这里也不做过多的介绍。
    3.2.2.3 @Named()限定Inject标注的属性
    @Named()之于@Inject相当于@ Qualifier之于@Autowired。实际上,@Named注解就是一个使用@Qualifier注解所标注的注解。@Qualifier注解帮助我们缩小匹配Bean选择的范围(默认使用ID),而@Named通过Bean的ID来标识可选择的Bean。
    3.2.3 @Value()注解——在注解中使用SPEL表达式
    @Autowired注解可以帮助我们自动装配其他Bean的引用,而@Value注解则可以帮我们装配基本类型合String类型的值。例如:
    @Value("《我爱你中国》")
    private String song;
    @Value如果只能装配简单类型的值也挺无聊的,@Value真正的强大之处在于它可以装配一个SpEL表达式。在前面的章节中我们知道SpEL表达式返回的结果可以是任意类型,所以,@Value可以根据SpEL表达式进行的动态求算来装配任何类型的值。
    @Value("#{violin}")
    private Instrument instrument;
    3.3 自动检测Bean
    在之前的学习中,我们使用了注解的方式帮助我们实现了为我们自定义的Spring Bean属性的自动注入——<property>和<constructor>元素遭到了灭顶之灾。但是,我们的XML配置仍然混乱不堪,这是因为我们仍然需要使用<bean>元素显式的定义Bean。我们既希望将我们自定义的Spring Bean放入Spring容器中,又希望在只XML中定义一些我们引入的非自定义的必要组件(比如说第5章要讲到的数据源),Spring为我们提供了简单的方法
    3.3.1 自动注册的Spring Bean
    <context:component-scan base-package="springIdol2.stuffImpl"/>
    <context:component-scan>元素除了完成与<context:annotation>一样的工作外,还允许Spring检测Bean和定义Bean。这意味着不使用<bean>元素,Spring应用中的大多数Bean都能实现定义和装配。
    <context:component-scan>元素将扫描base-package属性标识的包及其子包,并将类名前有如下注解(构造型注解)的类注册为Spring Bean:
    注解名称 相关描述
    @Component 通用的构造型注解,标识该类为Spring组件
    @Controller 标识该类定义为SpringMVC的controller
    @Repository 标识该类定义为数字仓库
    @Service 标识该类定义为服务
    其中@Component标注可以任意自定义注解,我们在后面SpringMVC的章节中会对其他注解进行介绍。例如:
    @Component("violin")
    public class Violin implements Instrument {
    public Violin() {
    System.out.println("小提琴准备好了");
    }
    @Override
    public void play(){
    System.out.println("同时演奏小提琴");
    }
    }
    其中violin代表这个Bean的id。
    3.3.2 过滤组件扫描
    试想,如果想通过注解的方式让所有实现Instrument接口的类都注册为Spring Bean,我们不得不依次查看这些乐器:小提琴,架子鼓,钢琴… …然后依次为其添加@Component注解,这是相当不方便的。
    Spring组件的组件扫描相当灵活,我们可以在<context:component-scan>中使用<context:include-filter>和<context:exclude-filter>子元素随意调整扫描行为:我们可以使用<context:include-filter>直接告知<context:component-scan>哪些类需要注册为Spring Bean,使用<context:exclude-filter>告知<context:component-scan>哪些类不需要注册为Spring Bean。例如:
    <context:component-scan base-package="springIdol2.stuffImpl">
    <context:include-filter type="assignable" expression="springIdol2.stuff.Instrument"/>
    <context:exclude-filter type="assignable" expression="springIdol2.stuffImpl.Cymbal"/>
    </context:component-scan>
    这两个子元素的expression属性定义了对哪些Bean需要注册哪些Bean不需要注册。type属性定义组件扫描方式:
    过滤器类型 描述
    annotation 过滤器扫描使用指定注解所标注的哪些类。通过expression属性指定要扫描的注解
    assignable 过滤器扫描派生于expression属性所指派类型的那些类
    aspectj 过滤器扫描与expression属性所指定的AspectJ表达式所匹配的哪些类
    custom 使用自定义的org.springframework.core.type.TypeFilter实现类,该类由expression属性指定
    regex 过滤器扫描类的名称与expression属性所指定的正则表达式所匹配的那些类
    这样,我们甚至摆脱了注解带来的极其微小的限制。
    3.4 使用Spring基于java的配置
    对于He-Man XML Haters俱乐部的正式成员,他们热衷于消除那些可恶的尖括号,Spring也之提供了基于java的配置。
    我们仍然需要及少量的XML来启动java配置:
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframe work.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

<!--<context:component-scan>元素不仅能够自动注册那些使用某种构造型注解所标注的Bean,而且还可以
加载使用@Configuration注解标注的类-->
<context:component-scan base-package="springIdol2"></context:component-scan>
</beans>
在基于Java的配置里使用@Configuration注解java类就等价于XML配置中的beans>元素,@Bean注解的java方法相当于XML配置中的,该注解告知Spring将其所注解的方法返回的对象在Spring上下文中注册为Spring Bean。该方法名作为Spring Bean的id。参数注入显得格外简单,直接实例化我们的对象以最传统的java方式为这些属性赋值就可以了。
package springIdol3.config;

import org.springframework.context.annotation.Bean;
import springIdol.stuff.Performer;
import springIdol.stuffImpl.Instrumentalist;
import springIdol2.stuffImpl.Cymbal;

/**

  • Created by hukaihe on 2016/3/4.
    */

@org.springframework.context.annotation.Configuration
//上面的注解会告诉Spring这个类将包含一个或多个Spring Bean的定义
public class Configuration {
@Bean
public Cymbal cymbal(){
return new Cymbal();
}

@Bean
//方法名为该bean的id
public Performer kenny(){
    Instrumentalist kenny=new Instrumentalist();
    kenny.setSong("我爱你中国");
    kenny.setInstrument(cymbal());
    return kenny;
}

}

打开App,阅读手记
18人推荐
发表评论
随时随地看视频慕课网APP

热门评论

很好的基础知识讲解,还需要好好回味实践

查看全部评论