Spring学习笔记》

第一章 装配Bean

一、 声明bean

1. 声明一个简单的bean

Spring3.0开始,提供xml和注解两种配置Bean的方式。

以下为xml配置文件声明一个Bean:

<bean id=”bean_id” class=”bean_class”/>

id属性定义了bean的名字,class属性定义了bean的类。当Spring容器加载该bean时,Spring将使用默认的构造方法来实例化该bean

2. 通过构造方法声明一个Bean

假设Bean中有一个带参数的构造方法,可以通过xml配置该bean使用带参数的构造方法实例化。

Bean代码:

package com.model

 

public class MyBean{

private int age;

public MyBean(int age)

{this.age=age;}

public void setAge(int age)

{this.age=age;}

public int getAge()

{return age;}

}

 

Xml文件:

<bean id=mybean class=com.model.MyBean >

<constructor-arg  value=18 />

</bean>

3. 通过构造方法引用对象

如果bean的构造方法参数是对象,则可以通过xml文件配置。假设上例中构造方法参数为对象,示例配置如下:

<bean id=mybean  class=com.model.MyBean>

<constructor-arg  ref=object/>

</bean>

 

4. 通过工厂方法创建Bean

有时候静态工厂方法是实例化对象的唯一方法。Spring支持通过<bean>元素的factory-method属性来装配工厂创建的Bean

假设工厂方法为

public static Instance getInstance()

{return new Instance();}

xml配置为:

<bean id=mybean   class=com.model.MyBean >

<factory-method =getInstance />

5. Bean的作用域

所有的Spring  Bean默认都是单例。当容器分配一个Bean时,它总是返回Bean的同一个实例。

Spring中配置<bean>元素时,我们可以为bean声明一个作用域,为了让Spring在每次请求时都获得一个新的Bean实例,只需要配置beanscope属性为prototype即可。

<bean id=mybean  class=com.model.MyBean   scope=prototype />

Spring还提供了其他的作用域选项,如下所示

作用域

定义

singleton

在每一个Spring容器中,一个bean定义只有一个实例对象(默认)

prototype

每次调用都创建一个实例

request

在一次http请求中,每个bean定义对于一个实例,该作用域仅在基于web的上下文中才有效

session

在一个http session中,每个bean定义对应一个实例,该作用域仅在基于web的上下文中才有效

global-session

在一个全局http session中,每个bean对应一个实例,该作用域仅在portlet上下文中才有效

6. 初始化和销毁Bean

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

示例:

<bean id=mybean  class=com.model.MyBean 

init-method=initMethod  destory-method=destoryMethod />

使用这种配置,bean在实例化之后会立即调用initMethod方法,在该bean从容器移除和销毁前,会调用destoryMethod方法。

如果上下文中定义的很多bean都拥有相同名字的初始化方法和摧毁方法,可以使用<beans>元素的default-init-methoddefault-destory-method属性:

<beans>

……………………

default-init-method=default-init-method

default-destory-method=default-destory-method</beans>

7. lazy-init

bean元素中可以设定lazy-init="true",这样这个bean可以不跟随容器启动而初始化,而是在需要用到这个bean的时候再初始化。

二、 注入bean属性

通常JavaBean的属性是私有的,同时拥有一组getset方法。Spring可以借助属性的set方法来配置属性的值,以实现setter方式的注入。

1. 注入简单值

spring中可以使用<property>元素配置bean的属性。<property>在许多方面都与<constructor-arg>类似,只不过一个是通过构造参数来注入值,另一个是通过调用属性的setter方法来注入值。

例如:

<bean id=mybean  class=com.model.MyBean  >

<property name=arg value=arg_value>

</bean>

一旦bean被实例化,spring就调用<property>元素所指定属性的setter方法为该属性注入值。value属性可以指定数值型(intfloatDouble等)、booleanString型的值。

 

2. 引用其他bean

假设mybean1mybean引用,

<bean id=mybean1  class=com.model.MyBean1/>

<bean id=mybean   class=com.model.MyBean  >

<property  name=arg  ref=mybean1>

</bean>

3. 注入内部bean

内部bean是定义在其他bean内部的bean

例如:

<bean id=mybean class=com.model.MyBean>

<property  name=arg>

<bean class=com.model.MyBean1/>

</property>

</bean>

内部bean并不仅限于setter注入,还可以把内部bean装配到构造方法的入参中,如下所示:

 

<bean id=mybean  class=com.model.MyBean>

<constructor-arg>

<bean class=com.model.MyBean1/>

</constructor-arg>

</bean>

注意内部bean没有id属性,虽然为内部bean配置一个id属性是完全合法的,但是并没有太大必要,因为我们永远不会通过名字来引用内部bean。内部bean最大的缺点是他们不能被复用。内部bean仅适用于一次注入,而且不能被其他bean引用。

4. 使用Spring命名空间P装配属性

命名空间pschema URI http://www.springframework.org/schema/p   使用命名空间p,需要在xml文件中增加如下一段声明:

xmlns:p=” http://www.springframework.org/schema/p

命名空间p使用示例:

<bean  id=mybean  class=com.model.MyBean

p:arg1=value1

p:arg2=value2

p:arg3-ref=object  />

5. 装配集合

valueref仅在bean的属性值是单个值的情况下才有用。当bean的属性值是复数时,如集合时,可以使用集合配置。

Spring提供了相应的集合配置元素

集合元素

用途

<list>

装配list类型的值,允许重复

<set>

装配set类型的值,不允许重复

<map>

装配map类型的值,名称和值可以是任意类型

<props>

装配properties类型的值,名称和值必须是String类型

装配ListSetArray

<bean id=mybean   class=com.model.MyBean>

<property name=objectList>

<list>

<ref  bean=object1/>

<ref  bean=object2/>

</list>

</property>

</bean>

list元素包含一个或多个值。这里的<ref>元素用来定义spring上下文中的其他bean引用,当然还可以使用其他的元素作为<list>成员,包括<bean><value><null/>。实际上,<list>可以包含另一个<list>作为其成员,形成多维列表。

装配map

<bean id=mybean  class=com.model.MyBean>

<property  name=objectMap>

<map>

<entry key =key1  value-ref=value1 />

<entry  key=key2 value-ref =value2/>

</map>

</property>

</bean>

<map>元素声明了一个java.util.Map类型的值。每个<entry>元素定义了一个map成员。key属性指定了entry的键,value-ref属性定义了entry的值,并引用了spring上下文中的其他bean

属性

用途

key

指定mapentry的键为String

key-ref

指定mapentry的键为spring上下文其他bean的引用

value

指定mapentry的值为String

value-ref

指定mapentry的值为spring上下文其他bean的引用

6. 装配空值

为属性设置null值,只需要使用<null/>元素

三、 使用表达式装配 2015-07-21

Spring3引入了Spring表达式语言 SpEL。它通过运行期间执行的表达式将值装配到bean的属性或构造函数参数中。

SpEL特性:

使用beanID来引用bean

调用方法和访问对象的属性

对值进行算术、关系和逻辑运算

正则表达式匹配

集合操作

一、 字面值

最简单的SpEL表达式仅包含一个字面值。

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

#{ }标记会提示Spring这个标记里的内容是SpEL表达式,它们还可以与非SpEL表达式的值混用。

<property name=message  value=the value is #{5} />

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

<property name=name  value=#{  kom  }>

二、 引用beanproperties方法

SpEL表达式能做的另一个基本失去是通过id引用其他bean

<property name=object  value=#{mybean} />

注意,是使用的value而不是ref

SpEL可以引用其他对象中的属性。例如

<bean id=bean1  class=com.model.MyBean>

<property name=anotherbean>

<bean class=com.model.AnotherBean/>

</property>

</bean>

 

<bean id=bean2  class=com.model.MyBean>

<property  name=anotherbean  value=#{bean1.anotherbean}/>

</bean>

 

其中bean2中的属性anotherbean引用了bean1中的anotherbean

SpEL不仅能调用bean的属性,还可以调用它的方法。例如

<property  name=anotherbean  value=bean1.getAnotherBean() />

如果bean1null,则SpEL则会抛出一个空指针异常。可以使用null-safe存取器,使用 ?.运算符代替 来访问方法。在放分右边方法之前,该运算符会确保左边项的值不会为null。如果左边为null,则SpEL不会尝试调用右边的方法。

三、 操作类

SpEL中,使用T()运算符会调用类作用域的方法和常量。例如在SpEL中使用javaMath类,可以这样写:

T(java.lang.Math)

在上面的例子中,T()运算符会返回一个java.lang.Math的类对象。通过该运算符可以访问指定类的静态方法和常量。

<property name=randomNumber  value=#{T(java.lang.Math).random()} />

上例中调用了Math.random();

四、 SpEl值上执行操作

SpEL提供了几种运算符,这些运算符可以用在SpEL表达式中的值上。

运算符类型

运算符

算术运算

+-*/%^

关系运算

<>==<=>=ltgteqlege

逻辑运算

andnotor、!

条件运算

?:(ternary)?:(Elvis)

正则表达式

mathes

SpEL提供了所有java支持的基础算术运算符,它还增加了^运算符执行乘方运算。

<property  name=age  value=#{Tom.age+20} /> 

比较值

例如,比较俩个值是否相同返回boolean类型的值

<property name=equal   value=#{bean1.age==23} />

 

逻辑表达式

<property  name=sex  value=#{!  true} />

 

五、 筛选集合

可以使用SpEL引用集合中的某个成员,SpEL同样具有基于属性值来过滤集合成员的能力。SpEL可以从集合的成员中提取某些属性放到一个新的集合中。

为了展示用户,定义一个City类。

package com.model

public class City{

private String name;

private int population;

public void setName(String name)

.

.

}

xml文件中,配置信息如下

<util : list id=cities >

<bean class=com.model.City  p:name=BeiJing  p:population=20000000/>

<bean class=com.model.City  p:name=ShangHai p:population=30000000/>

<bean class=com.model.City  p:name=HangZhou p:population=10000000 />

</util:list>

 

访问集合成员

从集合中提取一个成员,并将它装配到某个属性中

<property  name=city  value=# { cities [ 2 ] } />

查询集合成员

如果我们想从cities中查询人口大于1500w的城市,在SpEL中,只需要使用查询运算符 . ? [ ] 就可以做到。

<property name=bigCities  value=#{cities.?[population gt 15000000]} />

查询运算符会创建一个新集合,新集合中只存放符合括号内的表达式的成员。

SpEL提供两种其他查询运算符: . ^[ ]和 . $ [ ] ,从集合中查询出第一个匹配项和最后一个匹配项。

投影集合

 集合投影是从集合的每一个成员中选择特定的属性放入一个新的集合中。SpEL的投影运算符 . ! [ ] 可以做到。

假设仅需要包含城市名字的集合,

<property  name=cityName  value=#{cities.![name]} />

可以对集合进行查询和投影运算,这里把符合条件的大城市名字注入cityNames

<property  name=cityNames  value=#{cities.?[population gt 15000000].![name] } />

第二章 最小化Spring XML配置

Spring提供了几种技巧,可以帮助减少XML的配置数量。

自动装配(autowiring),有助于减少甚至消除配置<property >元素和<constructor-arg>元素,让spring自动识别如何装配bean的依赖关系。

自动检测(autodiscovery)比自动装配更进了一步,让spring自动识别哪些类需要被配置成spring bean,从而减少对<bean> 元素的使用。

一、 自动装配Bean属性

一、 4种类型的自动装配

byName   把与bean的属性具有相同名字(或者id)的其他bean自动装配到bean的对应属性中。如果没有跟属性的名字匹配的bean,则该属性不进行装配。

byType    把与Bean的属性具有相同类型的其他bean自动装配到bean的对应属性中。如果没有跟属性的类型匹配的bean,则该属性不进行装配。

constructor  把与bean的构造方法入参具有相同类型的其他bean自动装配到bean的构造方法对应的入参中。

autodetect   首先尝试使用constructor进行自动装配,如果失败,再尝试使用byType进行自动装配。

 

byName自动装配

 

手动装配:

<bean id=”bean class=com.model.Bean />

<bean id=bean2  class=com.model.Bean >

<property  name=name  value= Tom / >

<property  name=bean ref=bean />

</bean>

自动装配:

<bean id=”bean class=com.model.Bean />

<bean id=bean2  class=com.model.Bean  autowire=byName>

<property  name=name  value= Tom / >

</bean>

这样,bean2中的bean属性就被自动装配了。

byName自动装配遵循一项约定:为属性自动装配ID与该属性的名字相同的Bean。使用byName自动装配的缺点是需要假设bean的名字与其他bean的属性的名字一样。

byType自动装配

当使用byType自动装配时,spring会寻找哪一个bean的类型与属性的类型相匹配。但byType自动装配存在一个局限性,如果spring寻到到多个bean,它们的类型与需要自动装配的属性的类型都匹配,则spring会抛出异常。所有,应用只允许存在一个bean与需要自动装配的属性类型相匹配。

为了避免使用byType自动装配带来的歧义,spring提供2种选择:可以为自动装配标识一个首选bean,或者可以取消某个bean自动装配的候选资格。

为自动装配标识一个首选bean,可以使用<bean>元素的primary属性。如果只有一个候选beanprimary属性为true,那么该bean比其他候选bean优先被选择。但是primary 默认设置为true

如果在自动装配时,我们希望排除某些bean,则可以设置这些beanautowire-candidate属性为false,这里我们要求spring在自动装配时忽略bean作为候选bean

constructor自动装配

如果要通过构造方法注入来配置bean,可以移除<constructor-arg>元素,由spring在应用上下文自动选择bean注入到构造方法入参中。

<bean  id=bean1  class=com.model.Bean  autowire=constructor />

由于constructor自动装配和byType自动装配都是通过bean的类型自动装配的,当发现多个匹配bean时,spring会抛出异常。

最佳自动装配

我们可以设置autowire属性为autodetect,由spring决定。spring首先尝试使用constructor自动装配,如果没有发现与构造方法匹配的bean时,spring将尝试使用byType自动装配。

二、 默认自动装配

如果需要为srping应用上下文中的每一个(或者其中大多数)bean配置相同的autowire属性,可以在根元素<beans> 上增加一个default-autowire属性。默认情况下,defau-autowire属性被设置为none

三、 混合使用自动装配和显示装配

对某个bean选择了自动装配策略,不代表不能对该bean的某些属性进行显示装配。

注意,使用constructor自动装配时,必须让spring自动装配构造方法的所有入参,不能混合使用constructor自动装配策略。

二、 使用注解装配

spring2.25开始,spring支持使用注解自动装配bean的属性。

spring容器默认禁止注解装配。使用前需要启用:

<beans>元素中配置<context:annotation-config />,如下

<beans>

……………….

<context:annotation-config />

</beans>

1. 使用@Autowired

使用@Autowiredspring自动装配Beancity属性,则可以对setCity()方法进行标注,如下

@Autowired

public void setCity(City city){

this.city=city;

}

现在我们可以移除city属性对应的<property>元素了,Spring会尝试对该方法执行byType自动装配。

我们不仅可以使用它标注setter方法,还可以标注需要自动 装配bean引用的任意方法。@Autowired注解可以标注构造方法。

另外当用@Autowired直接标注属性,并删除setter方法,

@Autowired

private City city;

@Autowored甚至不会受限于private关键字。

如果没有匹配的bean或者存在多个匹配的bean@Autowired注解就会遇到一些麻烦。但是这两种情况都有相应的解决办法。如下

2. 可选的自动装配

属性不一定非要装配,null也是可以接受的。在这种情况下,可以设置@Autowiredrequired属性为false来配置自动装配是可选的。这样,spring将先尝试装配,如果找不到与之匹配的bean,应用不会发生任何问题,而属性值会被设置为null

3. 限定歧义性的依赖

当与之匹配的bean有多个的时候,可以使用@Qualifier注解来指明使用id来匹配bean,例如

@Autowired

@Qualifier(ShangHai)

private City city;

如上,@Qualifier注解将尝试注入idShangHai 的bean

4. 在注解注入中使用表达式

spring3.0引入了@Value,可以让我们使用注解装配String类型的值和基本类型的值,例如intboolean。例如

@Value(Tom Clone)

private String name;

在这里,为String类型的属性name装配了一个String类型的值。

借助于SpEL表达式,使得@Value成为强大的装配可选方案。

@Value(#{city[0].name})

private String name;

5. @Scope

 

相当于<bean>元素中的scope属性

6. @postconstruct

相当于init-method

7. @predestor

相当于destory-method

三、 自动检测bean

<context:component-scan>元素除了完成与<context:annotation-config>一样的工作,还允许spring自动检测bean和定义bean。这意味着不用<bean>元素,spring中大多数bean能偶实现定义和装配。

为了完成spring自动检测,需要使用 <context:component-scan>代替<context:annotation-config>

<beans>

…………………………………………….

<context:component-scan  base-package=com.model>

</ context:component-scan >

</beans>

 

<context:component-scan>元素会自动扫描指定的包及其所有子包,并查找出能自动注册为bean的类。base-package属性标识了所扫描的包。

1. 为自动检测标注bean

默认情况下,<context:component-scan>查找使用构造型(stereotype)注解所标注的类,这些特殊的注解如下

@Component   通用的构造型注解,标识该类为spring组件

@Controller  标识将该类定义为spring mvc controller

@Repository   标识将该类定义为数据仓库

@Service  标识将该类定义为服务

使用@Component 标注的任意自定义注解

可以使用 @Component( id )在括号里显式的指定beanid

2. 过滤组件扫描

在如何扫描来获得候选bean方面,<context:component-scan>非常灵活。通过<context:component-scan>配置<context:include-filter>或者<context:exclude-filter>子元素,可以随意调整扫描行为。


 

<context:include-filter>typeexpression属性一起写作来定义组件扫描策略。

过滤器类型

描述

annotation

过滤器扫描使用指定注解标注的那些类,通过expression属性指定要扫描的注解

assignable

过滤器扫描派生于expression属性所指定类型的那些类

 

aspectj

过滤器扫描与expression属性所指定的AspectJ表达式所匹配的那些类

custom

使用自定义的org.springframework.core.type.TypeFilter实现类,该类由expression属性指定

regex

过滤器扫描类的名称与expression属性所指定的正则表达式所匹配的类

除了使用<context:include-filter>告知哪些类需要注册为bean以外,还可以使用<context:exclude-filter>告知哪些类不需要注册为bean

四、 使用基于Spring基于java的配置

 

第三章 面向切面的Spring

一、 什么是面向切面编程(AOP)

1. 定义AOP术语

描述切面常用的术语有通知(advice)、切点(pointcut)和连接点(join point)

通知(advice)

AOP术语中,切面的工作被称之为通知。通知定义了切面是什么以及何时使用。除了描述切面要完成的工作,通知还解决了何时执行这个工作的问题。它应该应用于某个方法被调用之前?之后?之前和之后?还是只在方法抛出异常时?

spring切面可以应用5种类型的通知

Before  在方法被调用之前调用通知

After   在方法完成之后调用通知,无论方法执行是否成功

After-returning  在方法成功执行之后调用通知

After-throwing 在方法抛出异常后调用通知

Around  通知包裹了被通知的方法,被通知的方法调用之前和调用之后执行自定义的行为。

连接点(Join point

连接点是在应用执行过程中能够插入切面的一个点。这个店可以是调用方法时、抛出异常时、甚至修改一个字段时。切面代码可以利用这些点插入到应用的正常流程之中,并添加新的行为。

切点(pointcut

一个切面并不需要通知应用的所有连接点。切点有助于缩小切面所通知连接点的范围。

切面(Aspect

切面是通知和切点的结合。通知和切点共同定义了关于切面的全部内容-它是什么,在何时和何处完成其功能。

引用(Introduction

引用允许我们向现有的类添加新的方法或属性。

织入(Weaving

织入是将切面应用到目标对象来创建新的代理对象的过程。切面在指定的连接点被织入到目标对象中。

2. 五种类型的通知

Before 在方法被调用之前调用通知

After 在方法完成之后调用通知,无论方法是否执行成功

After-returnning 在方法成功执行之后调用通知

After-throwing在方法抛出异常后调用通知

Around 通知包裹了被通知的方法,被通知的方法调用之前和调用之后执行自定义的行为。

3. 编写切点(annotation方式)

首先,需要在xml文件中打开aop,在xml文件中添加以下代码

<aop:aspectj-autoproxy/>

添加aspectJ的依赖包aspectjweaver.jar

在切面类上使用注解@Aspect,并且使用注解@Component或者xml文件配置方式把切面类配置成bean

使用execution()指示器选择Instrumentplay()方法。方法表达式以*号开始,标识了不需要关心方法返回值的类型,然后我们置顶了全限定类名和方法名。对于方法参数列表,使用( . . )标识切点选择任意的play()方法,无论该方法的入参是什么。


 

现在假设我们需要配置切点仅匹配com.springinaction.springidol包。此时可以使用withdin()指示器来限制匹配。如下

execution(*com.springincation.springidol.Instrument.play(..))&&within(com.springincation.springidol.*)

注意,使用&&execution()within()指示器连接在一起形成and关系。类似,也可以使用||!操作符,在xml文件中,&符号具有特殊意义,所以使用and代替&&。同样可以使用ornot

注意,bean()指示器允许我们在切点表达式中使用bean id来标识beanbean()指示器使用bean  idbean名称作为参数来限制切点只匹配特定的bean。 

4. xml中声明切面

AOP配置元素

描述

<aop:advisor>

定义AOP通知

<aop:after>

定义AOP后置通知(不管通知的方法是否执行成功)

<aop:after-returning>

定义AOP after-returning通知

<aop:around>

定义AOP around 通知

<aop:aspect>

定义切面

<aop:aspectj-autoproxy>

启用@AspectJ注解

<aop:before>

定义AOP after通知

<aop:declare-parents>

为被通知的对象引入额外的接口,并透明地实现

<aop:pointcut>

定义切点

<aop:config>

顶层的AOP元素,大多数<aop:*>元素必须包含在此元素下