Spring学习6(4)
在上一篇笔记中述写了spring配置bean的基本格式和方式后,这篇笔记的主要内容是继续了解bean配置的一些进阶的功能和基本概念。
方法注入
假设Boss配置为单例模式(一个容器中只存在一个实例的bean),如果我们希望每次调用Boss Bean的getCar()
方法都能得到一新的car Bean,使用传统的注入就无法实现这样的要求了,因为boss是单例的。
我们可以使用BeanFactoryAware接口,访问容器的引用,代码如下:
public Car getCar(){
return (Car)factory.getBean("car");
}
但是这样的操作会让代码和spring框架绑定,不是特别灵活,所以我们可以利用spring提供的新方法来完成同样的功能。
lookup方法注入
spring IoC容器利用CGLib包可以拥有复写Bean方法的能力,从而可以为Bean动态创建子类或实现类。
把car.java放到com.smart.injectfun包下,并且在这个包下创建MagicBoss.java,代码如下:
package com.smart.injectfun;
public interface MagicBoss{
Car getCar();
}
下面我们可以不写任何的实现类,仅仅通过配置就为该接口提供动态的实现:
<bean id="car" class="com.smart.injectfun.Car"
p:brand="HongQi" p:price="2000" scope="prototype"/>
<bean id="magicBoss" class="com.smart.injectfun.MagicBoss">
<lookup-method name="getCar" bean="car"/>
</bean>
注意每次都是要返回新的carBean,所以Car的scope要为prototype,所以lookup方法注入一般在希望通过一个singleton Bean获取一个prototype Bean时使用。
方法替换
在spring容器中可以使用某个Bean的方法去替换另一个Bean的方法。
下面的例子中Boss1的getCar()方法返回一辆宝马Z4:
package com.smart.injectfun;
public class Boss1{
public Car getCar() {
Car car = new Car();
car.setBrand("宝马Z4");
return car;
}
}
在Boss2中实现org.springframework.beans.factory.support.MethodReplacer
接口,在接口方法reimplement()
中放回一辆美人豹:
package com.smart.injectfun;
import java.lang.reflect.Method;
import org.springframework.beans.factory.support.MethodReplacer;
public class Boss2 implements MethodReplacer{
public Object reimplement(Object arg0, Method arg1, Object[] arg2)
throws Throwable{
Car car = new Car();
car.setBrand("美人豹");
return car;
}
}
注意用于替换他人的Bean必须是新啊MethodReplacer接口。
在配置中我们使用如下代码:
<bean id="boss1" class="com.smart.injectfun.Boss1">
<replaced-method name="getCar" replacer="boss2"/>
</bean>
<bean id="boss2" class="com.smart.injectfun.Boss2"/>
<bean>
之间的关系
不但可以使用<ref>
来引用另一个Bean,<bean>
标签之间本身亦可以建立类似的关系。
继承
spring允许当多个<bean>
有重复的信息的时候,定义一个父<bean>
来简化述写,下面是一个重复读很高,没有使用父<bean>
的配置:
<bean id="car1" class="com.smart.tagdepend.Car"
p:brand="HongQi" p:price="2000" p:color="black"/>
<bean id="car2" class="com.smrt.tagdepend.Car"
p:brand="HongQi" p:price="2000" p:color="red"/>
针对上述这种只有color属性不一样的<bean>
配置情况,我们使用如下的代码来替代:
<bean id="abstractCar" class="com.smart.tagdepend.Car"
p:brand="HongQi" p:price="2000" p:color="black" abstract="true"/>
<bean id="car3" p:color="red" parent="abstractCar"/>
<bean id="car4" p:color="green" parent="abstractCar"/>
注意这里父<bean>
要申明是abstract="true"
这样就不会实例化为对应的Bean。在子<bean>
中提供了父<bean>
已有的配置信息将覆盖父<bean>
中的配置。
依赖
一般情况下,可以使用<<ef>
提供依赖,但是有时候Bean之间的依赖关系不是那么明显。
如一个系统在开启时会配置基础的环境信息,但是参数会随着环境演进而变化,我们需要对其进行定时存储,但是每次存储时我们去读取的是配置的信息,而没有去读取是否发生参数的改变,所以其中存在着隐形的依赖,即是只有更改过了参数刷新才有效。
<bean id="sysfresh" class="com.smart.tagdepend.SysInit"/>
<bean id="manager" class="com.smart.tagdepend.CacheManager" depends-on="sysfresh"/>
引用
假设一个<bean>
要引用另一个<bean>
的id属性值(一般是用来在运行时使用getBean(beanName)方法来获取对应的Bean,并且要有引用关系,我们可以通过下面的代码来实现:
<bean id="boss" class="com.smart.tagdepend.Boss">
<property name="carId">
<idref bean="car"/>
</property>
</bean>
整合多个配置文件
对于一个大型应用程序来说可能存在多个配置文件,spring提供了一种<import>
元素标签将多个配置文件引入到一个文件中,进行配置文件继承,这样在spring加载的时候就只要指定这个合并好的文件即可。
<import resource="classpath:com/smart/impt/beans1.xml/>
<import resource="classpath:com/smart/impt/beans2.xml/>
需要注意的是如果一个配置文件引用了其他配置文件的<bean>
,是不用import的,只需要将这两个配置文件都放入配置文件列表即可。
Bean作用域
用户不但可以在配置文件中配置Bean的属性和相互之间的关系,还可以控制作用域。下面是预定义的5中定义域:
- singleton:在容器中仅存在一个Bean实例。
- prototype:每次使用getBean()都会返回一个新的实例。
- request:每次Http请求都会创建一个新的Bean,该作用域适用于WebApplicationContext环境。
- session:同一个Http session共享一个Bean,仅使用于WebApplicationContext环境。
- globalSession:同一个全局Session共享一个Bean,一般用于portlet应用环境,仅适用于WebApplicationContext环境。
singleton作用域
如下列一行配置代码,其意义是如图所示:
<bean id="car" class="com.smart.scope.Car" scope="singleton" />
<bean id="boss1" class="com.smart.scope.Boss" p:car-ref="car"/>
<bean id="boss2" class="com.smart.scope.Boss" p:car-ref="car"/>
<bean id="boss3" class="com.smart.scope.Boss" p:car-ref="car"/>
结构如下:
prototype 作用域
代码的结构和容器的结构如下:
<bean id="car" class="com.smart.scope.Car" scope="prototype" />
<bean id="boss1" class="com.smart.scope.Boss" p:car-ref="car"/>
<bean id="boss2" class="com.smart.scope.Boss" p:car-ref="car"/>
<bean id="boss3" class="com.smart.scope.Boss" p:car-ref="car"/>
与Web相关的Bean作用域
在使用WebApplicationContext,使用与web有关的bean作用域需要进行一些额外的配置。我们在web.xml中为http请求配置“驱动”。
同第四章配置contextLoaderListener,这里也有两种配置方法,首先是通过过滤器:
<filter>
<filter-name>requestContextFilter</filter-name>
<filter-class>org.springframework.web.filter.RequestContextFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>requestContextFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
或者通过http请求监听器:
<listener>
<listener-class>
org.springframework.web.context.request.RequestContextListener
</listener-class>
</listener>
完成了上述的配置后,就可以对bean进行配置。
request:
<bean id="car" class="com.smart.scope.Car" scope="request" />
每次http请求调用car Bean时spring都会创建新的car,处理完毕后会销毁这个bean。
session:
<bean id="car" class="com.smart.scope.Car" scope="session" />
其横跨整个session,只有http session结束后,实例才被销毁。
globalSession:
<bean id="car" class="com.smart.scope.Car" scope="globalSession" />
&emsp;仅在portlet的web应用中使用,如果不是,则相当于session。
web中的bean的依赖
在web中相关作用域的Bean注入singleton或prototype中,需要额外的配置即是需要用Aop来设置代理,而不能直接引用。
下面是一个非webBean引用webBean的实例代码:
<?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:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop
/spring-aop-4.0.xsd
">
<bean id="car" class="com.smart.scope.Car" scope="request" />
<bean id="boss1" class="com.smart.scope.Boss" p:car-ref="car"/>
<bean id="boss2" class="com.smart.scope.Boss" p:car-ref="car"/>
<bean id="boss3" class="com.smart.scope.Boss" p:car-ref="car"/>
<bean name="car" class="com.smart.scope.Car" scope="request">
<aop:scoped-proxy/>
</bean>
<bean id="boss" class="com.smart.scope.Boss">
<property name="car" ref="car"/>
</bean>
</beans>
这里car Bean是request作用域,为了能够被singleton引用,需要用aop配置一个代理类,aop会判断car Bean定位于那个Http请求线程中,并从对应的http请求线程获取对应car Bean。并且这里Boss是singleton作用域,所以每次调用carBean都会创建一个car,但是通过aop可以让boss每次引用都对应到http请求的car Bean中。