4

有个类实现了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,则无需特别配置:DispatcherServletDispatcherPortlet已经暴露了所有的相关状态。

若使用了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都是做相同的事儿,也就是绑定HTTPrequest对象到服务的Thread线程中,并开启接下来
用到的session-scoped功能。

<h5 id='beans-factory-scopes-request'>Request作用域</h5>
考虑下面这种bean定义:

<bean id="loginAction" class="com.foo.LoginAction" scope="request"/>

Spring 使用该bean定义为每一次HTTP 请求创建一个新的LoginActionbean 的实例。也就是,loginActionbean作用域范围在HTTP 请求级别。可以改变实例的内部状态,多少实例都可以,因为根据此loginAcitonbean定义创建的其他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定义创建了一个UserPreferencesbean的实例。换句话说userPreferencesbean在HTTP Session会话期间有效。和request-scopedbean相类似,可以改变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应用创建一个AppPreferencesbean的实例。appPreFerencesbean作用域是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注入,该如何配置。(下面的userPreferencesbean,实际上并不完整)。

<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。注意,userManagerbean是一个单例bean:每个容器只会实例化一个,他的依赖(本例中只有一个,userPreferencesbean)也仅会注入一次。也就是说,userManagerbean只能操作相同的userPreferences对象,就是注入的那一个。

将一个短生命周期作用域bean注入给长生命周期作用域bean,比如将HTTP Session作用域bean作为依赖注入给一个单例bean。然而,你需要一个userManager对象,在HTTP Session会话期间,需要与session同生命周期的对象userPreferences。 因此,容器会创建一个对象,该对象拥有和UserPreferences完全相同的public接口并暴露所有的public接口。,该对象能根据作用域机制获取真真的UserPreferences对象。容器会将这个代理对象注入给userManagerbean,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-basedinterface-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容器内置的作用域singletonprototype。第二个参数是自定义作用域实现的实例,就是你想注册的、使用的那个自定义作用域。

写好了自定义作用域的实现,就可以像下面那样注册它了:


注意

下面的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的生命周期,通过实现SpringInitializingBeanDisposableBean接口。容器会调用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-methoddestroy-method

Spring使用BeanPostProcessor实现类处理所有的回调接口并调用相应的方法,接口由Spring 负责查找。若需要自定义功能或其他生命周期行为,Spring并未提供开箱即用的支持,但是可以自己实现BeanPostProcessor类。详情参看"Section 5.8, “Container Extension Points”

除了initializationdestruction方法,Spring bean也可以实现Lifecycle接口,这些接口可以参与Spring容器生命周期的startupshutdown过程。

本章讲解生命周期回调接口。

<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将会自动探测指定类上的public close或者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>
若不是使用InitializingBeanDisposableBean接口实现初始化和销毁回到方法,通常使用规范的方法名比如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生命周期行为,有三种生命周期回调机制,或者说是三种方式实现:InitializingBeanDisposableBean 回调接口;自定义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();

}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,230评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,261评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,089评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,542评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,542评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,544评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,922评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,578评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,816评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,576评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,658评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,359评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,937评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,920评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,859评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,381评论 2 342

推荐阅读更多精彩内容