承接前一篇Spring --- IOC,继续IOC的介绍
6) 方法注入
首先说说方法注入的使用场景:
当一个singleton bean A 在每次方法调用的时候都需要一个non-singleton bean B,此时就会产生这样一个问题,因为A为singleton,所以容器只会创建一次A,那么也只有一次机会来创建A的属性,无论你是通过setter还是constructor方式注入Bean B,Bean B也只能被初始化一次,而实际需求是我们调用每个方法的时候又都需要一个新的Bean B实例。而方法注入就是解决这个问题的方法之一。具体看看如何实现:
Spring 会自动通过CGLIB动态生成一个子类,继承CommandManager ,并重写原有的createCommand()方法,再根据配置信息createCommand()方法会自动返回一个command bean。
还有一种任意方法的替代方式,不过这不太常用,这里写出来仅供参考吧:
MyValueCalculator 的computeValue方法将被ReplacementComputeValue中的reimplement方法中对应的逻辑所取代。
7) Bean scopes
spring的Bean scopes一共有五种: singleton、prototype、request、session、global session,大致可以分为两类:
第一类是singleton和prototype, singleton表示单件模式,prototype在每次请求该Bean时会新建一个实例。其中singleton是spring bean的缺省值。
第二类是request,session,global session。它们是专用于Web应用程序上下文里的Bean的范围的。要使用这3个属性值之前,我们需要先在web应用的web.xml文件中添加如下配置:
对于spring2.4+的版本:
而对于spring2.3及以下版本,添加的配置应为:
[b]request[/b]表示该针对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP request内有效。
[b]session[/b]作用域表示该次HTTP请求会产生一个新的bean,同时该beand的生命周期与当前HTTP session一样。
[b]global session[/b]与session类似,只是它的生命周期与global portlet session一致。(要理解global session需要知道portlet的概念,为了突出重点,这里就不对portlet作进一步的说明了。[url]http://www.lhjy.net/RecruiStu/JSJ/200512/20350.html[/url])
最后,当一个singleton的bean需要关联一个request session或global session 的时候,因为singleton的bean只会实例化一次,所以我们需要一个代理来实现userPreference的scope
8) bean的生命周期
[b]bean的初始化:[/b]
1' 声明初始化函数:
2' 继承InitializingBean 接口:
[b]bean的销毁:[/b]
1'声明销毁函数:
2' 继承DisposableBean接口:
[b]在Spring启动和关闭时被调用的callback方法:[/b]
首先是类的层次关系:
当ApplicationContext启动或停止时,它会通过LifecycleProcessor来与所有声明的bean的周期做状态更新。而SmartLifecycle的用法如下:
isAutoStartup返回的值决定是否在Spring启动的时候运行start方法,ture的话则运行.
isRunning返回的值决定是否在Spring关闭的时候运行stop(Runnable arg0)方法,ture的话则运行.
getPhase返回的值决定启动的顺序,值越小越先启动,越后关闭,用于调整在有多个这样的bean的时候,这些bean之间启动的优先级.
非web环境下,需要设置ctx.registerShutdownHook(),Spring容器关闭的时候才会调用stop方法.
6) 方法注入
首先说说方法注入的使用场景:
当一个singleton bean A 在每次方法调用的时候都需要一个non-singleton bean B,此时就会产生这样一个问题,因为A为singleton,所以容器只会创建一次A,那么也只有一次机会来创建A的属性,无论你是通过setter还是constructor方式注入Bean B,Bean B也只能被初始化一次,而实际需求是我们调用每个方法的时候又都需要一个新的Bean B实例。而方法注入就是解决这个问题的方法之一。具体看看如何实现:
package fiona.apple;
// no more Spring imports!
public abstract class CommandManager {
public Object process(Object commandState) {
// grab a new instance of the appropriate Command interface
Command command = createCommand();
// set the state on the (hopefully brand new) Command instance
command.setState(commandState);
return command.execute();
}
// okay... but where is the implementation of this method?
protected abstract Command createCommand();
}
<!-- a stateful bean deployed as a prototype (non-singleton) -->
<bean id="command" class="fiona.apple.AsyncCommand" scope="prototype">
<!-- 具体配置这里省略 -->
</bean>
<!-- commandProcessor uses statefulCommandHelper -->
<bean id="commandManager" class="fiona.apple.CommandManager">
<lookup-method name="createCommand" bean="command"/>
</bean>
Spring 会自动通过CGLIB动态生成一个子类,继承CommandManager ,并重写原有的createCommand()方法,再根据配置信息createCommand()方法会自动返回一个command bean。
还有一种任意方法的替代方式,不过这不太常用,这里写出来仅供参考吧:
public class MyValueCalculator {
public String computeValue(String input) {
// some real code...
}
// some other methods...
}
/** meant to be used to override the existing computeValue(String)
implementation in MyValueCalculator
*/
public class ReplacementComputeValue implements MethodReplacer {
public Object reimplement(Object o, Method m, Object[] args) throws Throwable {
// get the input value, work with it, and return a computed result
String input = (String) args[0];
...
return ...;
}
}
<bean id="myValueCalculator" class="x.y.z.MyValueCalculator">
<!-- arbitrary method replacement -->
<replaced-method name="computeValue" replacer="replacementComputeValue">
<arg-type>String</arg-type>
</replaced-method>
</bean>
<bean id="replacementComputeValue" class="a.b.c.ReplacementComputeValue"/>
MyValueCalculator 的computeValue方法将被ReplacementComputeValue中的reimplement方法中对应的逻辑所取代。
7) Bean scopes
spring的Bean scopes一共有五种: singleton、prototype、request、session、global session,大致可以分为两类:
第一类是singleton和prototype, singleton表示单件模式,prototype在每次请求该Bean时会新建一个实例。其中singleton是spring bean的缺省值。
第二类是request,session,global session。它们是专用于Web应用程序上下文里的Bean的范围的。要使用这3个属性值之前,我们需要先在web应用的web.xml文件中添加如下配置:
对于spring2.4+的版本:
<web-app>
...
<listener>
<listener-class>
org.springframework.web.context.request.RequestContextListener
</listener-class>
</listener>
...
</web-app>
而对于spring2.3及以下版本,添加的配置应为:
<web-app>
...
<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>
...
</web-app>
[b]request[/b]表示该针对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP request内有效。
[b]session[/b]作用域表示该次HTTP请求会产生一个新的bean,同时该beand的生命周期与当前HTTP session一样。
[b]global session[/b]与session类似,只是它的生命周期与global portlet session一致。(要理解global session需要知道portlet的概念,为了突出重点,这里就不对portlet作进一步的说明了。[url]http://www.lhjy.net/RecruiStu/JSJ/200512/20350.html[/url])
最后,当一个singleton的bean需要关联一个request session或global session 的时候,因为singleton的bean只会实例化一次,所以我们需要一个代理来实现userPreference的scope
<bean id="userPreferences" class="com.foo.UserPreferences" scope="session">
<aop:scoped-proxy/>
</bean>
<bean id="userManager" class="com.foo.UserManager">
<property name="userPreferences" ref="userPreferences"/>
</bean>
8) bean的生命周期
[b]bean的初始化:[/b]
1' 声明初始化函数:
<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>
public class ExampleBean {
public void init() {
// do some initialization work
}
}
2' 继承InitializingBean 接口:
<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
public class AnotherExampleBean implements InitializingBean {
public void afterPropertiesSet() {
// do some initialization work
}
}
[b]bean的销毁:[/b]
1'声明销毁函数:
<bean id="exampleInitBean" class="examples.ExampleBean" destroy-method="cleanup"/>
public class ExampleBean {
public void cleanup() {
// do some destruction work (like releasing pooled connections)
}
}
2' 继承DisposableBean接口:
<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
public class AnotherExampleBean implements DisposableBean {
public void destroy() {
// do some destruction work (like releasing pooled connections)
}
}
[b]在Spring启动和关闭时被调用的callback方法:[/b]
首先是类的层次关系:
public interface Lifecycle {
void start();
void stop();
boolean isRunning();
}
public interface LifecycleProcessor extends Lifecycle {
void onRefresh();
void onClose();
}
public interface Phased {
int getPhase();
}
public interface SmartLifecycle extends Lifecycle, Phased {
boolean isAutoStartup();
void stop(Runnable callback);
}
当ApplicationContext启动或停止时,它会通过LifecycleProcessor来与所有声明的bean的周期做状态更新。而SmartLifecycle的用法如下:
public class LfCycleBean1 implements SmartLifecycle{
protected static final Log log = LogFactory.getLog(LfCycleBean1.class);
private boolean isRunning = false;
@Override
public boolean isAutoStartup() {
// TODO Auto-generated method stub
return true;
}
@Override
public void stop(Runnable arg0) {
arg0.run();
isRunning = false;
log.info("stop Runnable");
}
@Override
public boolean isRunning() {
// TODO Auto-generated method stub
return isRunning;
}
@Override
public void start() {
isRunning = true;
log.info("start ");
}
@Override
public void stop() {
isRunning = false;
log.info("stop");
}
@Override
public int getPhase() {
return -1;
}
}
<bean id="LfCycleBean1" class="com.test.spring.lifecycle.LfCycleBean1" />
isAutoStartup返回的值决定是否在Spring启动的时候运行start方法,ture的话则运行.
isRunning返回的值决定是否在Spring关闭的时候运行stop(Runnable arg0)方法,ture的话则运行.
getPhase返回的值决定启动的顺序,值越小越先启动,越后关闭,用于调整在有多个这样的bean的时候,这些bean之间启动的优先级.
非web环境下,需要设置ctx.registerShutdownHook(),Spring容器关闭的时候才会调用stop方法.
AbstractApplicationContext ctx
= new ClassPathXmlApplicationContext(new String []{"beans.xml"});
ctx.registerShutdownHook();