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定义,用来部署的源类,要设置方法重写,大概这么搞:


    
    
        String
    



可以在元素内设置一个或多个元素来指明被替换方法的参数类型。只有被覆盖的方法在类有重载,参数签名才是必要的。为了方便,String类型的参数只需要其完全限定类型名称的字串即可。比如,下面列出的均可匹配java.lang.String:

java.lang.String
String
Str

因为参数的数量基本就可以确定方法(重载的方法,基本上是参数数量有区别),此简写能大量减少打字,让你仅打几个字符就能匹配参数类型。
译注,Spring是工业品质的框架,如此细微的人性化设计,值得学习

bean作用域


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。有关如何注册该作用域和注册自定义作用域,参看本章使用自定义作用域

单例作用域


单例bean只会产生一个实例,对于所有的请求,Spring容器都只会返回一个实例。

换句话说,当定义了单例bean,Srping容器只会创建一个实例,这个实例存储在单例池中,单例池应该属于缓存,接下来所有对于该单例bean的请求和引用,都将返回缓存中的对象。

Figure 5.2.

替换的文本可选的

Spring单例bean的概念,和四人帮GOF那本《设计模式》中定义的单例模式不同。GOF的单例是硬编码级的对象作用域,因此导致每一个类加载器内会产生单例类的一个实例。Spring的单例恰如其名,在容器范围内只会产生一个类实例。Spring中,bean默认的作用域都是单例作用域。使用xml 定义单例bean,像这样:





prototype原型作用域


设置bean作用域为prototype,就是非单例,对于每次请求都将返回一个该类的新实例。也就是说,原型bean注入另一个bean,或者是请求原型bean,都是通过在容器上调用getBean()方法产生的。一般来说 ,原型bean用于有状态bean,单例bean用于无状态bean。

下图示例了Srping原型作用域。一个数据访问对象(DAO)通常不会配置成原型作用域,因为通常DAO不会持有任何会话状态;因为作者偷懒,所以重用了上面单例示意图。


替换的文本可选的

接下来看看如何在XML中定义原型bean:


和其他作用域相比,Srping并不管理原型bean的完整的生命周期:容器实例化,配置或者组装原型独享,注入给其他类,然后并未进一步记录那个原型bean。因此,尽管对象的初始化回调方法会调用,不受scope影响,但是对于原型bean,销毁回调不会被调用。客户端代码必须清理原型对象并且释放原型bean持有的资源。为了让Spring容器释放原型bean持有的资源,可以用自定义的bean[post-processor](#beans-factory-extension-bpp),
它持有需要被清理bean的引用。

某种意义上,对于原型bean来说,Spring容器的角色就是替换了new 操作符。所有的生命周期管理,在经过实例化之后,都需要由客户端来处理。(Spring 容器中bean的生命周期详情,请参看本章5.6.1生命周期回调)

单例依赖原型


单例类依赖了原型类,要知道依赖在单例类初始化的时候就已经注入好了。因此,若你注入了一个原型bean给单例bean,将会是一个新的原型bean的实例注入了单例bean实例。原型bean实例将会是唯一的实例,再也不会为单例bean产生新的实例。

假若你需要单例bean在运行时重复的获取新的原型bean实例。那就不能将原型bean注入给单例bean,因为那样注入只会发生一次,就是发生在在Srping容器实例化单例bean并解析注入依赖时。如果需要多次获取新的原型bean实例,参看本章5.4.6方法注入

Request, session, and global session scopes


request,session,global session作用域,只有在spring web ApplicationContext的实现中(比如XmlWebApplicationContext)才会起作用,若在常规Spring IoC容器中使用,比如ClassPathXmlApplicationContext中,就会收到一个异常IllegalStateException来告诉你不能识别的bean作用域

初始化web配置

为了支持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文件中:


    ...
    
        
            org.springframework.web.context.request.RequestContextListener
        
    
    ...

如果设置listener有问题的话,可以考虑使用RequestContextFilter。filter映射要根据web 应用配置来调整:


    ...
    
        requestContextFilter
        org.springframework.web.filter.RequestContextFilter
    
    
        requestContextFilter
        /*
    
    ...

DispatcherServlet,RequestContextListener,RequestContextFilter都是做相同的事儿,也就是绑定HTTPrequest对象到服务的Thread线程中,并开启接下来
用到的session-scoped功能。

Request作用域

考虑下面这种bean定义:


Spring 使用该bean定义为每一次HTTP 请求创建一个新的LoginActionbean 的实例。也就是,loginActionbean作用域范围在HTTP 请求级别。可以改变实例的内部状态,多少实例都可以,因为根据此loginAcitonbean定义创建的其他bean实例并不会看到这些状态的改变;他们为各自的request拥有。当reqeust完成处理,request作用的bean就被丢弃了。

session作用域

考虑下面这种bean定义:


在一个session会话期间,Spring容器使用userPreferences定义创建了一个UserPreferencesbean的实例。换句话说userPreferencesbean在HTTP Session会话期间有效。和request-scopedbean相类似,可以改变bean实例的内部状态,不管bean创建了多少实例都可以,要知道,使用相同的userPreferences定义创建的其他的bean实例看不到这些状态的改变,因为他们都是为各自的HTTP Session服务的。当HTTP Session最终被丢弃时,该session内的session-scoped作用域的bean实例也会被丢弃。

全局Session作用域

考虑下面这种bean定义:


全局session作用域与标准HTTP Session作用域类似,仅能应用于基于portlet的web应用的上下文环境中。portlet规范中定义的global Session概念是,在由单个portlet web应用创建的所有的的portlets中共享。全局session作用域的bean和global portlet Session全局portlet会话生命周期相同。
若是在标准的基于Servelt web应用中定义了全局session作用域bean,那么将会使用标准的Session作用域,不会报错。

应用作用域

考虑下面这种bean定义:


Spring 容器使用该定义为整个web应用创建一个AppPreferencesbean的实例。appPreFerencesbean作用域是ServeletContext级别,存储为一个常规的ServletContext属性。这个Spring单例作用域有几分相似,但是和单例作用域相比有两个重要不同:1、他是每一个ServeltContext一个实例,而不是SpringApplicationContext范围。2、它是直接暴露的,作为ServletContext属性,因此可见。

不同级别作用域bean之间依赖

Spring IoC容器不仅管理bean的实例化,也负责组装(或者依赖)。如果想将HTTP request作用域bean注入给其他bean,就得给作用域bean(request或者session)注入一个AOP代理用来替换作用域bean。通过注入一个代理对象暴露于作用域bean相同的的接口,他是代理对象也能从相关作用域(request或者session)中检索到真正的被代理对象,并委派方法调用实际对象的方法。

注意

不需要再单例或者原型bean内部使用

下面的配置虽然简单,但是重要的理解“为什么”和“如何搞”




    
    
        
        
    

    
    
        
        
    

为了创建一个代理,得在作用域bean定义内插入子元素。详情参看 “Choosing the type of proxy to create” 和 Chapter 34, XML Schema-based configuration.)。request, session, globalSession , custom-scope,为什么这些级别的作用域需要元素? 下面来做个小测验,一个单例bean定义,对比一下,它如果要实现前面提到的作用域bean注入,该如何配置。(下面的userPreferencesbean,实际上并不完整)。




    

上例中,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 创建代理时,Spring 容器默认使用CGLIB类型创建代理。

注意

CGLIB代理只会拦截public方法调用。非public方法不会“呼叫转移”给实际的作用域bean。

还有个选择,通过配置,使Spring容器为这些作用域bean创建标准的JDK interface-based代理,设置元素proxy-target-class属性的值为false即可。使用标准JDK接口代理好处是无需引入第三方jar包。然而,作用域bean 至少实现一个接口,需要注入作用域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”.

自定义作用域


bean的作用域机制是可扩展的;可以定义自己的作用域,甚至重新定义已存在的作用域,经管后者不推荐,并且,不能重写内置单例作用域和原型作用域。

创建自定义作用域

实现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()

使用自定义作用域

可能是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定义,该定义要遵守自定义作用域的规则


自定义作用域的实现,不局限于编程式注册。也可以使用CustomScopeConfigurer类声明式注册

*译注*,编程式就是指硬编码,hard-code,声明式就是指配置,可以是xml可以是注解总之无需直接使用代码去撰写相关代码。不得不说,*编程式和声明式*与*硬编码和配置*相比,更加高端大气上档次。技术人员尤其要学习这种官方的、概念性的、抽象的上档次的语言或者说式地道的表达,假若谈吐用的全是这种词汇,逼格至少提升50%,镇住其他人(入行时间不长的同行,或者面试官)的概率将大大提升。当然了,和生人谈吐要用高逼格词汇,比如*声明式*,*编程式*,然而和自己人就要用人话了,比如*硬编码*,*xml配置*,因为他们得能先听懂才能干活。
总之,**装逼用官话,聊天用人话**,闲话少絮,看如何声明式注册(因为此处要装逼,人话是看如何xml)

blablablab




    
        
            
                
                    
                
            
        
    

    
        
        
    

    
        
    


注意

When you place in a FactoryBean implementation, it is the factory bean itself that is scoped, not the object returned from getObject().
如果在FactoryBean实现中设置了,表示是工厂bean他本身的作用域,并不是getObject()返回的对象的作用域。TODO

Customizing the nature of a bean自定义bean的xxx擦这个nature该怎么翻

生命周期回调函数


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过程。

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

初始化回调

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”。看代码


public class ExampleBean {

    public void init() {
        // do some initialization work
    }

}

和下面的效果相同,但上面的没有耦合Spring。


public class AnotherExampleBean implements InitializingBean {

    public void afterPropertiesSet() {
        // do some initialization work
    }

}

销毁回调

实现org.springframework.beans.factory.DisposableBean接口,作用是Spring销毁bean时调用该方法。 DisposableBean接口只有一个方法:

void destroy() throws Exception;

和上面初始化函数一样,推荐你不要使用DisposableBean回调接口,因为会产生不必要的耦合之类的balbalbal。还是和上面一样,能使用 @PreDestroy注解或者指定一个spring bean定义支持的方法TODO??若使用XML配置,可是使用元素的destroy-method属性来完成该设置。若是使用Java config,可以使用@Bean注解的destroyMethod属性来完成销毁回调设置。see the section called “Receiving lifecycle callbacks”。看样例:


public class ExampleBean {

    public void cleanup() {
        // do some destruction work (like releasing pooled connections)
    }

}

和下面代码效果一样,但是上面的代码不和Spring耦合


public class AnotherExampleBean implements DisposableBean {

    public void destroy() {
        // do some destruction work (like releasing pooled connections)
    }

}
注意

元素的destroy-method属性可以指定一个特别的值,设置该值后Spring将会自动探测指定类上的public close或者shutdown方法。这个设置自动探测销毁方法的属性,也可以设置给元素的default-destroy-method属性,用来设置元素内的所有的 自动探测销毁方法,详情参看section called “Default initialization and destroy methods”。注意,在Java config配置元数据中,这种自动探测是默认的。

译注,现在是羊年除夕夜23:40,再过30分钟,公司服务器上的一些定时器就要开始运行了。不知道还会不会线程挂起了,多线程中使用网络输出流时如果发生断网,线程则会处于阻塞状态,然后就没有然后一直阻塞,已经修改过了。外面的烟花炮仗声逐渐的密集了起来,放炮仗,污染太重了,国家抑制的手段就和抑制烟草手段一样,重税。心乱了,不能专心翻译了。

默认的初始化函数和销毁函数

若不是使用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.");
        }
    }

}


    
        
    

在顶级元素中定义了default-init-method属性,使Spring Ioc 容器解析bean中名为init的方法为初始化回调方法。当bean创建实例并组装时,若bean类中有个一个init()方法,该初始化回调会在合适的时间调用。

联合混合使用多种生命周期回调机制

Spring2.5 以后,控制bean生命周期行为,有三种生命周期回调机制,或者说是三种方式实现:InitializingBean 和 DisposableBean 回调接口;自定义init()destroy()方法; @PostConstruct and @PreDestroy 注解。这些方式可以混合使用。

注意

如何一个bean上配置了多种生命周期回调机制,并且每种机制都使用了不同的方法,那么所有的回调方法都会按次序执行。然而,如果配置了相同的方法名,比如init()方法作为初始化方法,该方法在多种生命周期回调机制中都有配置,但是,该方法只会执行一次。

在一个bean中,配置多种生命周期回调机制,每种机制使用了不同的初始化方法,会按照下列次序调用:

  • @PostConstruct注解的方法
  • InitializingBean回调接口中的afterPropertiesSet()方法
  • 自定义的init()方法

销毁回调也使用相同的次序

  • @PreDestroy注解的方法
  • DisposableBean回调接口中的destroy()方法
  • 自定义的destroy()方法

容器启动和关闭回调

Lifecycle接口定了对象有自己生命周期需求的必须的方法(比如启动停止某些后台处理)

public interface Lifecycle {

    void start();

    void stop();

    boolean isRunning();

}

任何Spring管理的对象都可以实现此接口。当ApplicationContext接口启动和关闭时,它会调用本容器内所有的Lifecycle实现。通过LifecycleProcessor来调用,

public interface LifecycleProcessor extends Lifecycle {

    void onRefresh();

    void onClose();

}

你可能感兴趣的:(4)