4

作者: google666s | 来源:发表于2016-09-03 14:59 被阅读30次

    有个类实现了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();
    
    }
    

    相关文章

      网友评论

          本文标题:4

          本文链接:https://www.haomeiwen.com/subject/xjgkettx.html