有个类实现了org.springframework.beans.factory.support.MethodReplacer
接口,类中有新的方法定义
/**
* 意味着用来重写MyValueCalculator类中computeValue(String)方法的实现
*/
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定义,用来部署的源类,要设置方法重写,大概这么搞:
<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"/>
可以在<replaced-method/>
元素内设置一个或多个<arg-type/>
元素来指明被替换方法的参数类型。只有被覆盖的方法在类有重载,参数签名才是必要的。为了方便,String
类型的参数只需要其完全限定类型名称的字串即可。比如,下面列出的均可匹配java.lang.String
:
java.lang.String
String
Str
因为参数的数量基本就可以确定方法(重载的方法,基本上是参数数量有区别),此简写能大量减少打字,让你仅打几个字符就能匹配参数类型。
译注,Spring是工业品质的框架,如此细微的人性化设计,值得学习
<h3 id='beans-factory-scopes'>bean作用域</h3>
Spring bean定义时,实际上是创建类实例的配方。这个观点非常重要,因为她意味着,通过一个配方,即可创建很多类的对象。
对于依据bean定义产生的bean,不仅可以控制依赖、设置对象的值,还可以对象作用域。这个手法强大而灵活,因为在配置过程中就可以可以控制的bean的作用域,无需在代码层面去控制,用代码去控制简直就是煎熬。要部署的bean可有设置1个或多个作用域:开箱即用,Spring框架支持5中作用域,其中有三种只有用web-awareApplicationContext
才能使用。
下面了列出的作用域开箱即用,你也可以自定义作用域
Table 5.3. Bean scopes
作用域 | 描述 |
---|---|
单例singleton | 默认的。一个bean定义,在一个IoC容器内只会产生一个对象。 |
prototype原型 | 一个bean定义会产生多个对象实例 |
request请求 | 一个bean定义产生的bean生命周期为一个HTTP请求;也就是,每一个HTTP请求都会根据bean定义产生一个对象实例。该作用域只有在Spring web上下文环境中才有效。 |
session会话 | 产生的bean生命周期在HTTP 会话期间。该作用域只有在Spring web上下文环境中才有效 |
gloabal session全局session | 声明周期为全局HTTP会话。通常使用portlet context时常用。该作用域只有在Spring web上下文环境中才有效。 |
application应用 | 生命周期与ServletContext 一样。该作用域只有在Spring web上下文环境中才有效 |
Spring3.0起 多了一个作用域-thred,但它默认是未注册的(不可用的意思?)。详情请参看文档去吧
SimpleThreadScope
。有关如何注册该作用域和注册自定义作用域,参看本章使用自定义作用域
<h4 id="#beans-factory-scopes-singleton">单例作用域</h4>
单例bean只会产生一个实例,对于所有的请求,Spring容器都只会返回一个实例。
换句话说,当定义了单例bean,Srping容器只会创建一个实例,这个实例存储在单例池中,单例池应该属于缓存,接下来所有对于该单例bean的请求和引用,都将返回缓存中的对象。
Figure 5.2.
Spring单例bean的概念,和四人帮GOF那本《设计模式》中定义的单例模式不同。GOF的单例是硬编码级的对象作用域,因此导致每一个类加载器内会产生单例类的一个实例。Spring的单例恰如其名,在容器范围内只会产生一个类实例。Spring中,bean默认的作用域都是单例作用域。使用xml 定义单例bean,像这样:
<bean id="accountService" class="com.foo.DefaultAccountService"/>
<!-- 和下面的写法相等,因为单例作用域是默认的,所以这么写有些画蛇添足,意思就是废话了 -->
<bean id="accountService" class="com.foo.DefaultAccountService" scope="singleton"/>
<h4 id='beans-factory-scopes-prototype'>prototype原型作用域</h4>
设置bean作用域为prototype
,就是非单例,对于每次请求都将返回一个该类的新实例。也就是说,原型bean注入另一个bean,或者是请求原型bean,都是通过在容器上调用getBean()
方法产生的。一般来说 ,原型bean用于有状态bean,单例bean用于无状态bean。
下图示例了Srping原型作用域。一个数据访问对象(DAO)通常不会配置成原型作用域,因为通常DAO不会持有任何会话状态;因为作者偷懒,所以重用了上面单例示意图。
接下来看看如何在XML中定义原型bean:
<bean id="accountService" class="com.foo.DefaultAccountService" scope="prototype"/>
和其他作用域相比,Srping并不管理原型bean的完整的生命周期:容器实例化,配置或者组装原型独享,注入给其他类,然后并未进一步记录那个原型bean。因此,尽管对象的初始化回调方法会调用,不受scope影响,但是对于原型bean,销毁回调不会被调用。客户端代码必须清理原型对象并且释放原型bean持有的资源。为了让Spring容器释放原型bean持有的资源,可以用自定义的bean[post-processor](#beans-factory-extension-bpp)
,
它持有需要被清理bean的引用。
某种意义上,对于原型bean来说,Spring容器的角色就是替换了new 操作符。所有的生命周期管理,在经过实例化之后,都需要由客户端来处理。(Spring 容器中bean的生命周期详情,请参看本章5.6.1生命周期回调)
<h4 id='beans-factory-scopes-sing-prot-interaction'>单例依赖原型</h4>
单例类依赖了原型类,要知道依赖在单例类初始化的时候就已经注入好了。因此,若你注入了一个原型bean给单例bean,将会是一个新的原型bean的实例注入了单例bean实例。原型bean实例将会是唯一的实例,再也不会为单例bean产生新的实例。
假若你需要单例bean在运行时重复的获取新的原型bean实例。那就不能将原型bean注入给单例bean,因为那样注入只会发生一次,就是发生在在Srping容器实例化单例bean并解析注入依赖时。如果需要多次获取新的原型bean实例,参看本章5.4.6方法注入
<h4 id='beans-factory-scopes-other'> Request, session, and global session scopes</h4>
request
,session
,global session
作用域,只有在spring web ApplicationContext
的实现中(比如XmlWebApplicationContext
)才会起作用,若在常规Spring IoC容器中使用,比如ClassPathXmlApplicationContext
中,就会收到一个异常IllegalStateException
来告诉你不能识别的bean作用域
<h5 id='beans-factory-scopes-other-web-configuration'>初始化web配置</h5>
为了支持request,sesssion,global session
这种级别bean的作用域(web作用域bean),在定义bean之前需要一些初始化的小配置。(Spring标准作用域,包括单例和原型,无需此配置。)
如何配置要根据具体的Servlet
环境
若使用 Spring Web MVC访问这些作用域bean,实际上是使用Srping DispatcherServlet
类或者DispatcherPortlet
类处理request,则无需特别配置:DispatcherServlet
和 DispatcherPortlet
已经暴露了所有的相关状态。
若使用了Servlet 2.5的web容器,使用了非Spring的DispacherServlet
处理请求(比如,JSF或者Struts),则需要注册org.springframework.web.context.request.RequestContextListener ServletRequestListener
。若使用的Servlet 3.0+,这些设置可以通过编程式方式使用WebApplicationInitializer
接口完成。若使用的是较老的容器,增加下面配置添加到你的web应用的web.xml
文件中:
<web-app>
...
<listener>
<listener-class>
org.springframework.web.context.request.RequestContextListener
</listener-class>
</listener>
...
</web-app>
如果设置listener
有问题的话,可以考虑使用RequestContextFilter
。filter映射要根据web 应用配置来调整:
<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>
DispatcherServlet
,RequestContextListener
,RequestContextFilter
都是做相同的事儿,也就是绑定HTTP
request对象到服务的Thread
线程中,并开启接下来
用到的session-scoped
功能。
<h5 id='beans-factory-scopes-request'>Request作用域</h5>
考虑下面这种bean定义:
<bean id="loginAction" class="com.foo.LoginAction" scope="request"/>
Spring 使用该bean定义为每一次HTTP 请求创建一个新的LoginAction
bean 的实例。也就是,loginAction
bean作用域范围在HTTP 请求级别。可以改变实例的内部状态,多少实例都可以,因为根据此loginAciton
bean定义创建的其他bean实例并不会看到这些状态的改变;他们为各自的request拥有。当reqeust完成处理,request作用的bean就被丢弃了。
<h5 id="beans-factory-scopes-session">session作用域</h5>
考虑下面这种bean定义:
<bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/>
在一个session会话期间,Spring容器使用userPreferences
定义创建了一个UserPreferences
bean的实例。换句话说userPreferences
bean在HTTP Session会话期间有效。和request-scoped
bean相类似,可以改变bean实例的内部状态,不管bean创建了多少实例都可以,要知道,使用相同的userPreferences
定义创建的其他的bean实例看不到这些状态的改变,因为他们都是为各自的HTTP Session服务的。当HTTP Session最终被丢弃时,该session内的session-scoped
作用域的bean实例也会被丢弃。
<h5 id="beans-factory-scopes-global-session">全局Session作用域</h5>
考虑下面这种bean定义:
<bean id="userPreferences" class="com.foo.UserPreferences" scope="globalSession"/>
全局session作用域与标准HTTP Session作用域类似,仅能应用于基于portlet的web应用的上下文环境中。portlet规范中定义的global Session
概念是,在由单个portlet web应用创建的所有的的portlets中共享。全局session作用域的bean和global portlet Session
全局portlet会话生命周期相同。
若是在标准的基于Servelt web应用中定义了全局session作用域bean,那么将会使用标准的Session作用域,不会报错。
<h5 id="beans-factory-scopes-application">应用作用域</h5>
考虑下面这种bean定义:
<bean id="appPreferences" class="com.foo.AppPreferences" scope="application"/>
Spring 容器使用该定义为整个web应用创建一个AppPreferences
bean的实例。appPreFerences
bean作用域是ServeletContext
级别,存储为一个常规的ServletContext
属性。这个Spring单例作用域有几分相似,但是和单例作用域相比有两个重要不同:1、他是每一个ServeltContext
一个实例,而不是SpringApplicationContext
范围。2、它是直接暴露的,作为ServletContext
属性,因此可见。
<h5 id='beans-factory-scopes-other-injection'>不同级别作用域bean之间依赖</h5>
Spring IoC容器不仅管理bean的实例化,也负责组装(或者依赖)。如果想将HTTP request作用域bean注入给其他bean,就得给作用域bean(request或者session)注入一个AOP代理用来替换作用域bean。通过注入一个代理对象暴露于作用域bean相同的的接口,他是代理对象也能从相关作用域(request或者session)中检索到真正的被代理对象,并委派方法调用实际对象的方法。
不需要再单例或者原型bean内部使用
<aop:scoped-proxy/>
下面的配置虽然简单,但是重要的理解“为什么”和“如何搞”
<?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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- an HTTP Session-scoped bean exposed as a proxy -->
<bean id="userPreferences" class="com.foo.UserPreferences" scope="session">
<!-- instructs the container to proxy the surrounding bean -->
<aop:scoped-proxy/>
</bean>
<!-- a singleton-scoped bean injected with a proxy to the above bean -->
<bean id="userService" class="com.foo.SimpleUserService">
<!-- a reference to the proxied userPreferences bean -->
<property name="userPreferences" ref="userPreferences"/>
</bean>
</beans>
为了创建一个代理,得在作用域bean定义内插入子元素<aop:scoped-proxy/>
。详情参看 “Choosing the type of proxy to create” 和 Chapter 34, XML Schema-based configuration.)。request
, session
, globalSession
, custom-scope
,为什么这些级别的作用域需要<aop:scoped-proxy/>
元素? 下面来做个小测验,一个单例bean定义,对比一下,它如果要实现前面提到的作用域bean注入,该如何配置。(下面的userPreferences
bean,实际上并不完整)。
<bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/>
<bean id="userManager" class="com.foo.UserManager">
<property name="userPreferences" ref="userPreferences"/>
</bean>
上例中,HTTP Session作用域beanuserPreferences
注入给了单例beanuserManger
。注意,userManager
bean是一个单例bean:每个容器只会实例化一个,他的依赖(本例中只有一个,userPreferences
bean)也仅会注入一次。也就是说,userManager
bean只能操作相同的userPreferences
对象,就是注入的那一个。
将一个短生命周期作用域bean注入给长生命周期作用域bean,比如将HTTP Session作用域bean作为依赖注入给一个单例bean。然而,你需要一个userManager
对象,在HTTP Session会话期间,需要与session同生命周期的对象userPreferences
。 因此,容器会创建一个对象,该对象拥有和UserPreferences
完全相同的public接口并暴露所有的public接口。,该对象能根据作用域机制获取真真的UserPreferences
对象。容器会将这个代理对象注入给userManager
bean,userManager
类则浑然不知这货居然是个代理。样例中,当UserManager
实例调用依赖UserPreferences
对象上的方法时,,实际上调用的是代理对象上的方法。代理对象从 Session
范围内获取真正的UserPreferences
对象,并将在代理对象上方法的调用“呼叫转移”给检索到的真正的UserPreferences
对象。
将一个request
,session
,globalSession
作用域bean注入给其他作用域bean,下面是正确的、完整的配置
<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>
<h5 id='beans-factory-scopes-other-injection-proxies'>选择代理类型</h5>
使用<aop:scoped-proxy/>
元素为bean 创建代理时,Spring 容器默认使用CGLIB
类型创建代理。
CGLIB代理只会拦截public方法调用。非public方法不会“呼叫转移”给实际的作用域bean。
还有个选择,通过配置,使Spring容器为这些作用域bean创建标准的JDK interface-based
代理,设置<aop:scoped-proxy/>
元素proxy-target-class
属性的值为false
即可。使用标准JDK接口代理好处是无需引入第三方jar包。然而,作用域bean 至少实现一个接口,需要注入作用域bean的类则依赖这些接口。
<!-- DefaultUserPreferences implements the UserPreferences interface -->
<bean id="userPreferences" class="com.foo.DefaultUserPreferences" scope="session">
<aop:scoped-proxy proxy-target-class="false"/>
</bean>
<bean id="userManager" class="com.foo.UserManager">
<property name="userPreferences" ref="userPreferences"/>
</bean>
For more detailed information about choosing class-based or interface-based proxying, see Section 9.6, “Proxying mechanisms”.
关于如何选择class-based
和interface-based
代理,详情参看Section 9.6, “Proxying mechanisms”.
<h4 id='beans-factory-scopes-custom'>自定义作用域</h4>
bean的作用域机制是可扩展的;可以定义自己的作用域,甚至重新定义已存在的作用域,经管后者不推荐,并且,不能重写内置单例作用域和原型作用域。
<h5 id='beans-factory-scopes-custom-creating'>创建自定义作用域</h5>
实现org.springframework.beans.factory.config.Scope
接口,就可以将自定义作用域集成到Srping容器中,本章主要将如何实现该接口。如何实现自定义作用域,参看Spring内置的作用域实现和Scope
类的javadocs,javadocs中解释了有关需要实现的方法的细节。
Scope
接口共有4个方法用于从作用域获取对象、从作用域删除对象、销毁对象(应该是指作用域内,英文档中未提到)
下面的方法作用是返回作用域中对象。比如,session
作用域的实现,该方法返回session-scoped
会话作用域bean(若不存在,方法创建该bean的实例,并绑定到session会话中,用于引用,然后返回该对象)
Object get(String name, ObjectFactory objectFactory)
下面的方法作用是从作用域中删除对象。以session
作用域实现为例,方法内删除对象后,会返回该对象,但是若找不到指定对象,则会返回null
Object remove(String name)
下面的方法作用是注册销毁回调函数,销毁是指对象销毁或者是作用域内对象销毁。销毁回调的详情请参看javadocs或者Spring 作用域实现。
void registerDestructionCallback(String name, Runnable destructionCallback)
下面的方法,用于获取作用域会话标识。每个作用域的标识都不一样。比如,session
作用域的实现中,标识就是session
标识(应该是指sessionId吧)
String getConversationId()
<h5 id='beans-factory-scopes-custom-using'>使用自定义作用域</h5>
可能是HelloWorld
,或者是更多的自定义的Scope
实现,得让Spring知道新写的作用域。下面的方法就是如何注册新的作用域到Spring 容器的核心方法:
void registerScope(String scopeName, Scope scope);
This method is declared on the ConfigurableBeanFactory interface, which is available on most of the concrete ApplicationContext implementations that ship with Spring via the BeanFactory property.
此方法声明在ConfigurableBeanFactory
接口中,该在大部分ApplicationContext
具体实现中都是可用的,通过BeanFactor
属性设置
registerScope(..)
方法第一个参数是作用域名称,该名称具有唯一性。比如Spring容器内置的作用域singleton
和prototype
。第二个参数是自定义作用域实现的实例,就是你想注册的、使用的那个自定义作用域。
写好了自定义作用域的实现,就可以像下面那样注册它了:
下面的
SimpleThreadScope
作用域,是Spring内置的,但是默认并未注册到容器中。
你自定义的作用域实现,应该也使用相同的代码来注册。
Scope threadScope = new SimpleThreadScope();
beanFactory.registerScope("thread", threadScope);
接下来是创建一个bean定义,该定义要遵守自定义作用域的规则
<bean id="..." class="..." scope="thread">
自定义作用域的实现,不局限于编程式注册。也可以使用CustomScopeConfigurer
类声明式注册
*译注*,编程式就是指硬编码,hard-code,声明式就是指配置,可以是xml可以是注解总之无需直接使用代码去撰写相关代码。不得不说,*编程式和声明式*与*硬编码和配置*相比,更加高端大气上档次。技术人员尤其要学习这种官方的、概念性的、抽象的上档次的语言或者说式地道的表达,假若谈吐用的全是这种词汇,逼格至少提升50%,镇住其他人(入行时间不长的同行,或者面试官)的概率将大大提升。当然了,和生人谈吐要用高逼格词汇,比如*声明式*,*编程式*,然而和自己人就要用人话了,比如*硬编码*,*xml配置*,因为他们得能先听懂才能干活。
总之,**装逼用官话,聊天用人话**,闲话少絮,看如何声明式注册(因为此处要装逼,人话是看如何xml)
blablablab
<?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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
<property name="scopes">
<map>
<entry key="thread">
<bean class="org.springframework.context.support.SimpleThreadScope"/>
</entry>
</map>
</property>
</bean>
<bean id="bar" class="x.y.Bar" scope="thread">
<property name="name" value="Rick"/>
<aop:scoped-proxy/>
</bean>
<bean id="foo" class="x.y.Foo">
<property name="bar" ref="bar"/>
</bean>
</beans>
When you place <aop:scoped-proxy/> in a FactoryBean implementation, it is the factory bean itself that is scoped, not the object returned from getObject().
如果在FactoryBean
实现中设置了<aop:scoped-proxy/>
,表示是工厂bean他本身的作用域,并不是getObject()
返回的对象的作用域。TODO
<h3 id='beans-factory-nature'>Customizing the nature of a bean自定义bean的xxx擦这个nature该怎么翻</h3>
<h4 id='beans-factory-lifecycle'>生命周期回调函数</h4>
Spring容器可以控制bean的生命周期,通过实现SpringInitializingBean
和DisposableBean
接口。容器会调用InitializingBean
接口的afterPropertiesSet()
方法,也会调用DisposableBean
接口的destroy()
方法。,也就是运行bean自定义的初始化方法和销毁方法。
Tip
JSR-250中,在现代Spring应用中,一般都是用@PostConstruct
和@PreDestroy
注解定义生命周期回调函数。使用注解的话,你的bean就无需和Spring API耦合了。,详情参看Section 5.9.7, “@PostConstruct and @PreDestroy”
如果不想用JSR-250,但又想解耦(Spring API),可以在定义对象的配置中指定init-method
和destroy-method
Spring使用BeanPostProcessor
实现类处理所有的回调接口并调用相应的方法,接口由Spring 负责查找。若需要自定义功能或其他生命周期行为,Spring并未提供开箱即用的支持,但是可以自己实现BeanPostProcessor
类。详情参看"Section 5.8, “Container Extension Points”
除了initialization
和destruction
方法,Spring bean也可以实现Lifecycle
接口,这些接口可以参与Spring容器生命周期的startup
和shutdown
过程。
本章讲解生命周期回调接口。
<h5 id='beans-factory-lifecycle-initializingbean'>初始化回调</h5>
org.springframework.beans.factory.InitializingBean
接口类的作用是,在容器设置bean必须的属性之后,执行初始化工作。InitializingBean
接口中只有一个方法:
void afterPropertiesSet() throws Exception;
推荐,尽量不用InitializingBean
接口,因为这将导致不必要的与Spring的耦合。还有更好的办法,使用@PostConstruct
注解,或者指定一个POJO的initialization
方法。XML配置元数据中,使用init-method
属性用来指定,其值为初始化方法名,初始化方法得是一个无参无返回值(void)方法。如果使用java Config,得在@Bean
注解中使用initMehtod
属性 ,详情参看 the section called “Receiving lifecycle callbacks”。看代码
<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>
public class ExampleBean {
public void init() {
// do some initialization work
}
}
和下面的效果相同,但上面的没有耦合Spring。
<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
public class AnotherExampleBean implements InitializingBean {
public void afterPropertiesSet() {
// do some initialization work
}
}
<h5 id='beans-factory-lifecycle-disposablebean'>销毁回调</h5>
实现org.springframework.beans.factory.DisposableBean
接口,作用是Spring销毁bean时调用该方法。 DisposableBean
接口只有一个方法:
void destroy() throws Exception;
和上面初始化函数一样,推荐你不要使用DisposableBean
回调接口,因为会产生不必要的耦合之类的balbalbal。还是和上面一样,能使用 @PreDestroy
注解或者指定一个spring bean定义支持的方法TODO??若使用XML配置,可是使用<bean/>
元素的destroy-method
属性来完成该设置。若是使用Java config,可以使用@Bean
注解的destroyMethod
属性来完成销毁回调设置。see the section called “Receiving lifecycle callbacks”。看样例:
<bean id="exampleInitBean" class="examples.ExampleBean" destroy-method="cleanup"/>
public class ExampleBean {
public void cleanup() {
// do some destruction work (like releasing pooled connections)
}
}
和下面代码效果一样,但是上面的代码不和Spring耦合
<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
public class AnotherExampleBean implements DisposableBean {
public void destroy() {
// do some destruction work (like releasing pooled connections)
}
}
<bean>
元素的destroy-method
属性可以指定一个特别的值,设置该值后Spring将会自动探测指定类上的publicclose
或者shutdown
方法。这个设置自动探测销毁方法的属性,也可以设置给<beans/>
元素的default-destroy-method
属性,用来设置<beans>
元素内的所有的<bean>
自动探测销毁方法,详情参看section called “Default initialization and destroy methods”。注意,在Java config配置元数据中,这种自动探测是默认的。
译注,现在是羊年除夕夜23:40,再过30分钟,公司服务器上的一些定时器就要开始运行了。不知道还会不会线程挂起了,多线程中使用网络输出流时如果发生断网,线程则会处于阻塞状态,然后就没有然后一直阻塞,已经修改过了。外面的烟花炮仗声逐渐的密集了起来,放炮仗,污染太重了,国家抑制的手段就和抑制烟草手段一样,重税。心乱了,不能专心翻译了。
<h5 id='beans-factory-lifecycle-default-init-destroy-methods'>默认的初始化函数和销毁函数</h5>
若不是使用InitializingBean
和DisposableBean
接口实现初始化和销毁回到方法,通常使用规范的方法名比如init
,initialize()
,dispose()
等等。理论上,生命周期回调方法名的规范性,应该贯穿于整个项目中,所有的开发者都应该使用相同的方法名保持一致性。译注,编码规范,Spring最讲究这个了
可以配置容器查找所有bean的初始化回调和销毁回调,当应用类中的初始化回调方法命名为init()
,就不需要在bean定义中配置init-method="init"
属性。Spring IoC容器在bean初始化时调用init()
回调。该功能强制初始化和销毁回调方法命名的规范性。
Suppose that your initialization callback methods are named init() and destroy callback methods are named destroy(). Your class will resemble the class in the following example.
假设,初始化回调方法命为init()
,销毁回调方法命名为destroy()
。应该和下面的样例差不多:
public class DefaultBlogService implements BlogService {
private BlogDao blogDao;
public void setBlogDao(BlogDao blogDao) {
this.blogDao = blogDao;
}
// this is (unsurprisingly) the initialization callback method
public void init() {
if (this.blogDao == null) {
throw new IllegalStateException("The [blogDao] property must be set.");
}
}
}
<beans default-init-method="init">
<bean id="blogService" class="com.foo.DefaultBlogService">
<property name="blogDao" ref="blogDao" />
</bean>
</beans>
在顶级<bean/>
元素中定义了default-init-method
属性,使Spring Ioc 容器解析bean中名为init
的方法为初始化回调方法。当bean创建实例并组装时,若bean类中有个一个init()
方法,该初始化回调会在合适的时间调用。
<h5 id='beans-factory-lifecycle-combined-effects'>联合混合使用多种生命周期回调机制</h5>
Spring2.5 以后,控制bean生命周期行为,有三种生命周期回调机制,或者说是三种方式实现:InitializingBean 和 DisposableBean 回调接口;自定义init()
和destroy()
方法; @PostConstruct and @PreDestroy 注解。这些方式可以混合使用。
如何一个bean上配置了多种生命周期回调机制,并且每种机制都使用了不同的方法,那么所有的回调方法都会按次序执行。然而,如果配置了相同的方法名,比如
init()
方法作为初始化方法,该方法在多种生命周期回调机制中都有配置,但是,该方法只会执行一次。
在一个bean中,配置多种生命周期回调机制,每种机制使用了不同的初始化方法,会按照下列次序调用:
- 带
@PostConstruct
注解的方法 -
InitializingBean
回调接口中的afterPropertiesSet()
方法 - 自定义的
init()
方法
销毁回调也使用相同的次序
- 带
@PreDestroy
注解的方法 -
DisposableBean
回调接口中的destroy()
方法 - 自定义的
destroy()
方法
<h5 id='beans-factory-lifecycle-processor'>容器启动和关闭回调</h5>
Lifecycle
接口定了对象有自己生命周期需求的必须的方法(比如启动停止某些后台处理)
public interface Lifecycle {
void start();
void stop();
boolean isRunning();
}
任何Spring管理的对象都可以实现此接口。当ApplicationContext
接口启动和关闭时,它会调用本容器内所有的Lifecycle
实现。通过LifecycleProcessor
来调用,
public interface LifecycleProcessor extends Lifecycle {
void onRefresh();
void onClose();
}