《Spring学习笔记》
从Spring3.0开始,提供xml和注解两种配置Bean的方式。
以下为xml配置文件声明一个Bean:
<bean id=”bean_id” class=”bean_class”/>
id属性定义了bean的名字,class属性定义了bean的类。当Spring容器加载该bean时,Spring将使用默认的构造方法来实例化该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>
如果bean的构造方法参数是对象,则可以通过xml文件配置。假设上例中构造方法参数为对象,示例配置如下:
<bean id=”mybean” class=”com.model.MyBean”>
<constructor-arg ref=”object”/>
</bean>
有时候静态工厂方法是实例化对象的唯一方法。Spring支持通过<bean>元素的factory-method属性来装配工厂创建的Bean。
假设工厂方法为
public static Instance getInstance()
{return new Instance();}
xml配置为:
<bean id=”mybean” class=”com.model.MyBean” >
<factory-method =”getInstance” />
所有的Spring Bean默认都是单例。当容器分配一个Bean时,它总是返回Bean的同一个实例。
在Spring中配置<bean>元素时,我们可以为bean声明一个作用域,为了让Spring在每次请求时都获得一个新的Bean实例,只需要配置bean的scope属性为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上下文中才有效 |
为bean定义初始化和销毁操作,只需要使用init-method和destory-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-method和default-destory-method属性:
<beans>
……………………
default-init-method=”default-init-method”
default-destory-method=”default-destory-method”</beans>
在bean元素中可以设定lazy-init="true",这样这个bean可以不跟随容器启动而初始化,而是在需要用到这个bean的时候再初始化。
通常JavaBean的属性是私有的,同时拥有一组get、set方法。Spring可以借助属性的set方法来配置属性的值,以实现setter方式的注入。
在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属性可以指定数值型(int、float、Double等)、boolean、String型的值。
假设mybean1被mybean引用,
<bean id=”mybean1” class=”com.model.MyBean1”/>
<bean id=”mybean” class=”com.model.MyBean” >
<property name=”arg” ref=”mybean1”>
</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引用。
命名空间p的schema 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” />
value和ref仅在bean的属性值是单个值的情况下才有用。当bean的属性值是复数时,如集合时,可以使用集合配置。
Spring提供了相应的集合配置元素
集合元素 |
用途 |
<list> |
装配list类型的值,允许重复 |
<set> |
装配set类型的值,不允许重复 |
<map> |
装配map类型的值,名称和值可以是任意类型 |
<props> |
装配properties类型的值,名称和值必须是String类型 |
<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>作为其成员,形成多维列表。
<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 |
指定map中entry的键为String |
key-ref |
指定map中entry的键为spring上下文其他bean的引用 |
value |
指定map中entry的值为String |
value-ref |
指定map中entry的值为spring上下文其他bean的引用 |
为属性设置null值,只需要使用<null/>元素
Spring3引入了Spring表达式语言 SpEL。它通过运行期间执行的表达式将值装配到bean的属性或构造函数参数中。
SpEL特性:
l 使用bean的ID来引用bean
l 调用方法和访问对象的属性
l 对值进行算术、关系和逻辑运算
l 正则表达式匹配
l 集合操作
最简单的SpEL表达式仅包含一个字面值。
<property name=”count” value=”#{5}”/>
#{ }标记会提示Spring这个标记里的内容是SpEL表达式,它们还可以与非SpEL表达式的值混用。
<property name=”message” value=”the value is #{5}” />
String类型的字面值可以使用单引号或双引号作为字符串的界定符。例如
<property name=”name” value=”#{ ‘ kom ’ }”>
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()” />
如果bean1为null,则SpEL则会抛出一个空指针异常。可以使用null-safe存取器,使用 ?.运算符代替 . 来访问方法。在放分右边方法之前,该运算符会确保左边项的值不会为null。如果左边为null,则SpEL不会尝试调用右边的方法。
在SpEL中,使用T()运算符会调用类作用域的方法和常量。例如在SpEL中使用java的Math类,可以这样写:
T(java.lang.Math)
在上面的例子中,T()运算符会返回一个java.lang.Math的类对象。通过该运算符可以访问指定类的静态方法和常量。
<property name=”randomNumber” value=”#{T(java.lang.Math).random()}” />
上例中调用了Math.random();
SpEL提供了几种运算符,这些运算符可以用在SpEL表达式中的值上。
运算符类型 |
运算符 |
算术运算 |
+、-、*、/、%、^ |
关系运算 |
<、>、==、<=、>=、lt、gt、eq、le、ge |
逻辑运算 |
and、not、or、! |
条件运算 |
?:(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的配置数量。
l 自动装配(autowiring),有助于减少甚至消除配置<property >元素和<constructor-arg>元素,让spring自动识别如何装配bean的依赖关系。
l 自动检测(autodiscovery)比自动装配更进了一步,让spring自动识别哪些类需要被配置成spring bean,从而减少对<bean> 元素的使用。
l byName 把与bean的属性具有相同名字(或者id)的其他bean自动装配到bean的对应属性中。如果没有跟属性的名字匹配的bean,则该属性不进行装配。
l byType 把与Bean的属性具有相同类型的其他bean自动装配到bean的对应属性中。如果没有跟属性的类型匹配的bean,则该属性不进行装配。
l constructor 把与bean的构造方法入参具有相同类型的其他bean自动装配到bean的构造方法对应的入参中。
l autodetect 首先尝试使用constructor进行自动装配,如果失败,再尝试使用byType进行自动装配。
手动装配:
<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自动装配时,spring会寻找哪一个bean的类型与属性的类型相匹配。但byType自动装配存在一个局限性,如果spring寻到到多个bean,它们的类型与需要自动装配的属性的类型都匹配,则spring会抛出异常。所有,应用只允许存在一个bean与需要自动装配的属性类型相匹配。
为了避免使用byType自动装配带来的歧义,spring提供2种选择:可以为自动装配标识一个首选bean,或者可以取消某个bean自动装配的候选资格。
为自动装配标识一个首选bean,可以使用<bean>元素的primary属性。如果只有一个候选bean的primary属性为true,那么该bean比其他候选bean优先被选择。但是primary 默认设置为true。
如果在自动装配时,我们希望排除某些bean,则可以设置这些bean的autowire-candidate属性为false,这里我们要求spring在自动装配时忽略bean作为候选bean。
如果要通过构造方法注入来配置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>
使用@Autowired让spring自动装配Bean的city属性,则可以对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注解就会遇到一些麻烦。但是这两种情况都有相应的解决办法。如下
属性不一定非要装配,null也是可以接受的。在这种情况下,可以设置@Autowired的required属性为false来配置自动装配是可选的。这样,spring将先尝试装配,如果找不到与之匹配的bean,应用不会发生任何问题,而属性值会被设置为null。
当与之匹配的bean有多个的时候,可以使用@Qualifier注解来指明使用id来匹配bean,例如
@Autowired
@Qualifier(“ShangHai”)
private City city;
如上,@Qualifier注解将尝试注入id为”ShangHai” 的bean。
spring3.0引入了@Value,可以让我们使用注解装配String类型的值和基本类型的值,例如int、boolean。例如
@Value(“Tom Clone”)
private String name;
在这里,为String类型的属性name装配了一个String类型的值。
借助于SpEL表达式,使得@Value成为强大的装配可选方案。
@Value(#{city[0].name})
private String name;
相当于<bean>元素中的scope属性
相当于init-method
相当于destory-method
<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属性标识了所扫描的包。
默认情况下,<context:component-scan>查找使用构造型(stereotype)注解所标注的类,这些特殊的注解如下
n @Component 通用的构造型注解,标识该类为spring组件
n @Controller 标识将该类定义为spring mvc controller
n @Repository 标识将该类定义为数据仓库
n @Service 标识将该类定义为服务
n 使用@Component 标注的任意自定义注解
可以使用 @Component(“ id ”)在括号里显式的指定bean的id
在如何扫描来获得候选bean方面,<context:component-scan>非常灵活。通过<context:component-scan>配置<context:include-filter>或者<context:exclude-filter>子元素,可以随意调整扫描行为。
<context:include-filter>的type和expression属性一起写作来定义组件扫描策略。
过滤器类型 |
描述 |
annotation |
过滤器扫描使用指定注解标注的那些类,通过expression属性指定要扫描的注解 |
assignable |
过滤器扫描派生于expression属性所指定类型的那些类
|
aspectj |
过滤器扫描与expression属性所指定的AspectJ表达式所匹配的那些类 |
custom |
使用自定义的org.springframework.core.type.TypeFilter实现类,该类由expression属性指定 |
regex |
过滤器扫描类的名称与expression属性所指定的正则表达式所匹配的类 |
除了使用<context:include-filter>告知哪些类需要注册为bean以外,还可以使用<context:exclude-filter>告知哪些类不需要注册为bean。
描述切面常用的术语有通知(advice)、切点(pointcut)和连接点(join point)。
在AOP术语中,切面的工作被称之为通知。通知定义了切面是什么以及何时使用。除了描述切面要完成的工作,通知还解决了何时执行这个工作的问题。它应该应用于某个方法被调用之前?之后?之前和之后?还是只在方法抛出异常时?
spring切面可以应用5种类型的通知
n Before 在方法被调用之前调用通知
n After 在方法完成之后调用通知,无论方法执行是否成功
n After-returning 在方法成功执行之后调用通知
n After-throwing 在方法抛出异常后调用通知
n Around 通知包裹了被通知的方法,被通知的方法调用之前和调用之后执行自定义的行为。
连接点是在应用执行过程中能够插入切面的一个点。这个店可以是调用方法时、抛出异常时、甚至修改一个字段时。切面代码可以利用这些点插入到应用的正常流程之中,并添加新的行为。
一个切面并不需要通知应用的所有连接点。切点有助于缩小切面所通知连接点的范围。
切面是通知和切点的结合。通知和切点共同定义了关于切面的全部内容-它是什么,在何时和何处完成其功能。
引用允许我们向现有的类添加新的方法或属性。
织入是将切面应用到目标对象来创建新的代理对象的过程。切面在指定的连接点被织入到目标对象中。
l Before 在方法被调用之前调用通知
l After 在方法完成之后调用通知,无论方法是否执行成功
l After-returnning 在方法成功执行之后调用通知
l After-throwing在方法抛出异常后调用通知
l Around 通知包裹了被通知的方法,被通知的方法调用之前和调用之后执行自定义的行为。
首先,需要在xml文件中打开aop,在xml文件中添加以下代码
<aop:aspectj-autoproxy/>
添加aspectJ的依赖包aspectjweaver.jar。
在切面类上使用注解@Aspect,并且使用注解@Component或者xml文件配置方式把切面类配置成bean。
使用execution()指示器选择Instrument的play()方法。方法表达式以*号开始,标识了不需要关心方法返回值的类型,然后我们置顶了全限定类名和方法名。对于方法参数列表,使用( . . )标识切点选择任意的play()方法,无论该方法的入参是什么。
现在假设我们需要配置切点仅匹配com.springinaction.springidol包。此时可以使用withdin()指示器来限制匹配。如下
execution(*com.springincation.springidol.Instrument.play(..))&&within(com.springincation.springidol.*)
注意,使用&&把execution()和within()指示器连接在一起形成and关系。类似,也可以使用||、!操作符,在xml文件中,&符号具有特殊意义,所以使用and代替&&。同样可以使用or、not。
注意,bean()指示器允许我们在切点表达式中使用bean 的id来标识bean。bean()指示器使用bean id或bean名称作为参数来限制切点只匹配特定的bean。
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:*>元素必须包含在此元素下 |
本文出自 “飞鱼技术” 博客,转载请与作者联系!