Spring学习-装配Bean

来源:互联网 发布:矩阵乘法mm 编辑:程序博客网 时间:2024/05/22 17:46

转自:http://blog.csdn.net/y492235584/article/details/51672285


前言

今天开始系统学习Spring框架,使用的书籍是Spring in action第三版中文版。之前已有一定的Spring基础,有些部分可能不会记录的非常详细。

依赖注入(前言)

依赖注入将对象的使用和对象的管理(创建、赋值等)进行了解耦。 
Spring通过应用上下文(Application Context)装载Bean的定义并把它们组装起来,Spring应用上下文全权负责对象的创建和组装,Spring自带了几种应用上下文的实现,它们之间的主要区别仅仅是如何加载它们的配置(xml配置、注解配置等)

//加载myxml.xml配置文件ApplicationContext context = new ClassPathXmlApplicationContext("myxml.xml");//获得BeanUser user = (User)context.geatBean("User");
  • 1
  • 2
  • 3
  • 4
  • 5

*

Spring容器:

容器是Spring框架的核心,Spring容器使用依赖注入管理构成应用的组件,它会创建相互协作的组件之间的关系。 
Spring自带了两种不同的容器: 
1、BeanFactory,由org.Springframework.beans.factory.BeanFactory接口定义,他是最简单的容器,提供基本的DI支持 
2、应用上下文,由org.springframework.context.ApplicationContext接口定义,基于BeanFactory之上构建,并提供面向应用的服务,例如从属性文件解析文本信息的能力,以及发布应用事件给感兴趣的事件监听者的能力。 
*:大部分情况下我们使用第二种容器。 
Spring自带了几种类型的应用上下文。下面3钟是比较常用的: 
1、ClassPathXmlApplicationContext:加载位于应用系统classpath下的一个或多个XML文件。 
2、FileSystemXmlApplicationContext:读取文件系统下的XML配置文件并加载上下文定义。 
3、XmlWebApplicationContext:读取Wweb应用下的XML配置文件并装载上下文。

Bean的生命周期

1、Spring对Bean进行实例化。 
2、Spring将值和Bean的引用注入Bean对应的属性中。 
3、如果Bean实现了BeanNameAware接口,Spring将Bean的ID传递给setBeanName()接口方法 
4、如果Bean实现了BeanFactoryAware接口,Spring将调用setBeanFactory()接口方法,将BeanFactory容器实例传入。 
5、如果Bean实现了ApplicationContextAware接口,Spring将调用setApplicationContext接口方法,将应用上下文的引用传入。 
6、如果Bean实现了BeanPostProcessor接口,Spring将调用它们的postProcessBeforeInitialization()接口方法。 
7、如果Bean实现了InitializingBean接口,Spring将调用它们的afterPropertiesSet()接口方法。类似地,如果Bean使用init-method声明了初始化方法,该方法也会被调用。 
8、如果Bean实现了BeanPostProcessor接口,Spring将调用它们的post-PoressAfterInitialization()方法。 
9、此时此刻,Bean已经准备就绪,可以被应用程序使用了,它们将一直驻留在应用上下文中,直到应用上下文被销毁。 
10、如果Bean实现了DisposableBean接口,Spring将调用它的destory()接口方法。同样,如果Bean使用destory-method声明了销毁方法,该方法也会被调用。

装配Bean

第一步: 
声明Bean,即创建对应的类

package xie;public class Juggler implements Performer {    private int beanBags = 3;    public Juggler() {    }    public Juggler(int beanBags) {        this.beanBags = beanBags;    }    public void perform() throws PerformanceException {    Sysotem.out.println("JUGGLING" + beanBags + "BEANBAGS");    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

第二步: 
创建Spring配置,有两种方式 
xml方式: 
在XML文件中声明Bean时,所有的Bean配置在Beans根元素下。 
一个典型的SpringXML配置文件:

< beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd>    < !-- Bean declarations go here -->< /beans>
  • 1
  • 2
  • 3
  • 4
  • 5

在< beans>元素内,可以配置所有的Spring配置信息,包括< bean>声明。beans命名空间不是唯一的Spring命名空间,Spring的核心框架自带了10个命名空间的配置:Spring命名空间 
在Spring中配置声明的类: 
< bean id=”duke” class=”xie.Juggler” /> 
这样就创建了一个由Spring容器管理的Bean,id属性定义了Bean的名字,也作为该Bean在Spring容器中的引用。 
当Spring容器加载该Bean时,Spring将调用默认的构造器来实例化duke Bean,实际上,duke会使用如下代码来创建:

new xie.Juggler();
  • 1

可以使用如下代码加载Spring上下文

ApplicationContext ctx = new ClassPathXmlApplication("***.xml");Performer performer = (Performer) ctx.getBean("duke");performer.perform();
  • 1
  • 2
  • 3

如果不想使用默认的构造方法,则可以这样配置:

< bean id="duke" class="xie.Juggler">    < constructor-arg value="15" />< /bean>
  • 1
  • 2
  • 3

通过使用< constructor-arg>标签,可以告诉Spring额外的构造信息,此时对象的创建将调用带一个参数的那个构造方法。 
如果在构造时想传入一个对象作为参数:

< bean id="obj1" class="...">< bean id="duke" class="xie.Juggler">    < constructor-arg ref="obj1" />< /bean>
  • 1
  • 2
  • 3
  • 4

这样,就可以吧名字为obj1的Bean作为duke构造器的参数了。(此处忽略了实现的可行性,只关注配置方法) 
*注:如果属性是简单类型,则可以使用value。如果是对象类型,则使用ref 
使用factory-method装配Bean 
可以使用factory-method属性指定创建对象的方法

< bean id="theSingle"     class="..."     factory-method="getInstance" />
  • 1
  • 2
  • 3

Bean的作用域 ##

所有Spring Bean默认都是单例,要改变Bean的作用范围,只需要配置Bean的scope属性,scope属性有以下取值 
scope取值 
*注:Spring中有关单例的概念限于Spring上下文的范围内,不像真正的单例,在每个类加载器中保证只有一个实例。Spring的单例Bean只能保证在每个应用上下文中只有一个Bean实例。没人可以阻止你使用传统方式实例化用一个Bean,或者你甚至可以定义几个< bean>声明来实例化一个bean。

初始化和销毁Bean

为Bean定义初始化和销毁操作,需要使用init-method和destory-method参数来配置< bean>元素。init-method属性指定在初始化Bean时要调用的方法,destory-method属性指定了Bean从容器中移除之前要调用的方法。

< bean id="auditorium"    class="..."    init-method="turnOnLights"    destory-method="turnOffLights" />
  • 1
  • 2
  • 3
  • 4

auditorium Bean实例化后会立即调用turnOnLights()方法,在该Bean从容器中移除和销毁前,会调用turnOffLights()方法。 
这里写图片描述 
在< beans>标签中可以使用default-init-method和default-destory-method方法为所有的bean设置默认的初始化和销毁方法。

注入Bean属性

在Spring中可以使用< property>元素配置Bean的属性。< property>和< constructor-arg>在很多方面都类似,只不过一个是通过构造器参数注入值,一个是通过setter方法注入值。

< bean id="kenny" class="...">    < property name="song" value="my love"/>< /bean>
  • 1
  • 2
  • 3

一旦Kenny被实例化,Spring就会调用< property>元素所指定属性的setter方法为该属性注入值。通过上述配置,Kenny实例的song属性将被设置为“my love”。 
*注:value属性可以指定String/int/float/double/boolean/long/char等简单类型的值。为它设置String类型和int等类型方式是一样的:使用“值”。指定引用对象使用ref属性 
注入内部Bean 
内部Bean:定义在其他Bean内部的Bean

< bean id="kenny" class="...">    < property name="instrument" >        < bean class="..." />    < /property>< /bean>
  • 1
  • 2
  • 3
  • 4
  • 5

内部Bean通过直接声明一个< bean>元素作为< property>元素的子节点而定义的。内部Bean不仅限于setter注入,我们还可以把内部Bean装配到构造方法的参数中。 
*注:内部Bean没有id属性,因为我们永远不会通过名字来引用内部Bean,这也突出了使用内部Bean的最大缺点:它不能被复用。内部Bean仅适用于一次注入,而且不能被其他Bean所引用。

Spring命名空间p装配属性

Spring的命名空间p提供了另一种Bean属性的装配方式。 
通过加入一段声明可以使用命名空间p

< 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"xsi:schemaLocation="http://www.springframework.org/schema/beans           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd>    < !-- Bean declarations go here -->< /beans>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

现在,可以使用P:作为< bean>元素所有属性的前缀来装配Bean的属性。

< bean id="kenny" class="..."    < !--使用my love装配song属性(通过setter方法) -->    p:song = "my love"    < !--使用id为saxophone的Bean装配instrument属性 -->    p:instrument-ref = "saxophone" />
  • 1
  • 2
  • 3
  • 4
  • 5

装配集合

Spring提供了4中类型的集合配置元素: 
这里写图片描述 
使用< list>

< bean id="hank" class="...">    < property name="instruments">        < list>            < ref bean="guiter" />            < ref bean="cymbal" />            < ref bean="harmonica" />        < /list>    < /property>< /bean>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

无论是< list>还是< set>都可以用来装配类型为java.util.collection的任意实现或者数组的属性。也可以使用< set>装配List集合,虽然这样有点怪,但确实似乎可以的,但是必须确保list集合中的每个元素都是唯一的(< set>重复的属性不会注入)。 
装配map

< bean id="hank" class="...">    < property name="instruments">        < map>            < entry key="GUITER" value-ref=“guiter”>            ...        < /map>    < /property>< /bean>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

上述中map key为String类型,value为对象类型这里写图片描述 
装配Properties 
当要注入的类型为Properties时

< bean id="hank" class="...">    < property name="instruments">        < !--定义一个Properties集合-->        < props>            < !--定义成员-->            < prop key="GUITAR">STRUM < /prop>            < prop key="CYMBAL">CRASH < /prop>            < prop key="HARMONICA">HUM < /prop>        < /props>    < /property>< /bean>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

< property>的name属性声明了字段的名称,Properties的每个成员都有< prop>元素定义,其中“GUITAR”为Properties成员的键,它的值为“STRUM” 
装配空值 
使用< null/>元素即可。

< property name="xxx">    < null/>< /property>
  • 1
  • 2
  • 3

使用Spring表达式

使用#{ }界定符来书写Spring表达式 
简单的SpEL(Spring表达式)

< property name="count" value="#{5}"/>
  • 1

这个表达式将count字段的值设为5,SpEL表达式还可以和非SpEL表达式混用:

< property name="count" value="the value is #{5}"/>
  • 1

String类型的字面值可以使用单引号或者双引号作为字符串的界定符。

< property name="userName" value="#{"小明"}"/>
  • 1

布尔类型

< property name="enabled" value="#{false}"/>
  • 1

复杂的SqEL 
引用Bean:可以通过Bean的ID直接引用Bean

< property name="instrument" value="#{saxophone}"/>
  • 1

引用Bean的属性

< !--使用Bean ID为kenny的song属性值作为值-->< property name="song" value="#{kenny.song}"/>
  • 1
  • 2

引用Bean的方法 
假设有一个songSelector Bean,该Bean有一个selectSong()方法

< property name="song" value="#{songSelector.selectSong()}"/>
  • 1

转换大小写

< !--将返回的字母转换为大写-->< property name="song" value="#{songSelector.selectSong().toUpperCase()}"/>
  • 1
  • 2

上述代码中如果selectSong()返回的是null,则会抛出一个NULLPointerException异常。使用null-safe存取器可以避免异常

< !--使用?判断-->< property name="song" value="#{songSelector.selectSong()?.toUpperCase()}"/>
  • 1
  • 2

减少XML的配置

自动装配

Spring提供了4中自动装配策略。 
1、byName:把Bean的属性具有相同名字(或ID)的其他Bean自动装配到Bean的对应属性中。

通过设置Bean的id与需要被自动装配的Bean对应的类的字段名称相同完成自动装配

< !--需要设置autowire属性-->< bean id="kenny" class="MyClass" autowire="byName"/>< !--如果Spring中还有一个Beanidname,并且MyClass类中有name属性,那么Bean kenny会自动为其属性name注入Bean name-->
  • 1
  • 2
  • 3

2、byType:把与Bean的属性具有相同类型的其他Bean自动装配到Bean的对应属性中。

byType使用与byName类似,只不过此处检测的是类型是否相同。如果匹配到多个Bean类型相同,Spring会抛出异常。 
经测试,byType会匹配子类型,同样如果匹配到多个子类型也会抛出异常。

3、constructor:把与Bean的构造器入参具有相同类型的其他Bean自动装配到Bean构造器的对应入参中。

< bean id="duke" class="..." autowire="constructor" />
  • 1

这种方式和byType有一样的局限性,如果匹配到多个Bean类型或多个构造器,Spring都会抛出异常。 
4、autodetect:首先尝试使用constructor进行自动装配,如果失败,再尝试byType进行自动装配。

< bean id="duke" class="..." autowire="autodetect" />
  • 1

设置首选Bean 
设置Bean的primary属性为true可以将Bean设置为首选Bean,但是Spring默认所有Bean的primary属性为true

< !--这个Bean是首选Bean-->< bean id="saxophone" class="..." primary="false" />
  • 1
  • 2

排除某个Bean 
设置Bean的autowire-candidate属性为false可以排除这个Bean(自动装配时将不会考虑使用这个Bean)

< !--这个Bean将不会被自动装配检测-->< bean id="saxophone" class="..." autowire-candidate="false" />
  • 1
  • 2

默认自动装配 
如果需要为Spring应用上下文的所有Bean均配置相同的autowire属性,只需要在根元素< beans>上添加default-autowire属性

<--设置beans下所有Bean默认使用byType装配-->< beans xmlns="..."            ......            default-autowire="byType">            ......< /beans>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

默认情况下default-autowire属性的值是none即不使用自动装配。 
*注:使用bean标签中的autowire属性可以覆盖默认设置。

混合使用自动装配和显式装配 
使用自动装配的同时也可以自己显式的装配字段。被显示装配的字段将不会被自动装配

< bean id="kenny" class="..." autowire="byType">    < property name="song" value="Jingle Bells">< /bean>
  • 1
  • 2
  • 3

Bean kenny的song字段通过setName方法设置成”Jingle Bells”,而这个类的其他字段将被自动装配(byType)

使用注解装配

注解详细介绍 
要使用注解装配,首先要在Spring中启用它,在< beans>标签下添加如下代码

< context:annotation-config />
  • 1

使用注解可以实现更小颗粒度的自动装配(可以只装配某一个字段) 
Spring3支持3种不同的自动装配注解 
- Spring自带的@Autowired注解 
- JSR-330的@Inject注解 
- JSR-250 的@Resource注解

@Autowired 
@Autowired可以标注任何方法,字段和构造器。

@Autowired //即使user是私有字段,也不需要setter方法即可注入,因为Spring直接通过反射注入属性值。private User user ---------@Autowired //自动注入userpublic void show(User user);-----------@Autowired //构造器也一样可以注入public Car(User user);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

当使用@Autowired注解时,Spring会搜索对应的Bean并将Bean注入。但是,如果没有Bean被匹配或多个Bean被匹配,使用这个注解就会可能产生异常。

可选的自动装配 
通过设置@Autowired的required属性为false,当没有找到合适的Bean时,就不会产生异常,而是会赋值为NULL。

@Autowired(required=false) //如果没有找到合适的Bean,instrument的值设为NULLprivate Instrument instrument
  • 1
  • 2

*注:required属性可以用于@Autowired注解使用的任何地方,但是当使用在构造器时,只有一个构造器可以将@Autowired的required属性设置为true。其他的构造器必须设置为false。此外,当使用@autowired标注多个构造器时,Spring会从所有满足装配条件的构造器中选择满足条件最多的那个构造器。 
@Value注解 
@Value可以注入简单类型的值:String、int、boolean等

@Value("xiaoming") /为name属性注入值xiaomingprivate String name;
  • 1
  • 2

在@Value注解中也可以使用SpEL表达式

@Value(*#{systenProperties.myFavoriteSong}*)private String song;
  • 1
  • 2

自动检测Bean

使用< context:component-scan>可以指定对应包自动检测Bean,如果要扫描多个包,使用逗号分隔开(使用这个标签指定的范围,除了完成了< context:annotation-config>的工作,还能自动检测Bean和定义Bean)

< beans .....>    < context:component-scan base-package="com.springinaction.springidol">    < /context:component-scan>< /beans>
  • 1
  • 2
  • 3
  • 4

base-package属性标识了< context:component-scan>标签所扫描的包。下面介绍这个标签检测Bean的方法: 
使用注解标识Bean 
这个标签指定包下的类可以使用集中注解表明这个类为Spring Bean 
- @Component 是一个泛化的概念,仅仅表示一个组件 (Bean) ,可以作用在任何层次。 
- @Service 通常作用在业务层,但是目前该功能与 @Component 相同。 
- @Constroller 通常作用在控制层,但是目前该功能与 @Component 相同。 
- @Repository 注解便属于最先引入的一批,它用于将数据访问层 (DAO 层 ) 的类标识为 Spring Bean。

当使用@Component注解一个类时(在扫描范围内),这个类会被标识为一个Bean,Bean的ID默认为类的无限定名,例如Guiter类标识后Bean的id默认为guiter, 
这个注解也可以使用一个参数设置Bean的id,例如@Component(“myGuiter”)设置Bean的Id为myGuiter 
过滤扫描组件、 
在< context:component-scan>标签下可以使用< context:include-filter>或者< context:exclude-filter>。通过这两个标签的type和expression属性可以定义过滤策略。 
type属性指定过滤器类型: 
这里写图片描述

< context:component-scan     base-package="com.springinaction.springidol">    < !--将指定包下所有派生于Instrument类的类自动标识为Bean-->    < context:include-filter type="assignable"        expression="com.springinaction.springidol.Instrument" />        < !--除了被@SkipIt注解标识的类-->    < context:exclude-filter type="annotation"        expression="com.springinaction.springidol.SkipIt">< /context:component-scan>

原创粉丝点击