spring-reference-The IoC container的翻译笔记(自己用)

1,如何把不在Spring管理范围内的Bean,加到Spring当中?可以使用BeanFactory的registerSingleton(..) and registerBeanDefinition(..)方法把,不在Spring管理范围内的Bean,加到Spring当中。
2,当命名Bean时,可以用id或name属性。用name属性可以给Bean起多个名字。
3,可以用<alias name="fromName" alias="toName"/>给Bean命名别名,这样的好处是不干扰各自的BeanDefinition定义。
   这个方法主要用在多个子系统定义上,例如:
   <alias name="subsystemA-dataSource" alias="subsystemB-dataSource"/>
   <alias name="subsystemA-dataSource" alias="myApp-dataSource" />
   用上面的在name里写多个名字的方法定义的话,就必须在一个Bean定义中写3名字,
   而这些定义都写到了一个子系统里了,当修改别的子系统的配置时,需求修改无关的子系统的配置。
4,实例化一个Bean有3种方法:
   1,用Bean的默认构造函数。
   2,用类的静态工厂方法。在class中指定类,factory-method中制定类中的静态方法。
     <bean id="clientService"
      class="examples.ClientService"
      factory-method="createInstance"/>
   3,用一个已经存在的Bean的方法,这个方法不是静态的。
   <!-- the factory bean, which contains a method called createInstance() -->
    <bean id="serviceLocator" class="examples.DefaultServiceLocator">
    <!-- inject any dependencies required by this locator bean -->
    </bean>

    <!-- the bean to be created via the factory bean -->
    <bean id="clientService"
        factory-bean="serviceLocator"
        factory-method="createClientServiceInstance"/>
5,注意:在Spring文档中,factory bean这个词指的是在Spring容器中,能够通过静态方法或实例生成对象的Bean。
   而FactoryBean指的是Spring自己用的特定的FactoryBean。

6,注入时,可以通过构造函数或Setter方法注入。
   什么时候使用Setter方法注入,什么时候使用构造函数注入呢?
   如果在Bean初始化时,一个依赖是必须项,一般使用构造函数注入;如果那个依赖不是一个必须项,使用Setter注入。
   Setter注入的好处是,可以重新注入。JMX MBeans就是使用Setter注入的。
    <bean id="person" class="test.Person">
        <constructor-arg value="Tom"/>
        <property name="age" value="20"/>
    </bean>

7,依赖解析过程:
     - ApplicationContext根据配置文件,被创建并初始化。
     - 对于每个Bean,它的依赖通过“属性”,“构造函数参数”,“静态工厂方法”来提供。当Bean被创建后,这些依赖也就提供了。
     - 每个属性或构造函数参数都是一个实际的值或一个Bean的引用
     - 每个属性或构造函数参数的值,都是通过Spring的默认的格式转换,把我们设置值转换成属性或构造函数参数实际的类型。
   Spring容器被创建后,会检查Bean的配置。但直到Bean被创建时,它的属性才会被设置。如果Bean是单例(singleton-scoped)
   并且是预初始化(pre-instantiate)类型的话,它会在容器被创建时,也一起被创建;如果是其他类型的Bean,只有在Bean被请求的时候,才会被创建。
   Bean的创建会引起一系列的创建,它的依赖和它的依赖的依赖等等。
   
8,关于Bean的循环依赖
   如果使用构造函数依赖的话,可能会产生循环依赖。例如:类A依赖于类B,使用构造函数注入类B;类B也依赖于类A,也是通过
   构造函数注入类A。如果你这样配置的话,容器就会抛出BeanCurrentlyInCreationException异常。
   如果你使用Setter注入的话,就不会抛出BeanCurrentlyInCreationException异常,造成循环依赖,但这样的做法并不推荐。
   循环依赖会强制使一个Bean在完全初始化完之前,就被设置到另一个Bean上,就像鸡和蛋的故事一样。
   你可以信任容器,它会检测一些问题,例如:引用了不存在的Bean,或循环依赖等。当创建Bean时,容器会尽可能晚地设置属性和解析依赖。
   这就意味着即使容器被正确地加载后,当你访问一个有问题(创建对象的问题或者它的依赖的问题)对象时,容器仍然会抛出一个异常。
   例如,缺少或非法属性的异常。也就是说,配置中存在的问题的发现被推迟了(直到使用时,才会发现配置的问题),这就是为什么
   ApplicationContext会默认预初始化(pre-instantiate)单例Bean。通过在前期花费时候和内存在真正使用它之前创建它,来发现一些配置上的问题。
   你可以修改单例Bean的这种预加载的行为。通过lazy-initialize属性来让单例Bean不预先加载。
   
   如果撇开循环依赖不谈,当协作bean被注入到依赖它的bean时,协作bean必须在依赖bean之前完全配置好。例如bean A对bean B存在依赖关系,
   那么Spring IoC容器在调用bean A的setter方法之前,bean B必须被完全配置,这里所谓完全配置的意思就是:bean将被实例化
   (如果Bean不是预实例化的单例Bean),相关的依赖也将被设置好,而且所有相关的lifecycle方法(如IntializingBean的init方法以及callback方法)
   也将被调用。


9,对依赖进行注入时,可以直接注入原始类型的值,例如:int,String。有两种方式,一个是使用property标签,还有一个是用p:namespace方式。
   p:namespace就是“p:”+“属性名”,例如p:password。

property标签1:
<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <!-- results in a setDriverClassName(String) call -->
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>

</bean>

property标签2:(使用Java.util.Properties)

<bean id="mappings"
    class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <!-- typed as a java.util.Properties -->
    <property name="properties">
        <value>
            jdbc.driver.className=com.MySQL.jdbc.Driver
            jdbc.url=jdbc:mysql://localhost:3306/mydb
        </value>
    </property>
</bean>

p:namespace方式:

<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource"
        p:driverClassName="com.mysql.jdbc.Driver"
        p:password="masterkaoli"/>


或者使用idref标签设置BeanId,这个标签的好处是,能够让容器在发布阶段进行验证引用的Bean,命名的Bean是否存在。一般来说,拼写错误(Bean的名字写错了等)能够在Bean初始化时被发现,但如果Bean类型是prototype的话,这个类型的Bean的拼写错误只有在Bean真正被调用时才能被发现,也就是系统运行时。(singleton类型,并且是预载(lazy-init属性为false)的Bean,在容器合建时候也一起被创建并初始化;但不是前面说的类型的Bean的话(例如prototype),直到被使用时,才会被创建并初始化)

<bean id="theTargetBean" class="..."/>

<bean id="theClientBean" class="...">
    <property name="targetName">
        <idref bean="theTargetBean" />
    </property>
</bean>


10,还可以在property标签里使用ref标签,这个标签和在property标签里使用ref属性一样。但使用ref标签可以引用父容器的Bean,ref属性的话引用的是本容器内的Bean。引用父容器的Bean

<!-- in the parent context -->
<bean id="accountService" class="com.foo.SimpleAccountService">
    <!-- insert dependencies as required as here -->
</bean>

<!-- in the child (descendant) context -->
<bean id="accountService" <!-- bean name is the same as the parent bean -->
    class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="target">
        <ref parent="accountService"/> <!-- notice how we refer to the parent bean -->
    </property>
    <!-- insert other configuration and dependencies as required here -->
</bean>


11,在<property/> 或<constructor-arg/>标签里使用 <bean/>标签进行对依赖进行初始化。

<bean id="outer" class="...">
    <!-- instead of using a reference to a target bean, simply define the target bean inline -->
    <property name="target">
        <bean class="com.example.Person"> <!-- this is the inner bean -->
            <property name="name" value="Fiona Apple"/>
            <property name="age" value="25"/>
        </bean>
    </property>
</bean>

   这个内部Bean标签,不需要ID或Name属性,设置了也不会被使用。容器会忽略内部Bean标签scope属性,因为内部Bean标签所创建的Bean是随着外部Bean的创建而创建的,无法注入到其它Bean里。


12,也可以使用<list/>, <set/>, <map/>, and <props/>标签,设置一些集合类型的值,如List, Set, Map, and Properties。
下面的这些值,也可以被设置到Map的Key或Value,Set的Key或Value里。
bean | ref | idref | list | set | map | props | value | null

<bean id="moreComplexObject" class="example.ComplexObject">
    <!-- results in a setAdminEmails(java.util.Properties) call -->
    <property name="adminEmails">
        <props>
            <prop key="administrator">administrator@example.org</prop>
            <prop key="support">support@example.org</prop>
            <prop key="development">development@example.org</prop>
        </props>
    </property>
    <!-- results in a setSomeList(java.util.List) call -->
    <property name="someList">
        <list>
            <value>a list element followed by a reference</value>
            <ref bean="myDataSource" />
        </list>
    </property>
    <!-- results in a setSomeMap(java.util.Map) call -->
    <property name="someMap">
        <map>
            <entry key="an entry" value="just some string"/>
            <entry key ="a ref" value-ref="myDataSource"/>
        </map>
    </property>
    <!-- results in a setSomeSet(java.util.Set) call -->
    <property name="someSet">
        <set>
            <value>just some string</value>
            <ref bean="myDataSource" />
        </set>
    </property>
</bean>


13,集合也支持合并。例如,当父Bean中用集合注入属性时,子Bean也用集合注入相同的相同的属性的话,可以选择把父Bean和子Bean注入的集合内容进行合并。注意下面代码中的:merge="true"。

<beans>
    <bean id="parent" abstract="true" class="example.ComplexObject">
        <property name="adminEmails">
            <props>
                <prop key="administrator">administrator@example.com</prop>
                <prop key="support">support@example.com</prop>
            </props>
        </property>
    </bean>
    <bean id="child" parent="parent">
        <property name="adminEmails">
            <!-- the merge is specified on the child collection definition -->
            <props merge="true">
                <prop key="sales">sales@example.com</prop>
                <prop key="support">support@example.co.uk</prop>
            </props>
        </property>
    </bean>
<beans>


14,关于集合的几点:
(1)如果父Bean和子Bean的集合中,有相同的Key的话,子Bean中的Value会覆盖父Bean中的Value。如果集合是List的话,集合会有顺序的概念,而Map和Set则没有。
(2)不同类型的集合不能合并
(3)集合可以声明为强类型(Map<String, Float>),spring会自动把Value给转型成定义的类型。
(4)String类型的属性不赋值的话,默认值为空字符串。如果想设置null的话,使用null标签。

        <bean class="ExampleBean">
            <property name="email">
                <null/>
            </property>
        </bean>
        
15,c-namespace和p-namespace。在注入属性时,除了用property标签,还可以用c-namespace和p-namespace进行设置。
c-namespace是用在构造函数注入的时候,p-namespace是用来Setter方法注入的时候。
    <bean name="classic" class="com.example.ExampleBean">
        <property name="email" value="foo@bar.com"/>
    </bean>

    <bean name="p-namespace" class="com.example.ExampleBean"
        p:email="foo@bar.com"/>
如果是引用别的Bean的话,可以像下面的Ref一样使用。
    <bean name="john-modern"
        class="com.example.Person"
        p:name="John Doe"
        p:spouse-ref="jane"/>
c:namespace还可以使用index的方式,来注入:
    <bean id="foo" class="x.y.Foo" c:_0-ref="bar" c:_1-ref="baz"/>

16,还可以使用复合属性。
<bean id="foo" class="foo.Bar">
    <property name="fred.bob.sammy" value="123" />
</bean>
这段代码的意思是,foo对象有一个属性叫fred,fred这个对象有一个属性叫bob,bob有一个属性呀sammy,把sammy这个属性设置成123。
如果这一串的调用中,有一个对象为null的话,就会抛出异常。

17,当一个Bean依赖另一个Bean时,通过使用ref来注入。但有时候两个Bean之间的关系不是那么紧密(例如,当类中的静态块的初始化被时,
如数据库驱动的注册),depends-on属性可以用于当前bean初始化之前显式地强制一个或多个bean被初始化。下面的例子中使用了
depends-on属性来指定一个bean的依赖。
<bean id="beanOne" class="ExampleBean" depends-on="manager,accountDao">
  <property name="manager" ref="manager" />
</bean>

<bean id="manager" class="ManagerBean" />
<bean id="accountDao" class="x.y.jdbc.JdbcAccountDao" />

18,前面说了,ApplicationContext实现的默认行为就是在启动时将所有singleton bean提前进行实例化。
如果不想在启动时实例化,需要设置<bean/>元素中的lazy-init属性来进行控制。把这个属性设置成True后,Bean就在第一次被调用时初始化。
<bean id="lazy" class="com.foo.ExpensiveToCreateBean" lazy-init="true"/>

如果一个bean被设置为延迟初始化,而另一个非延迟初始化的singleton bean依赖于它,那么当ApplicationContext提前实例化singleton bean时,

它必须也确保所有上述singleton 依赖bean也被预先初始化,当然也包括设置为延迟实例化的bean。因此,如果Ioc容器在启动的时候创建了那些设置

为延迟实例化的bean的实例,你也不要觉得奇怪,因为那些延迟初始化的bean可能在配置的某个地方被注入到了一个非延迟初始化singleton bean里面。


19,你可能需要在运行期让singleton-scoped bean每次都获得prototype-scoped bean的新实例。在这种情况下,只将prototype-scoped bean注入到你的singleton bean中是没有用的,因为正如上文所说的,仅仅在当Spring容器实例化singleton bean并且处理注入的依赖时,生成唯一实例。如果你需要在运行期一次又一次的生成(prototype) bean的新实例,你可以参考方法注入


20,关于Bean的作用域,

request:spring会针对每个Http Request,创建一个Bean

Session:spring会针对每个Http Session,创建一个Bean

Application :spring会针对ServletContext,创建一个Bean。这个Bean做为ServletContext的一个属性。作用域和Singleton作用域有点像,但有两点不同:

  1,Application作用域是对每一个ServletContext是单例的,并不是对每个ApplicationContext(在一个WebApplication里可能会有多个ApplicationContext)

  2,Application作用域是作为ServletContext的一个属性。


如果你打算将一个Http request范围的bean注入到另一个bean中,那么需要注入一个AOP代理来替代被注入的作用域bean。也就是说,你需要注入一个代理对象,该对象具有与被代理对象一样的公共接口,而容器则可以足够智能的从相关作用域中(比如一个HTTP request)获取到真实的目标对象,并把方法调用委派给实际的对象。

要创建这样的代理,只需要在Bean作用域定义中增加一个<aop:scoped-proxy/>子元素(为了让容器可以有效的使用基于类(而不是接口)的代理,你需要在classpath中加入CGLIB包, 并且要使用Appendix A,XML Schema-based configuration配置方式)。

    <!-- a HTTP Session-scoped bean exposed as a proxy -->
    <bean id="userPreferences" class="com.foo.UserPreferences" scope="session">
          
          <!-- this next element effects the proxying of 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>

为什么在request,session, globalSession 和 '自定义作用域' 需要<aop:scoped-proxy/>元素?

从上述配置中可以很明显的看到singleton bean userManager被注入了一个指向HTTP Session作用域bean userPreferences的引用。singleton userManager bean会被容器仅实例化一次,并且其依赖(即userPreferences bean)也仅被注入一次。这意味着,userManager在理论上只会操作同一个userPreferences对象,即原先被注入的那个bean。而注入一个HTTPSession作用域的bean作为依赖,有违我们的初衷。因为我们想要的只是一个userManager对象,在它进入一个HTTPSession生命周期时,我们希望去使用一个HTTPSessionuserPreferences对象。

当注入某种类型对象时,该对象实现了和UserPreferences类一样的公共接口(即UserPreferences实例)。并且不论我们底层选择了何种作用域机制(HTTP request、Session等等),容器都会足够智能的获取到真正的UserPreferences对象,因此我们需要将该对象的代理注入到userManager bean中, 而userManager bean并不会意识到它所持有的是一个指向UserPreferences引用的代理。在本例中,当UserManager实例调用了一个使用UserPreferences对象的方法时,实际调用的是代理对象的方法。随后代理对象会从HTTPSession获取真正的UserPreferences对象,并将方法调用委派给获取到的实际的UserPreferences对象。

默认情况下,当一个bean有<aop:scoped-proxy/>标记时,Spring容器将为它创建一个基于CGLIB的类代理,这意味着你需要 将CGLIB库添加到应用的classpath中。

注意:CGLIB代理仅仅拦截public方法的调用!对于非public的方法调用,不会对目标对象产生委托。

你可以将<aop:scoped-proxy/>的属性'proxy-target-class'设置为'false'来选择标准JDK推荐的基于接口的代理,这样就不需要在应用的classpath中增加额外的库。但是,这就意味着类必须实现至少一个接口。并且所有的协作者必须通过某一个 接口来引用bean。


21,生命周期回调

实现Bean生命周期回调的方法有3个:

1,在Bean的方法上加@PostConstruct或@PreDestroy注解。

2,实现InitializingBean和DisposableBean接口。InitializingBean接口实现的方法为afterPropertiesSet(),DisposableBean接口实现的方法为destroy()。

3,在Bean标签里使用init-method属性或destroy-method属性,在属性上指定Bean的方法(这个方法不限定必须在要被初始化的Bean里)。

     <bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>

上面3种方法中推荐使用@PostConstruct或@PreDestroy注解,因为他与Spring无关,不需要和Spring耦合,这两个注解是Java注解。


使用在Bean标签里使用init-method属性或destroy-method属性指定方法时,可以进行全局指定方法,而不需要每个Bean标签都指定一下。

<beans default-init-method="init">
    <bean id="blogService" class="com.foo.DefaultBlogService">
        <property name="blogDao" ref="blogDao" />
    </bean>

</beans>


最后,请注意Spring容器保证在bean的所有依赖都满足后立即执行配置的初始化回调。这意味着初始化回调在原生bean上调用,这也意味着这个时候任何诸如AOP拦截器之类的将不能被应用。一个目标bean是首先完全创建,然后才应用诸如AOP代理等拦截器链。注意,如果目标bean和代理是分开定义了,你的代码甚至可以绕开代理直接和原生bean通信。因此,在初始化方法上使用拦截器将产生未知的结果,因为这将目标bean和它的代理/拦截器的生命周期绑定并且留下了和初始bean直接通信这样奇怪的方式。

当组合不同的生命周期机制时 - 例如,类层次中使用了不同的生命周期机制 - 开发者必须注意这些机制的应用顺序,下面是初始化方法中的顺序:

  • @PostConstruct元注释

  • InitializingBeanafterPropertiesSet()定义

  • 自定义init()方法配置

析构方法调用顺序是相同的:

  • @PreDestroy元注释

  • DisposableBeandestroy()定义

  • 自定义destroy()方法

如果bean存在多种的生命周期机制配置并且每种机制都配置为不同的方法名, 那所有配置的方法将会按照上面的顺利执行。然而如果配置了相同的方法名 - 例如, init()初始化方法 - 采用多种机制配置后,只会执行一次。



22,ApplicationContextAware and BeanNameAware

ApplicationContextAware:(在Bean实例化之后执行)

如果有Bean实现了ApplicationContextAware接口,你可以在接口的回调方法中得取ApplicationContext的实例,进而可以对ApplicationContext操作,比如创建Bean,或者把ApplicationContext映射成它的一个实现类ConfigurableApplicationContext,并使用这个实现类的一些功能。

BeanNameAware:(在Bean实例化之后执行)

如果有Bean实现了BeanNameAware接口,可以在接口提供的回调方法中,取得配置文件中定义的BeanName。这个回调方法在Bean的所有属性设置完成后,InitializingBean等类似的初始化接口调用之前被调用。

BeanFactoryAware:(在Bean实例化之后执行)

如果有Bean实现了BeanFactoryAware接口,你可以在接口的回调方法中得取BeanFactory的实例。这样bean可以以编程的方式操控创建它们的BeanFactory,当然我们可以将引用的BeanFactory造型(cast)为已知的子类型来获得更多的功能。

    与BeanFactoryAware等效的另一种选择是使用ObjectFactoryCreatingFactoryBean。ObjectFactoryCreatingFactoryBean是 FactoryBean 的一个实现,它返回一个指向工厂对象的引用,该对象将执行bean的查找。ObjectFactoryCreatingFactoryBean类实现了BeanFactoryAware接口;被实际注入到客户端bean的是ObjectFactory接口的一个实例。这是Spring提供的一个接口(因而依旧没有完全与Spring解耦),客户端可以使用ObjectFactory的getObject()方法来查找bean(在其背后,ObjectFactory实例只是简单的将调用委派给BeanFactory,让其根据bean的名称执行实际的查找)。你要做的全部事情就是给ObjectFactoryCreatingFactoryBean提供待查找bean的名字。让我们看一个例子:

http://docs.spring.io/autorepo/docs/spring-framework/3.2.17.RELEASE/javadoc-api/org/springframework/beans/factory/config/ObjectFactoryCreatingFactoryBean.html#setTargetBeanName(java.lang.String)

http://shouce.jb51.net/spring/beans.html#beans-some-examples    (3.5.2.1. BeanFactoryAware)

看了一下ObjectFactoryCreatingFactoryBean的使用例子,这个类是FactoryBean的一个实现类,它返回一个指向ObjectFactory的引用(从它的名字也可以看出来,它本质是一个FactoryBean,这个FactoryBean是来用生成ObjectFactory实例的)。这个ObjectFactory的引用,通过BeanFactory来返回特定的Bean。客户端可以使用ObjectFactorygetObject()方法来查找bean(在其背后,ObjectFactory实例只是简单的将调用委派给BeanFactory,让其根据bean的名称执行实际的查找)。你要做的全部事情就是给ObjectFactoryCreatingFactoryBean提供待查找bean的名字。

总的来说ObjectFactoryCreatingFactoryBean这个类,就是需要你配置给它一个BeanName,然后它使用这个BeanName,通过BeanFactory来取得这个Bean。这个类的好处就是,当你有一些复杂的创建Bean的逻辑,这些逻辑中有一步是需要从BeanFactory里取得Bean,这时你就可以把这个类注入到有复杂逻辑的Bean中,在被注入的Bean中使用注入进来的ObjectFactory,取得你想要的Bean。


在看ObjectFactoryCreatingFactoryBean这个类的意思时,被FactoryBean,BeanFactory,ObjectFactory这几个接口弄的有点蒙。现在说一下这几个接口的作用:

BeanFactory:

这个接口用作管理Bean的容器,Spring中生成的Bean都是由这个接口的实现来管理的。

FactoryBean:

这个接口使你可以提供一个复杂的逻辑来生成Bean。它本质是一个Bean,但这个Bean不是用来注入到其它地方像Service、Dao一样使用的,它是用来生成其它Bean使用的。实现了这个接口后,Spring在容器初始化时,把实现这个接口的Bean取出来,使用接口的getObject()方法来生成我们要想的Bean。当然,那些生成Bean的业务逻辑也要写getObject()方法中。BeanFactory和FactoryBean的使用方法,请看:http://chenzehe.iteye.com/blog/1481476

ObjectFactory:

它的目的也是做为一个工厂,来生成Object(这个接口只有一个方法getObject())。这个接口一般被用来,包装一个factory,通过个这工厂来返回一个新实例(prototype类型)。这个接口和FactoryBean有点像,但FactoryBean的实现是被当做一个SPI(Service Provider Interface)实例来使用在BeanFactory里面;ObjectFactory的实现一般被用来注入到其它Bean中,作为API来使用。就像ObjectFactoryCreatingFactoryBean的例子,它的返回值就是一个ObjectFactory,这个ObjectFactory被注入到了Bean中,在Bean通过这个接口的实例,来取得我们想要的Bean。

    总的来说,FactoryBean和ObjectFactory都是用来取得Bean,但使用的方法和地方不同,FactoryBean被配置好后,被Spring调用getObject()方法来取得Bean,ObjectFactory配置好后,在Bean里面可以取得ObjectFactory实例,需要我们手动来调用getObject()来取得Bean。

(Service Provider Interface:SPI是一种API,这种API被第三方来实现或扩展。它可以被用来扩展框架或实现组件替换功能)

还有其它的一个Aware接口:

ApplicationContextAware
ApplicationEventPublisherAware
BeanClassLoaderAware
BeanFactoryAware
BeanNameAware
BootstrapContextAware
LoadTimeWeaverAware
MessageSourceAware
NotificationPublisherAware
PortletConfigAware
PortletContextAware
ResourceLoaderAware
ServletConfigAware
ServletContextAware


23,BeanDefinition是可以继承的,在Bean标签里使用parent属性,指定要继承的Bean的Id,这样子BeanDefinition就会继承父BeanDefinition的属性定义。

<bean id="inheritedTestBean" abstract="true"
        class="org.springframework.beans.TestBean">
    <property name="name" value="parent"/>
    <property name="age" value="1"/>
</bean>

<bean id="inheritsWithDifferentClass"
        class="org.springframework.beans.DerivedTestBean"
        parent="inheritedTestBean" init-method="initialize">
    <property name="name" value="override"/>
    <!-- the age property value of 1 will be inherited from parent -->
</bean>

如果子BeanDefinition的定义和父BeanDefinition的定义相同的话,会覆盖父BeanDefinition的属性定义。

有一些属性是继承不了的:depends on, autowire mode, dependency check, singleton, lazy init

父BeanDefinition可以没有class属性,但abstract属性要设置成True,这样的话,这个父BeanDefinition就不能被实例化了。这个父BeanDefinition就成了一个模版,主要用来设置一个共通的属性,让子BeanDefinition继承。


24,一般来说,应用程序开发者不需要实现ApplicationContext,可以通过实现一些特殊的接口,来扩展Spring IoC容器。

BeanPostProcessor接口

这个接口提供了回调方法,让你实现自己的初始化逻辑。这些回调方法的调用,是在容器完成容器本身的初始化,配置以及初始化Bean之后被执行的。

你可以配置多个BeanPostProcessor,如果想控制多个BeanPostProcessor的执行顺序的话,可以让实现BeanPostProcessor接口的类也实现Ordered接口,并配置order属性。

注意:BeanPostProcessor主要是用来操作Bean实例的,也就是说,容器在初始化Bean实例之后,才会调用BeanPostProcessor的回调。BeanPostProcessor的作用域是只针对配置它的那个容器。当你在一个容器里定义了BeanPostProcessor,BeanPostProcessor只能处理当前容器里的Bean。也就是说,定义在一个容器里了BeanPostProcessor,不会去处理另一个容器里的Bean,即使两个容器是相同的层级的一部分。

如果要改变BeanDefinition的话,要使用BeanFactoryPostProcessor接口。


BeanPostProcessor接口由两个回调方法组成。当BeanPostProcessor实现类被配置到容器里后,在每个Bean被容器创建后,初始化方法(InitializingBean接口的方法,或@PostContruct等)执行之前调用BeanPostProcessor的postProcessBeforeInitialization回调,并在初始化方法执行之后调用BeanPostProcessor的postProcessAfterInitialization回调。在回调里,可以对Bean做任何操作,包括忽略当前回调。BeanPostProcessor这种后置回调一般用来检查回调接口(不明白啥意思),或用代理包装Bean。一些Spring AOP的基础类实现为一种后置处理器,提供代理包装逻辑。

ApplicationContext自动检测实现了BeanPostProcessor接口的Bean,并把这些Bean注册为后置处理器,以便在Bean合建时进行调用。配置一个后置处理器和配置其它Bean没有什么区别。

当你用@Bean注解来声明一个Bean时,使用这个注解的方法的返回类型至少是BeanPostProcessor接口的实现类,否则ApplicationContext无法通过类型自动检测到它。既然为了其它Bean的初始化,要尽早地初始化BeanPostProcessor,所以这个早期的类型检测是十分重要的。

注意:建议使用ApplicationContext自动检测的方式来注册BeanPostProcessor,但也可以通过编程的方式来注册,例如使用ConfigurableBeanFactory的addBeanPostProcessor方法。如果在注册前有逻辑判断,或在有层级的容器里,跨上下文拷贝后置处理器需求的话,使用编程方式进行注册的方法还是很有用的。但是,使用编程方式注册的话,用来控制启动顺序的Ordered接口的功能就失效了。记住,编程方式注册的后置处理器,总是在前面介绍的“自动检测的方式来注册”的后置处理器前执行,不管是不是显示声明order。

注意:BeanPostProcessor的实现类,和那些被这些实现类直接引用的Bean,都会在容器启动时进行初始化,这个初始化过程被作为一个特殊的阶段。这些BeanPostProcessor的实现类被保存起来,并应用在以后被创建的Bean上。因为AOP自动代理实现了BeanPostProcessor,所以BeanPostProcessor实现类和这些实现类引用的Bean不在代理的范围之内,切面无法织入它们。对于这样的Bean,可以看到这样的log message:"Bean foo is noteligible for getting processed by all BeanPostProcessor interfaces (for example: noteligible for auto-proxying)"


BeanPostProcessor接口的实现类,是一个扩展容器功能的通常手段。其中一个例子就是RequiredAnnotationBeanPostProcessor,这个类的功能是检查带有@required注解属性,是否被注入了,没注入就报错。


25,下一个扩展点是:BeanFactoryPostProcessor接口

这个接口和BeanPostProcessor接口很像,主要的区别是BeanFactoryPostProcessor能够操作Bean的配置文件的内容。容器允许BeanFactoryPostProcessor在容器初始化Bean之前,去读取Bean配置文件内容,并修改它。

你可以配置多个BeanFactoryPostProcessor,如果要控制调用顺序的话,也要实现Ordered接口。

注意:如果你想改变实例化后的Bean内容的话,你需要使用BeanPostProcessor接口。从技术上看,可以在BeanFactoryPostProcessor内部操作Bean的实例(通过使用BeanFactory.getBean()方法),这样的话会造成过早地实例化Bean,违反标准的容器生命周期。这会造成负面影响,例如错过一些后置处理器的处理。(因为一个Bean通过BeanFactory.getBean()方法生成Bean(如果是singleton的Bean)后,在容器集体生成Bean的处理里就不会再次生成这个Bean了。容器集体生成的Bean时,如果有后置处理器,会调用后置处理器进行对Bean进行修改。但在BeanFactoryPostProcessor里调用BeanFactory.getBean()方法生成Bean时,后置处理器还没有生成,所以调用BeanFactory.getBean()方法成生的Bean就无法接收到后置处理器的处理)

BeanFactoryPostProcessor的作用是针对每个配置它的容器。和BeanPostProcessor一样,在哪个容器里配置了这个接口的实现类,就作用于哪个容器,即使配置它的容器是一个有层级关系的容器。

Spring定义了一些Bean Factory后置处理器,例如PropertyOverrideConfigurer和PropertyPlaceholderConfigurer。

注意:一般来说,你不会把Bean Factory后置处理器配置成“延迟加载”。如果你把它配置成了延迟加载,而且没有任何Bean依赖它,那它就不会被初始化。因此,如果你在<beans/>元素的定义中使用了'default-lazy-init'属性,请确信你的各个BeanFactoryPostProcessor标记为'lazy-init="false"'。


看了上面关于Aware接口和后置处理器接口(BeanPostProcessor和BeanFactoryPostProcessor),感觉他们之间的功能有点乱,说一下他们之间的区别:

(1)Aware接口的方法(都是setXXX方法)是在Bean的属性被设置之后,初始化方法初始化方法(InitializingBean接口的方法,或@PostContruct等)执行之前被调用;BeanPostProcessor接口由两个回调方法组成,初始化方法(InitializingBean接口的方法,或@PostContruct等)执行之前调用BeanPostProcessor的postProcessBeforeInitialization回调,并在初始化方法执行之后调用BeanPostProcessor的postProcessAfterInitialization回调;BeanFactoryPostProcessor是在容器初始化之前被调用。

(2)从功能上来看,BeanPostProcessor主要是用来对实例化后的Bean做操作;BeanFactoryPostProcessor可以取得BeanFactory的引用,对实例化前的Bean定义做操作。Aware接口分的比较细,可以取得各种引用,例如:ApplicationContext,BeanFactory,MessageSource等等。在ApplicationContextAware接口里,也可以可以通过ApplicationContext取得BeanFactory,但要注意Aware接口方法的被调用时间点和BeanFactoryPostProcessor不一样。(也可以使用BeanFactoryAware接口取得BeanFactory)

(3)从实现上看,Aware接口的回调其实是通过BeanPostProcessor接口实现的。可以看一下ApplicationContextAwareProcessor这个类,这个类继承了BeanPostProcessor接口。这个类的处理逻辑就是,看Bean是否是几个Aware接口的实例,如果是的话就调用接口提供的回调。

 


26,使用FactoryBean,来自定义Bean初始化逻辑。

FactoryBean接口是插入到Spring IoC容器用来定制实例化逻辑的一个接口点。如果你有一些复杂的初始化代码用Java可以更好来表示,而不是用(可能)冗长的XML,那么你就可以创建你自己的FactoryBean,并在那个类中写入复杂的初始化动作,然后把你定制的FactoryBean插入容器中。
FactoryBean接口提供三个方法:
Object getObject():返回一个由这个工厂创建的对象实例。这个实例可能被共享(取决于isSingleton()的返回值是singleton或prototype)。
boolean isSingleton():如果要让这个FactoryBean创建的对象实例为singleton则返回true,否则返回false。
Class getObjectType():返回通过getObject()方法返回的对象类型,如果该类型无法预料则返回null。
在Spring框架中FactoryBean的概念和接口被用于多个地方;在本文写作时,Spring本身提供的FactoryBean接口实现超过了50个。

最后,有时需要向容器请求一个真实的FactoryBean实例本身,而不是它创建的bean。这可以通过在FactoryBean(包括ApplicationContext)调用getBean方法时在bean id前加'&'(没有单引号)来完成。因此对于一个假定id为myBean的FactoryBean,在容器上调用getBean("myBean")将返回FactoryBean创建的bean实例,但是调用getBean("&myBean")将返回FactoryBean本身的实例。


27,Annotation

如果注解和XML文件配置对同一个属性等进行配置的话,XML的配置会覆盖注解的配置。

使用<context:annotation-config/>标签进行自动注入的话,隐含使用了几个后置处理器:

  • AutowiredAnnotationBeanPostProcessor
  • CommonAnnotationBeanPostProcessor
  • PersistenceAnnotationBeanPostProcessor
  • aforementionedRequiredAnnotationBeanPostProcessor

<context:annotation-config/>标签只会查找被定义在相同ApplicationContext中的注解Bean。意思就是,如果你把<context:annotation-config/>标签放入一个DispatcherServlet的WebApplicationContext中的话,它只会检查Controller中的注解Bean,不会检查Service。

为什么呢?这要说一下WebMVC的结构,

ApplicationContext在Spring中是有作用域的。在WebMVC框架中,每个DispatcherServlet都有一个自己的WebApplicationContext,这个WebApplicationContext继承所有被定义在root WebApplicationContext中Bean。root WebApplicationContext中包含了,在各个context或Servlet实例中共享的Bean。这些被继承的Bean,在具体的作用域中会被覆盖(override),这些作用域中的Bean只有这个Servlet实例可以使用。

@Autowired注解的不常见的用法:把某个类型或它的子类型放到一个数组或集合里,如果你想控制放到集合里的顺序的话,让这个类型的类实现Ordered接口。

    // 放到数组里

    @Autowired
    private MovieCatalog[] movieCatalogs;

    // 放到集合里

    @Autowired
   public void setMovieCatalogs(Set<MovieCatalog> movieCatalogs) {
       this.movieCatalogs = movieCatalogs;
    }

放到Map集合里也是可以的,Map的Key就是Bean的Name属性,Value就是那个Bean。

    @Autowired
    public void setMovieCatalogs(Map<String, MovieCatalog> movieCatalogs) {
       this.movieCatalogs = movieCatalogs;
    }

如果被加了@Autowired注解的属性,方法或构造函数没有相对应的对象能够注入的话,会报错。但可以使用required属性来设置不报错。

    @Autowired(required=false)
    public void setMovieFinder(MovieFinder movieFinder) {
      this.movieFinder = movieFinder;
    }

每个类,只能有一个构造函数使用“默认为required为true的@Autowired”标注,但可以使用“required为false的@Autowired”来标注多个构造函数。在后面那种情况下,只有满足“贪婪条件”的构造函数被调用,“贪婪条件”意思是构造函数的参数能够最大程度的被满足的意思。

@Autowired的required属性和@Required相比,前者更为推荐。@Autowired的required属性用来表明,如果属性不是必须自动注入的话,没有有合适的类型,就不注入。@Required注解是强制属性必须被注入,如果没有被注入的话,就会抛出异常。

(如果只使用@Autowired而不用required属性的话,效果和@Required一样。感觉@Required想要表达的意思是某个的属性必须补以注入,不管你用什么方式注入(xml还是什么))


你也可以使用@Autowired来注入那些广为人知的接口:BeanFactory, ApplicationContext, Environment, ResourceLoader,ApplicationEventPublisher, MessageSource。以及他们的子接口 ConfigurableApplicationContext,ResourcePatternResolver等。(这个还每一次知道)

public class MovieRecommender {
    @Autowired
    private ApplicationContext context;
    public MovieRecommender() {
    }
    // ...
}

注意:BeanPostProcessor处理这些@Autowired, @Inject,@Resource, 和@Value注解,就意味着不能把这个注解加到BeanPostProcessor或BeanFactoryPostProcessor上面。BeanPostProcessor或BeanFactoryPostProcessor必须用@Bean注解或XML进行显示声明。


28,@Primary注解是用在生成的Bean上面,代表如果有几个相类似的Bean作为注入对象时,优先使用加了@Primary的Bean进行注入。@Qualifiers用在要被注入的属性或参数上面,使用@Qualifiers("beanName")的方式来指明,如果有几个相同条件的Bean可以被注入时,使用BeanName为我们指定的Bean进行注入。@Qualifiers可以被用在构造函数或方法的参数上。

    @Autowired
    public void prepare(@Qualifier("main")MovieCatalog movieCatalog,
            CustomerPreferenceDao customerPreferenceDao) {
        this.movieCatalog = movieCatalog;
        this.customerPreferenceDao = customerPreferenceDao;
    }

作为一个对应手段,Bean Name被用作qualifier的值。你也可以指定,要求使用BeanID为“main”的Bean注入(像最常用的方法一样),而不使用Qualifier标签,达到一样的注入效果。尽管你可以像上面一样,使用指定BeanName的方法来指明你要注入的Bean,但@Autowired注解还是会默认地使用“类型匹配注入”和确认“qualifier的值是是否相同”的方式,来控制可注入的Bean。这就意味着,使用qualifier的值能够在“类型匹配”的基础上,更进一步缩小符合条件的Bean的范围,而不是只用BeanID来指定一个Bean。好的Bean qualifier的值,应该用“main”,“EMEA”或是“persistent”这样的词(我想应该是BeanName的意思)来表达一个具体的功能的特点,和BeanID没有关系。(因为有些时候BeanID是自动产生的,无法指定,就像上面的匿名Bean定义的例子)

也可以通过Qulifier来指定集合的值,例如上面的Set<MovieCatalog>例子。在这个例子中,所有符合Qulifier条件的Bean都会被注入到集合中。这就暗示着,Qulifier的值不是唯一的,它他们的值被用来做为作为一种筛选条件。例如:你定义了多个MovieCatalog Bean,用相同的Qulifier名“action”,所有这些Bean都会被注入到一个使用了@Qualifier("action")的Set<MovieCatalog>集合中。

注意:如果你想要通过name进行annotation注入的话,最好不要使用@Autowired注解,即使从技术上可以通过把Bean name设置成@Qualifier的值,来引用想使用的Bean。作为替代手段,使用JSR-250的@Resource注解。这个注解主要用来,用Bean的唯一的name,引用一个具体的component。@Autowired还有一些不同的含义:在通过类型来确定一些符合范围的Bean后,Qualifier的值会做为另一个条件来筛选Bean。


你可以创建自定义的Qualifier注解:

@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Genre {

String value();
}

你可以在<bean/>标签里面,添加<qualifier/>标签,然后指定type和value属性,type属性指定为你的自定义qualifier。指定type属性时,要用包+类的方式。如果类名没有冲突(重名)的话,可以只用类名。
    <bean class="example.SimpleMovieCatalog">
        <qualifier type="Genre" value="Action"/>
        <!-- inject any dependencies required by this bean -->
    </bean>

    <bean class="example.SimpleMovieCatalog">
        <qualifier type="example.Genre" value="Comedy"/>
        <!-- inject any dependencies required by this bean -->
    </bean>


有一些情况下,只需要注解而不用指定具体值。这种方法,在注解被用来作为一个通用的目的,而且被应用到几种不同类型的注入时,非常有用。例如,你可以定义一个offline类型的注解,当互连网络连接不可用时,主动或被动地被查找并使用被offline注解的Bean,去做一些处理。
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Offline {

}

public class MovieRecommender {

    @Autowired
    @Offline
    private MovieCatalog offlineCatalog;

    // ...

}

<bean class="example.SimpleMovieCatalog">
    <qualifier type="Offline"/>
    <!-- inject any dependencies required by this bean -->
</bean>


你也可以自定义qualifier注解,使自定义注解接受你指定的属性,而不是value属性。如果自定义多个属性,在使用该注解时,只有所有属性都被满足的Bean,才会做为可能被注入的Bean的候选。


如果有Bean实现了一个泛型接口,例如:Store<String>或Store<Integer>,你可以把这样的Bean注入到这个泛型接口里(泛型里的具体类型(int or String)也有一些qualifier作用的意思)。
@Configuration
public class MyConfiguration {
    @Bean
    public StringStore stringStore() {
        return new StringStore();// 实现了Store<String>接口
    }
    @Bean
    public IntegerStore integerStore() {
        return new IntegerStore(); // 实现了Store<Integer>接口
    }

}

@Autowired
private Store<String> s1; // <String> qualifier, injects the stringStore bean
@Autowired
private Store<Integer> s2; // <Integer> qualifier, injects the integerStore bean

泛型qualifier也可以用在list,map和array上。

// Inject all Store beans as long as they have an <Integer> generic
// Store<String> beans will not appear in this list
@Autowired
private List<Store<Integer>> s;


29,自定义标签如何才能生效呢?使用CustomAutowireConfigurer。CustomAutowireConfigurer是一种BeanFactoryPostProcessor。CustomAutowireConfigurer帮助你注册自定义注解,即使自定义注解中没有使用@Qualifier注解。

<bean id="customAutowireConfigurer"
        class="org.springframework.beans.factory.annotation.CustomAutowireConfigurer">
    <property name="customQualifierTypes">
        <set>
            <value>example.CustomQualifier</value>
        </set>
    </property>
</bean>


30,Spring也支持使用@Resource注解注入,它可以用在属性或Setter方法上。@Resource注解有一个name属性,Spring会默认地把值作为被注入的BeanName。
public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Resource(name="myMovieFinder")
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

}

注意:注解提供的name,是在ApplicationContext里被解析作为一个bean name使用的。在ApplicationContext里使用的是CommonAnnotationBeanPostProcessor。如果你显示地配置了SimpleJndiBeanFactory,也可以通过JNDI解析name。但是还是推荐依靠默认的行为,和使用Spring JNDI的查找能力来保持层级间的间接关系。


30,@Resource独有的一种使用方法就是不显示指明name属性,和@Autowired有点像。@Resource进行类型的匹配,而不用名字匹配那些广为人知的依赖:BeanFactory, ApplicationContext, ResourceLoader, ApplicationEventPublisher, and MessageSource接口。
下面的例子,customerPreferenceDao首先查找BeanName是customerPreferenceDao的Bean,然后再去看是否有CustomerPreferenceDao类型的Bean。context字段被广为人知的ApplicationContext类型注入。
public class MovieRecommender {

    @Resource
    private CustomerPreferenceDao customerPreferenceDao;

    @Resource
    private ApplicationContext context;

    public MovieRecommender() {
    }

    // ...

}


31,“元注解”(Meta-annotation)。很多注解可以当做元注解使用。元注解是可以被使用在其它注解定义中的注解。@Service就是使用了@Component元注解。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component // Spring will see this and treat @Service in the same way as @Component
public @interface Service {

    // ....
}

元注解可以被组合使用,来创建新的注解。例如:@RestController就是@Controller和@ResponseBody的组合。

另外,组合注解可以选择使用元注解,来重新设置属性。当你只想暴露元注解的一个属性子集时,这个方法很有用。例如:@SessionScope注解硬编码Score Name,但允许自定义proxyMode。
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Scope(WebApplicationContext.SCOPE_SESSION)
public @interface SessionScope {

    /**
     * Alias for {@link Scope#proxyMode}.
     * <p>Defaults to {@link ScopedProxyMode#TARGET_CLASS}.
     */
    @AliasFor(annotation = Scope.class)
    ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS;

}

可以不声明prxoyMode属性,使用@SessionScope。
@Service
@SessionScope
public class SessionScopedService {
    // ...
}
或者覆盖prxoyMode属性的值
@Service
@SessionScope(proxyMode = ScopedProxyMode.INTERFACES)
public class SessionScopedUserService implements UserService {
    // ...
}

32,自动检测类和自动注册注解
在使用了@Configuration的类上,加上@ComponentScan注解,并把想要被扫描的包设置到basePackages属性的话,就可以进行自动检测类和自动注册注解。(你可以使用逗号、空格、分号来分隔多个你想指定的包)
@Configuration
@ComponentScan(basePackages = "org.example")
public class AppConfig  {
    ...
}
也可以会用它的value属性,相当于使用basePackages属性:ComponentScan("org.example")

另外,当你使用自动扫描标签<context:component-scan>的话,默认自动使用<context:annotation-config>标签和使用AutowiredAnnotationBeanPostProcessor,CommonAnnotationBeanPostProcessor这两个后置处理器。(你也可以通过注解配置,来不使用前面的那两个后置处理器)


默认情况下,@Component, @Repository, @Service, @Controller和含有@Component的定义注解会被自动扫描到。但你也可以修改扫描过滤器,通过使用@ComponentScanner的includeFilters或excludeFilters属性来设置过滤器。对于前面的那两个属性,你还需要设置它的type和Expression属性。
Type属性
annotation:要被扫描的注解
assignable:要被扫描的类
aspectj:通过aspectj表达式来表示要被扫描的component
regex:通过正则表达式来表示要被扫描的component
custom:org.springframework.core.type .TypeFilter接口的实现类。

@Configuration
@ComponentScan(basePackages = "org.example",
        includeFilters = @Filter(type = FilterType.REGEX, pattern = ".*Stub.*Repository"),
        excludeFilters = @Filter(Repository.class))
public class AppConfig {
    ...
}

你可以通过useDefaultFilters=false的属性设置,来关闭@ComponentScan的默认扫描设置。这样的话,就默认的@Component及相关注解就不会被扫描到了。


33,Spring的component可以生成BeanDefinition的定义给容器。你可以在使用了@Configuration的类中,使用@Bean注解来定义BeanDefinition。
@Component
public class FactoryMethodComponent {

    @Bean
    @Qualifier("public")
    public TestBean publicInstance() {
        return new TestBean("publicInstance");
    }

    public void doWork() {
        // Component method implementation omitted
    }

}

@Bean注解指定了工厂方法和其它一些BeanDefinition属性,就像通过qualifier 注解定义qualifier值(这里的使用Qualifier在Bean定义的地方,意思是给Bean定义了一个别名,好匹配那个标识了特定Qualifier值才能注入的地方)。其它的一些方法级注解和自定义的注解都可以使用,像@Scope,@Lazy。
注意:@Lazy除了使用在被定义的component上面,还可以使用在被注入的点(就是那些使用@Autowired或@Inject的地方)


下面例子中,把privateInstance这个Bean的Age属性,自动注入到方法参数country。这是利用Spring的表达式语言实现的(#{ <expression> })。如果使用了@Value注解,会预先配置好一个去查找BeanName的表达式解析器,当要解析表达式时,就会调用它。

@Component
public class FactoryMethodComponent {

    private static int i;

    @Bean
    @Qualifier("public")
    public TestBean publicInstance() {
        return new TestBean("publicInstance");
    }

    // use of a custom qualifier and autowiring of method parameters

    @Bean
    protected TestBean protectedInstance(
            @Qualifier("public") TestBean spouse,
            @Value("#{privateInstance.age}") String country) {
        TestBean tb = new TestBean("protectedInstance", 1);
        tb.setSpouse(spouse);
        tb.setCountry(country);
        return tb;
    }

    @Bean
    private TestBean privateInstance() {
        return new TestBean("privateInstance", i++);
    }

    @Bean
    @RequestScope
    public TestBean requestScopedInstance() {
        return new TestBean("requestScopedInstance", 3);
    }

}


加了@Bean注解的方法,在使用了@Configuration的类中和使用了@Component的类中的处理是不一样的。区别是,当方法或属性被调用时,CGLIB不会增强加了@Component注解的类。在使用@Component注解的类中,调用加了@Bean注解的方法或属性时,会使用CGLIB代理来创建协作对象的Bean metadata reference。这样的调用不普通的JAVA方法调用,而是通过容器调用,为了提供Bean的生命周期管理和代理(即使是通过编程调用的加了@Bean的方法)。与此相反,调用@Component类中的了@Bean方法的话,就是一个普通的JAVA调用,没有任何CGLIB处理或限制。
(上面说的提供Bean的生命周期管理和代理的意思就是,可以被后置处理器等处理)
具体的例子请参看:http://blog.csdn.net/ttjxtjx/article/details/49866011

注意:你可以把一个加了@Bean的方法,定义为一个static方法。这种Static方法可以在,定义他的类没有被实例化的情况下,被调用(静态方法的特点)。这个特点在定义后置处理器(BeanFactoryPostProcessor or BeanPostProcessor)时意义。因为后置处理器在容器生命周期之前被初始化,并避免触发其它配置。
Static的@Bean方法的调用,不会被容器拦截,即使这个方法是使用@Configuration注解的方法。这是由于技术限制:CGLIB只能覆盖非Static方法。@Bean方法的直接调用,就是普通的Java调用,只会返回一个独立(没有Bean生命周期)的实例。

@Bean方法的可见性,对生成Bean Definition的定义没有直接影响。你可以任意地声明你的工厂方法在没有@Configuration的类中。然而,普通的@Configuration类中的@Bean方法应该是可复写的,不能是private或是final的。
@Bean方法所在类的基类如果包含了@Configuration或@Component的话,@Bean方法也会被扫描到。这样做增加了很多灵活性在“组合复杂的配置”,甚至在多继承方面(通过Java8的default方法)。
最后说一下,一个类可以有多个@Bean方法,而这些@Bean方法是用来生成相同的Bean。根据运行时能得到的依赖,来决定哪个@Bean方法被调用。这个地方的算法和前面说过的多个@Autowired构造函数的“贪婪条件”算法一样,最多条件被满足的方法会被调用。

34,当一个componet被自动检测到,会按BeanNameGenerator的策略来创建bean name。默认使用模板注解(@Component, @Repository, @Service, and @Controller)的Value做为Bean Name。如果没有Value,默认的名字生成器会把类名的首字母小写,作为名字。
如果你不想使用默认的名字生成器,你可以实现BeanNameGenerator接口,把实现类写到@ComponentScan的属性上。
@ComponentScan(basePackages = "org.example", nameGenerator = MyNameGenerator.class)
作为一个一般的规则,为注解指定一个name,这样其它Components可随时引用到它。另一方面,当容器负责注入时,自动产生名字也没什么问题。


35,作用域自己检测component。
作为一个Spring管理的component,用作自动检测的,默认的普遍的作用域就是singoton。但有时候你需要通过@Scope来指定一个不同的作用域,例如:
@Scope("prototype")
@Repository
public class MovieFinderImpl implements MovieFinder {
    // ...
}

如果要实现一个自定义的作用域解析,就要实现ScopeMetadataResolver接口,在@ComponentScan注解上指定接口的实现类。
@Configuration
@ComponentScan(basePackages = "org.example", scopeResolver = MyScopeResolver.class)
public class AppConfig {
    ...
}

当使用一个非singleton作用域时,必须为对象创建一个作用域代理。所以需要指定代理类型:no, interfaces, targetClass.
@Configuration
@ComponentScan(basePackages = "org.example", scopedProxy = ScopedProxyMode.INTERFACES)
public class AppConfig {
    ...
}


36,关于@Qualifier,关使用@ComponentScan注解来扫描components时,你应该把@Qualifier注解用到类型级别。例如:下面的例子都是把注解放到了类定义上面。
@Component
@Qualifier("Action")
public class ActionMovieCatalog implements MovieCatalog {
    // ...
}

@Component
@Genre("Action")
public class ActionMovieCatalog implements MovieCatalog {
    // ...
}

@Component
@Offline
public class CachingMovieCatalog implements MovieCatalog {
    // ...
}


37,对于依赖注入,JSR330定义了一组关于依赖注入的注解,这些注解也被Spring支持。但@Named or @ManagedBean无法用来做自定义注解的一部分,来定义自定义注解。
JSR330和Spring的注解对应关系如下:
Autowired = Inject
Component = Named / ManagedBean(ManagedBean是JSR-250定义的)
@Scope("singleton") = @Singleton
@Qualifier = @Qualifier / @Named
@Value = 无
@Required = 无
@Lazy = 无
ObjectFactory = 无


38,@Bean注解被用来表示,用一个方法来实例化,配置,并且初始化一个新的对象,这个对象是被Spring容器管理的。你可以在使用@Component的类中使用@Bean注解方法,但@Bean注解方法经常被用在使用@Configuration的类中。
在类中使用@Configuration注解,表示这个类主要是用来进行Bean定义的。而且使用@Configuration的类允许,内部Bean依赖(所依赖的Bean是由另一个@Bean方法提供的)

当带有@Bean的方法被使用在,没有@Configuration注解的类中时,这些方法被称作“lite mode”。例如@Bean方法被声明在@Component方法中。
不像full @Configuration(就是在@Configuration的类中使用@Bean方法),lite mode的@Bean方法不能轻易地声明一个“内部Bean依赖”(就是依赖的Bean,是由一个@Bean方法提供的)。通常,在lite mode,一个@Bean方法不应该调用另一个@Bean方法。
只有在@Configuration类中使用@Bean方法的这种使用方法,才是一个推荐的方式,来保证一直使用Full @Configuration mode。这会防止相同的Bean方法在偶然的情况下被调用多次,并且还能帮助减少不易察觉的Bug(那种在lite mode下很难追踪的Bug)


40,当使用@Configuration的类做为一种输入时,使用@Configuration的类会被注册为一个Bean,所有@Bean方法也会被注册为Bean。当使用@Component和JSR-330的注解的类作为一种输入时,使用这些注解的类也会被注册为Bean,而且它们被认为是要为被注入其它地方的Bean。(而@Configuration的类不会被这么认为,它的@Bean方法所产生的Bean会被认为是要被用来注入到其它地方的Bean)。
AnnotationConfigApplicationContext接口,像ClassPathXmlApplicationContext一样,不同点是不需要XML的配置。AnnotationConfigApplicationContext对@Configuration还有其它JSR330注解,没有任何限制。
public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
    MyService myService = ctx.getBean(MyService.class);
    myService.doStuff();
}


41,一个使用了无参构造函数进行初始化的AnnotationConfigApplicationContext,在配置时要使用Register()方法,这个方法在编程创建AnnotationConfigApplicationContext时非常有用。
public static void main(String[] args) {
    AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
    ctx.register(AppConfig.class, OtherConfig.class);
    ctx.register(AdditionalConfig.class);
    ctx.refresh();
    MyService myService = ctx.getBean(MyService.class);
    myService.doStuff();
}

我们可以通过@ComponentScanner注解来扫描包,也可以使用AnnotationConfigApplicationContext的scan方法来扫描包,通过refresh方法把所有@Bean方法生产的Bean都注册到容器里。
public static void main(String[] args) {
    AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
    ctx.scan("com.acme");
    ctx.refresh();
    MyService myService = ctx.getBean(MyService.class);
}

42,WebApplicationContext是AnnotationConfigApplicationContext接口的一个变体。AnnotationConfigWebApplicationContext是WebApplicationContext接口的一个实现类。当配置了ContextLoaderListener时,这个实现类就会被容器使用。下面是一段spring MVC web程序的web.xml文件的配置。注意contextClass的context-param和init-param属性

<web-app>
    <!-- Configure ContextLoaderListener to use AnnotationConfigWebApplicationContext
        instead of the default XmlWebApplicationContext -->
    <context-param>
        <param-name>contextClass</param-name>
        <param-value>
            org.springframework.web.context.support.AnnotationConfigWebApplicationContext
        </param-value>
    </context-param>

    <!-- Configuration locations must consist of one or more comma- or space-delimited
        fully-qualified @Configuration classes. Fully-qualified packages may also be
        specified for component-scanning -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>com.acme.AppConfig</param-value>
    </context-param>

    <!-- Bootstrap the root application context as usual using ContextLoaderListener -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!-- Declare a Spring MVC DispatcherServlet as usual -->
    <servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- Configure DispatcherServlet to use AnnotationConfigWebApplicationContext
            instead of the default XmlWebApplicationContext -->
        <init-param>
            <param-name>contextClass</param-name>
            <param-value>
                org.springframework.web.context.support.AnnotationConfigWebApplicationContext
            </param-value>
        </init-param>
        <!-- Again, config locations must consist of one or more comma- or space-delimited
            and fully-qualified @Configuration classes -->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>com.acme.web.MvcConfig</param-value>
        </init-param>
    </servlet>

    <!-- map all requests for /app/* to the dispatcher servlet -->
    <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <url-pattern>/app/*</url-pattern>
    </servlet-mapping>
</web-app>


43,把@Bean注解加在方法上,来声明一个Bean时,bean name默认和方法名一样。或者用@Bean(name = "myFoo")来指定一个名字。或者指定多个名字:@Bean(name = { "dataSource", "subsystemA-dataSource", "subsystemB-dataSource" })。或者加给方法加一段说明:@Description("Provides a basic example of a bean")。

一个@Bean方法可以有任意个参数,来描述要通过方法生成的Bean的依赖。例如:下面的TransferService需要一个AccountRepository依赖,我们可以通过方法参数来赋值。(没测试过,参数accountRepository好像是可以自动被容器注入)
@Configuration
public class AppConfig {

    @Bean
    public TransferService transferService(AccountRepository accountRepository) {
        return new TransferServiceImpl(accountRepository);
    }

}

@Bean注解支持指定任意的初始化方法和销毁方法属性。
public class Foo {
    public void init() {
        // initialization logic
    }
}
public class Bar {
    public void cleanup() {
        // destruction logic
    }
}
@Configuration
public class AppConfig {

    @Bean(initMethod = "init")
    public Foo foo() {
        return new Foo();
    }

    @Bean(destroyMethod = "cleanup")
    public Bar bar() {
        return new Bar();
    }

}


注意:默认地,如果使用Java配置来生成Bean,而且Bean有public的close或shutdown方法的话,这两个方法会被自动地被做为销毁回调函数被调用。如果你的Bean中有这两个方法,并且你不想在容器关闭时“不”被调用的话,你可以使用@Bean(destroyMethod="")方法。

上面的@Bean(initMethod = "init")的设置的结果,可以翻译成下面的这种实现方式。

@Configuration
public class AppConfig {
    @Bean
    public Foo foo() {
        Foo foo = new Foo();
        foo.init();
        return foo;
    }

    // ...

}


44,可以用@Scope注解来指定Bean的作用域,例如:@Scope("prototype")。如果不指定默认为singleton。

Spring提供了一种方便的方式给依赖加“作用域代理”。最简单的方法是在XML文件上使用<aop:scoped-proxy/>标签。如果是Java配置的话,直接使用相对应的注解,例如:@SessionScope。
(以前的话,需要像@Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)这样使用,但最新的SessionScope注解内部已经默认实现了代理:ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS)


45,注入内部Bean(inter-bean)依赖
在@Configuration类内部的@Bean方法,也可以用来定义内部Bean依赖。在下面的例子中,foo Bean接受一个bar的引用,通过foo的构造函数,例如:
@Configuration
public class AppConfig {

    @Bean
    public Foo foo() {
        return new Foo(bar());
    }

    @Bean
    public Bar bar() {
        return new Bar();
    }

}
注意:方法级的内部Bean依赖,只有在@Bean方法被声明在一个@Configuration类中才好用。你无法在@Configuration类中,声明内部Bean依赖。

(像上面那个调用return new Foo(bar()),bar()应该不是简单的Java方法调用返回一个new Bar,而是Spring调用bar()方法,返回一个Bean,给Foo)

46,Lookup方法注入
Lookup方法注入是一个高级特性,你可能会很少用到。当一个作用域是singleton的Bean有一个作用域是prototype的依赖时,会很有用。
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();
}
使用Java配置,你可以创建一个CommandManager的子类,来实现抽象方法,像下面那样来查找一个新的(prototype)的command对象。(没具体试过,但像下面那样代码return asyncCommand(),应该不是普通的Java方法调用,而是返回asyncCommand()通过Spring生成的Bean。)
@Bean
@Scope("prototype")
public AsyncCommand asyncCommand() {
    AsyncCommand command = new AsyncCommand();
    // inject dependencies here as required
    return command;
}

@Bean
public CommandManager commandManager() {
    // return new anonymous implementation of CommandManager with command() overridden
    // to return a new prototype Command object
    return new CommandManager() {
        protected Command createCommand() {
            return asyncCommand();
        }
    }
}


47,关于Java配置的内部工作方式
下面的代码中,有两个方法调用同一个内部Bean依赖。

@Configuration
public class AppConfig {

    @Bean
    public ClientService clientService1() {
        ClientServiceImpl clientService = new ClientServiceImpl();
        clientService.setClientDao(clientDao());
        return clientService;
    }

    @Bean
    public ClientService clientService2() {
        ClientServiceImpl clientService = new ClientServiceImpl();
        clientService.setClientDao(clientDao());
        return clientService;
    }

    @Bean
    public ClientDao clientDao() {
        return new ClientDaoImpl();
    }

}
clientDao()被clientService1()和clientService2()调用。clientDao()这个方法的功能是创建一个新的实例,并返回它。既然它被调用了两次,你会认为有两个实例存在,这就是问题。在Spring里面,Bean的默认作用域是singleton,所有的含有@Configuration的类,在容器启动的时候,都会被CGLIB给子类化。在子类里面,在调用父方法创建新的实例之前,子方法都会先确认容器是否有Bean的缓存。因为clientDao()生成的Bean默认为singleton作用域,所以两次调用返回的是相同的实例。
(根据Bean的作用域的不同,创建Bean的行为也会不同,我们在这里说的是singleton作用域)


48,可以像<import/>标签一样,使用@Import注解来把想要载入的Bean,定义到一个作为配置用的类中。
@Configuration
public class ConfigA {
     @Bean
    public A a() {
        return new A();
    }
}

@Configuration
@Import(ConfigA.class)
public class ConfigB {
    @Bean
    public B b() {
        return new B();
    }
}

public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(ConfigB.class);

    // now both beans A and B will be available...
    A a = ctx.getBean(A.class);
    B b = ctx.getBean(B.class);
}
光声明ConfigB类的载入就可以了,ConfigA因为是在ConfigB中被Import了,所以会随着ConfigB的载入而自动载入。

49,上面的例子能很好的工作,但有点过于简化了。在实际的应用场景中,被依赖的Bean会被配置到一个专门用作配置的类中。那些被配置的Bean如果需要依赖其它Bean,可以使用@Bean方法上设置参数来注入他们的依赖。下面的例子中,被配置的ServiceConfig和RepositoryConfig类中的@Bean方法需要依赖,利用方法参数把依赖注入进去。而所依赖的Bean,是在配置类SystemTestConfig中生成的。
@Configuration
public class ServiceConfig {
    @Bean
    public TransferService transferService(AccountRepository accountRepository) {
        return new TransferServiceImpl(accountRepository);
    }
}

@Configuration
public class RepositoryConfig {
    @Bean
    public AccountRepository accountRepository(DataSource dataSource) {
        return new JdbcAccountRepository(dataSource);
    }
}

@Configuration
@Import({ServiceConfig.class, RepositoryConfig.class})
public class SystemTestConfig {
    @Bean
    public DataSource dataSource() {
        // return new DataSource
    }
}

public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(SystemTestConfig.class);
    // everything wires up across configuration classes...
    TransferService transferService = ctx.getBean(TransferService.class);
    transferService.transfer(100.00, "A123", "C456");
}

注意:请确认好你要注入的依赖是最简单的方式。因为使用@Configuration的类是在容器初始化时很早的就被处理了,如果在那个时候进行强制注入,会导致不可预期的过早初始化(估计这里指的是所依赖的Bean过早初始化了,可能就不会被那个后置处理器等处理了)。如果可以的话,请使用参数注入的方式。
也要注意通过@Bean方法定义的后置处理器(BeanPostProcessor and BeanFactoryPostProcessor)的情况。这样的后置处理器,应该用static @Bean方法来声明,不应该使用@Configuration这种配置类,或者通过初始化来触发Bean的生成。


记住,带有@Configuration的类,其实就是容器里的一个Bean,所以它也可以使用@Autowired和@Value进行注入。例如

@Configuration
public class ServiceConfig {
    @Autowired
    private AccountRepository accountRepository;
    @Bean
    public TransferService transferService() {
        return new TransferServiceImpl(accountRepository);
    }
}

@Configuration
public class RepositoryConfig {
    private final DataSource dataSource;
    @Autowired
    public RepositoryConfig(DataSource dataSource) {
        this.dataSource = dataSource;
    }
    @Bean
    public AccountRepository accountRepository() {
        return new JdbcAccountRepository(dataSource);
    }
}

@Configuration
@Import({ServiceConfig.class, RepositoryConfig.class})
public class SystemTestConfig {
    @Bean
    public DataSource dataSource() {
        // return new DataSource
    }
}

public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(SystemTestConfig.class);
    // everything wires up across configuration classes...
    TransferService transferService = ctx.getBean(TransferService.class);
    transferService.transfer(100.00, "A123", "C456");

}

注意:在@Configuration类中,使用构造函数注入的方式,是在SpringFramework4.3及以上才支持的。也请注意,当目标Bean只有一个构造函数时,可以省略构造函数上的@Autowired方法。例如上面的RepositoryConfig类的构造函数上面的@Autowired就不需要。


50,选择性地使@Configuration类或@Bean方法可用,或不可用
在某种系统状态下,选择性地使@Configuration类或@Bean方法可用或不可用,有时候是很有用的。一个常见的例子就是,在Spring Environment下,使用@Profile注解使Bean可用。
@Profile注解实际上,是使用@Conditional注解来实现的(@Conditional更为有灵活性的)。@Conditional注解内部表明了具体的Condition接口的实现,就是在@Bean被注册之前应当先进行某种确认。
Condition接口的实现提供了一个match()方法,这个方法返回true或false。下面就是@Profile的Condition接口的实现。
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
    if (context.getEnvironment() != null) {
        // Read the @Profile annotation attributes
        MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
        if (attrs != null) {
            for (Object value : attrs.get("value")) {
                if (context.getEnvironment().acceptsProfiles(((String[]) value))) {
                    return true;
                }
            }
            return false;
        }
    }
    return true;
}


51,XML配置和Java配置的结合
以XML文件为主,XML包含Java配置,Java配置主要是一些@Configuration类和@Bean方法。注意:下面的<bean class="com.acme.AppConfig"/>没有声明id属性,因为这个Bean是用来声明配置的,不需要也不应该用来注入到哪里。
<beans>
<!-- enable processing of annotations such as @Autowired and @Configuration -->
    <context:annotation-config/>
    <context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>

    <bean class="com.acme.AppConfig"/>

    <bean class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
</beans>


以Java配置为主,在Java配置中引用XML文件,XML文件做一些配置
@Configuration
@ImportResource("classpath:/com/acme/properties-config.xml")
public class AppConfig {

    @Value("${jdbc.url}")
    private String url;

    @Value("${jdbc.username}")
    private String username;

    @Value("${jdbc.password}")
    private String password;

    @Bean
    public DataSource dataSource() {
        return new DriverManagerDataSource(url, username, password);
    }

}

properties-config.xml
<beans>
    <context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>
</beans>



52,Environment抽象
Environment是一个整合了的抽象,这个抽象描述了关于应用环境的两个方面:profiles和properties。
一个profile是一组被命名的Bean定义,当指定的profile生效时,被定义的这组Bean会被注册到容器里。Bean可以通过注解或XML定义,赋给一个profile。Environment和profile之间的关系是,前者可以决定哪个profile生效,和哪个profile默认地生效。

Properties在所有应用中扮演了重要的角色,它是源于properties文件, JVM system properties, system environment variables, JNDI, servlet context parameters, ad-hoc Properties objects, Maps等等。Environment和Properties之间的关系是,前者给使用提供一个方便的服务接口,通过配置property源和解析他们。

53,Bean definition profiles
Bean definition profiles是容器的一个核心机制,这个机制允许在不同的environments下,注册不同的Bean。environment这个词对于不同的用户,意味也不同。这个特点在很多方面都有帮助,包括:
 - 在内存数据源下工作,或在QA或生产环境里通过JNDI查找数据源
 - 在把应用发布到性能测试环境时,注册监视基础架构
 - 注册不同的自定义实现,为在客户A或客户B

让我们考虑一下第一个用例,一个应用需要一个数据源,在测试管理下,配置如下:
@Bean
public DataSource dataSource() {
    return new EmbeddedDatabaseBuilder()
        .setType(EmbeddedDatabaseType.HSQL)
        .addScript("my-schema.sql")
        .addScript("my-test-data.sql")
        .build();
}
让再考虑一下,如果这个应用被发布到QA或生产环境中,数据源配置如下:
@Bean(destroyMethod="")
public DataSource dataSource() throws Exception {
    Context ctx = new InitialContext();
    return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");
}
问题是,如何从现在的环境,切换致两种不同的环境。很长时间,Spring用户发明了几种方式来解决这个问题,依靠system environment variables和XML的<import/>的结合,通过<import/>标签含中有${placeholder}来解决配置文件路径(路径是依赖一个环境变量的值)正确性的问题。Bean definition profiles这个容器核心机制可以解决这个问题。


54,@Profile
@Profile注解表示,当一个或多个profile生效时,一个component就会被注册到容器。使用我们上面的例子,我们可以这样重写数据源的配置:
@Configuration
@Profile("dev")
public class StandaloneDataConfig {

    @Bean
    public DataSource dataSource() {
        return new EmbeddedDatabaseBuilder()
            .setType(EmbeddedDatabaseType.HSQL)
            .addScript("classpath:com/bank/config/sql/schema.sql")
            .addScript("classpath:com/bank/config/sql/test-data.sql")
            .build();
    }
}
@Configuration
@Profile("production")
public class JndiDataConfig {

    @Bean(destroyMethod="")
    public DataSource dataSource() throws Exception {
        Context ctx = new InitialContext();
        return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");
    }
}
注意:就像前面说过关于@Bean方法的说明一样,你可以使用“编程式的JNDI查找”:使用JndiTemplate/JndiLocatorDelegate帮助类或直接的JNDI InitialContext使用方法,而不是JndiObjectFactoryBean的变体(这会强制你声明把返回类型为FactoryBean类型)


@Profile也可以当作“元注解”,来创建新的注解。可以使用一个自定义的@Prodution注解,来替换@Profile("production")。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Profile("production")
public @interface Production {
}

在一个配置类中,@Profile可以使用在方法上。
@Configuration
public class AppConfig {

    @Bean
    @Profile("dev")
    public DataSource devDataSource() {
        return new EmbeddedDatabaseBuilder()
            .setType(EmbeddedDatabaseType.HSQL)
            .addScript("classpath:com/bank/config/sql/schema.sql")
            .addScript("classpath:com/bank/config/sql/test-data.sql")
            .build();
    }

    @Bean
    @Profile("production")
    public DataSource productionDataSource() throws Exception {
        Context ctx = new InitialContext();
        return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");
    }
}
注意:一个@Configuration类上有一个类级别的@Profile的话,如果这个@Profile不生效的话,所有@Bean方法和@Import注解都无法生效。如果一个@Component类或一个@Configuration类上有一个类级别的@Profile({"p1", "p2"})注解的话,只有p1 and/or p2都生效,类才会被注册到容器。还可以加表示非的前缀“!”,例如@Profile({"p1", "!p2"}),表示p1生效p2不生效时,才会被注册。


55,激活profile
有几种方法,其中一种就是,依赖Environment可以通过编程式的方法来激活。
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.getEnvironment().setActiveProfiles("dev");
ctx.register(SomeConfig.class, StandaloneDataConfig.class, JndiDataConfig.class);
ctx.refresh();

另外你可以通过spring.profiles.active属性来激活,可以在以下的地方定义这个属性:system environment variables, JVM system properties, 在web.xml中的servlet context parameters,JNDI的entry。在整合测试时,可以通过在spring-test模块里声明@Active标签来激活。
并不是在很多的profiles里只能激活一个profile,可以同时激活多个。例如:
编程式:ctx.getEnvironment().setActiveProfiles("profile1", "profile2");
参数式:-Dspring.profiles.active="profile1,profile2"

还可以指定默认有效的profile。没有指定任何profile时,默认的profile就会有效,例如:
@Configuration
@Profile("default")
public class DefaultDataConfig {

    @Bean
    public DataSource dataSource() {
        return new EmbeddedDatabaseBuilder()
            .setType(EmbeddedDatabaseType.HSQL)
            .addScript("classpath:com/bank/config/sql/schema.sql")
            .build();
    }
}
也可以用用编程的方式:在Environment上使用setDefaultProfiles()方法
属性的方式:指定spring.profiles.default属性。


56,PropertySource抽象
Environment抽象提供了,在可配置的有层级关系的property source中查找的操作。例如:
ApplicationContext ctx = new GenericApplicationContext();
Environment env = ctx.getEnvironment();
boolean containsFoo = env.containsProperty("foo");
System.out.println("Does my environment contain the 'foo' property? " + containsFoo);
在上面的代码中,我们可以看到一个高层次的查询(查询在当前的Environment中是否有foo属性)。对于这个查询,Environment对象完成了对一组PropertySource的查询。一个PropertySource就是一个简单的key-value的抽象,并且Spring的StandardEnvironment配置了两个PropertySource对象:一个代表一组JVM system properties(通过System.getProperties()),或者一组system environment variables(通过System.getenv())
注意:StandardEnvironment的property source,使用在单独的应用里。StandardServletEnvironment配置了额外的property source,包括servlet config和servlet context parameters。StandardPortletEnvironment配置了portlet config和portlet context parameters作为property source。这两个Environment都可以选择使用JndiPropertySource。

当使用StandardEnvironment,调用env.containsProperty("foo")语句时,如果在system property或environment variable中有定义的话,就会返回true。
注意:搜索是有先后顺序的(原文是:hierarchical)。默认地,system properties优先于environment variables,所以如果foo属性在两个地方都定义了,在使用env.getProperty("foo")的时候,会从system properties里读取,而不是environment variables里。记住,property值,不会合并,只会覆盖。
常用的StandardServletEnvironment的优先级顺序如下(上面的优先级高):
 - ServletConfig parameters(if applicable, e.g. in case of a DispatcherServlet context)
 - ServletContext parameters (web.xml context-param entries)
 - JNDI environment variables ("java:comp/env/" entries)
 - JVM system properties ("-D" command-line arguments)
 - JVM system environment (operating system environment variables)


最重要地,整个机制是可配置的。可能你有一个自定义的property source,你想把这它整合到查找内容里。没问题,只要你实现并且实例化你自己的PropertySource,把它加到当前Enviroment的的PropertySources里就行。
ConfigurableApplicationContext ctx = new GenericApplicationContext();
MutablePropertySources sources = ctx.getEnvironment().getPropertySources();
sources.addFirst(new MyPropertySource());
在上面的代码里MyPropertySource被加到查找的PropertySource里,并且查找的优先级最高。MutablePropertySources的API提供了一些方法,让你精确处理PropertySource。


57,@PropertySource
@PropertySource注解提供了一个方便的方式,来添加PropertySource到Spring的Enviroment。
假如一个文件“app.properties”包含键值对testbean.name=myTestBean,下面的@Configuration配置类使用了@PropertySource,以至于在调用testBean.getName()方法时,会返回"myTestBean"。
@Configuration
@PropertySource("classpath:/com/myco/app.properties")
public class AppConfig {
    @Autowired
    Environment env;

    @Bean
    public TestBean testBean() {
        TestBean testBean = new TestBean();
        testBean.setName(env.getProperty("testbean.name"));
        return testBean;
    }
}


任何出现在@PropertySource的占位符(${…​}),都会通过“已经解析了”的property source内容进行设置。
@Configuration
@PropertySource("classpath:/com/${my.placeholder:default/path}/app.properties")
public class AppConfig {
    @Autowired
    Environment env;

    @Bean
    public TestBean testBean() {
        TestBean testBean = new TestBean();
        testBean.setName(env.getProperty("testbean.name"));
        return testBean;
    }
}
假如上面的"my.placeholder",应该是已经注册的property source中的属性,例如:system properties 或environment variables。如果没有的话,就会使用默认值"default/path"。如果没有设置默认值,就会抛出IllegalArgumentException。

在标签里的占位符
以前,在标签里的占位符只能在JVM system properties或environment variables里查找并解析,但现在不是了(我看的文档是spring framework 4.3.3)。因为Enviroment抽象被整合到了容器里,很容易通过Environment来找到并解析点位符里的变量。这就意味着你可以随意配置解析处理,例如:改变system properties和environment variables的查找优先级,或为了不查找他们干脆移除他们。或者添加你自己的property source。
具体地说,对于下面的声明,只要custom属性在Enviroment里能取到就行,无论是在哪定义的。
<beans>
    <import resource="com/bank/service/${customer}-config.xml"/>
</beans>


58,ApplicationContext和BeaFactory 相比,多了一些面向框架的功能:
 - 用i18n的方式访问message(也就是国际化),通过MessageSource接口
 - 访问resource,例如:URL或文件,通过ResourceLoader接口。
 - 发布事件(接收事件的Bean实现ApplicationListener接口,发布事件使用ApplicationEventPublisher接口)
 - 通过HierarchicalBeanFactory 接口,载入多个(层级的)context,并且允许每个Context用于特定的layer,例如web layer。

ApplicationContext接口继承了MessageSource接口,因此能够提供国际化功能。Spring也提供了HierarchicalMessageSource接口,这个接口可以按层级的解析message。这些接口提供了Spring操作message解析的基础。方法如下:
 - String getMessage(String code, Object[] args, String default, Locale loc):从MessageSource里检索message的基础方法。如果没有从特定的locale里找到message,就使用方法参数里的默认message。被传递过去的参数,会被MessageFormat换成要替换的值。
 - String getMessage(String code, Object[] args, Locale loc):和上个方法基本一样,但没有“默认值”这个参数。如果找不到message,就会抛出NoSuchMessageException异常。
 - String getMessage(MessageSourceResolvable resolvable, Locale locale):上面方法中的属性被包装在一个叫做MessageSourceResolvable的类中。当有MessageSourceResolvable对象时,你可以用这个方法。


当ApplicationContext被加载,它会在context里面自动地搜索MessageSource的Bean定义。这个Bean必须叫做“messageSource”。如果Bean被找到的话,上面介绍的方法就可以访问message source。如果没有message source被找到,ApplicationContext就会去父容器中找相同名字的Bean,如果找到了就把Bean当成MessageSource使用;如果没找到,就会实例化一个DelegatingMessageSource,以便能接受上面方法的调用。


59,Spring提供了两个MessageSource的实现,ResourceBundleMessageSource and StaticMessageSource。两个实现也都实现了HierarchicalMessageSource,以便做嵌套message。StaticMessageSource提供了编程式地添加message到source,一般很少使用。ResourceBundleMessageSource例子如下:
<beans>
    <bean id="messageSource"
            class="org.springframework.context.support.ResourceBundleMessageSource">
        <property name="basenames">
            <list>
                <value>format</value>
                <value>exceptions</value>
                <value>windows</value>
            </list>
        </property>
    </bean>
</beans>
上面的例子显示,有3个resource bundle被定义到你的classpath:format, exceptions and windows。任何对message的解析,都通过ResourceBundles,以JDK的标准方式进行处理。对于上面的例子,假定两个resource bundle文件内容如下:
# in format.properties
message=Alligators rock!

# in exceptions.properties
argument.required=The {0} argument is required.

下面是使用MessageSource功能的例子。ApplicationContext接口继承了MessageSource接口,所以ApplicationContext接口实例可以映射成MessageSource接口实例。
public static void main(String[] args) {
    MessageSource resources = new ClassPathXmlApplicationContext("beans.xml");
    String message = resources.getMessage("message", null, "Default", null);
    System.out.println(message);
}
结果如下:
Alligators rock!

总结一下上面的例子,MessageSource是被定义在beans.xml文件中,这个文件要放在classpath的根路径中。messageSource的Bean定义引用了一些resource bundle,通过besenames属性。这3个文件,以list的形式传给了basenames属性,并且存放在classpath的根路径上:format.properties, exceptions.properties, and windows.properties。

60,下个例子,演示了MessageSource作为参数传给使用它的类:
<beans>

    <!-- this MessageSource is being used in a web application -->
    <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
        <property name="basename" value="exceptions"/>
    </bean>

    <!-- lets inject the above MessageSource into this POJO -->
    <bean id="example" class="com.foo.Example">
        <property name="messages" ref="messageSource"/>
    </bean>

</beans>

public class Example {

    private MessageSource messages;

    public void setMessages(MessageSource messages) {
        this.messages = messages;
    }
    public void execute() {
        String message = this.messages.getMessage("argument.required",
            new Object [] {"userDao"}, "Required", null);
        System.out.println(message);
    }
}
结果:
The userDao argument is required.


关于国际化,多种的MessageSource实现,按标准JDK的ResourceBundle方式,根据local来做相同的处理。简而言之,按照前面的例子来说,如果你想以British (en-GB) locale来解析message,你需要创建format_en_GB.properties, exceptions_en_GB.properties, and windows_en_GB.properties文件。
locale会根据应用的环境,来做不同的处理。下面的例子中,local是通过手动指定的:
# in exceptions_en_GB.properties
argument.required=Ebagum lad, the {0} argument is required, I say, required.

public static void main(final String[] args) {
    MessageSource resources = new ClassPathXmlApplicationContext("beans.xml");
    String message = resources.getMessage("argument.required",
        new Object [] {"userDao"}, "Required", Locale.UK);
    System.out.println(message);
}
结果:
Ebagum lad, the 'userDao' argument is required, I say, required.


如果想取得已经被定义的MessageSource的引用,可以使用MessageSourceAware接口。任何实现了MessageSourceAware接口Bean(在ApplicationContext中),都会被注入一个被创建并配置好的MessageSource。
注意:作为ResourceBundleMessageSource的一个替代品,Spring提供了ReloadableResourceBundleMessageSource类。这个变体支持相同的bundle文件模式,但是比标准JDK的ResourceBundleMessageSource实现更灵活。特别是,它允许读取文件从任何的Spring资源位置(不只是classpath),还支持随时地“重新”加载property文件。


61,标准和自定义事件
ApplicationContext也提供了事件处理机制,通过ApplicationEvent类和ApplicationListener接口。如果一个实现了ApplicationListener接口的Bean被注册到context里的话,每次ApplicationEvent被发布到ApplicationContext,Bean都会被通知到。这是一个标准的Observer设计模式。
注意:从4.2开始,事件基础架构有显著地提升,提供了注解的方式来发布任意事件,而不用继承ApplicationEvent。当这样一个对象被发布时,Spring会来包装它。

Spring提供了几个标准事件:
ContextRefreshedEvent:
当ApplicationContext被初始化或刷新的时候被发布。例如:使用ConfigurableApplicationContext接口的refresh()方法时。“初始化”意思是所有的Bean被加载,后置处理器的Bean被检测到并被使用了,singleton的Bean已经被预实例化,并且ApplicationContext对象已经可以使用。只要Conext没有被关闭,refresh可以被触发多次,只要被选择的ApplicationContext支持热refresh。例如:XmlWebApplicationContext支持热refresh,但 GenericApplicationContext不支持。
ContextStartedEvent:
当ApplicationContext启动时被发布。启动是通过ConfigurableApplicationContext的start()方法。“启动”的意思是,所有实现Lifecycle接口的Bean收到一个显示的启动信号。通常这个信号被用来重新启动Bean,在显示的停止之后。但也可以用来启动Components(那些没有配置成自动启动的Components),例如,在容器初始化时,还没有启动的那些Components。
ContextStoppedEvent:
当ApplicationContext停止时被发布。启动是通过ConfigurableApplicationContext的stop()方法。“停止”的意思是,所有实现Lifecycle接口的Bean收到一个显示的停止信号。一个停止了的context可以重新启动,通过start()方法的调用。
ContextClosedEvent:
当ApplicationContext关闭时被发布。启动是通过ConfigurableApplicationContext的close()方法。“停止”的意思是,所有singleton的Bean都被销毁了。一个关闭了的context到了它的生命周期的最后,不能重启动或重刷新。
RequestHandledEvent:
一个特定的web事件告诉所有的Bean,http request已经被接收。这个事件在Request完成后,被发布。这个事件只适用于,使用DispatcherServlet的web应用。


你也可以创建和发布自定义的事件,下面的例子展示了一个简单的继承了ApplicationEvent的类:
public class BlackListEvent extends ApplicationEvent {

    private final String address;
    private final String test;

    public BlackListEvent(Object source, String address, String test) {
        super(source);
        this.address = address;
        this.test = test;
    }

    // accessor and other methods...

}

想要发布一个自定义的ApplicationEvent话,需要调用ApplicationEventPublisher的publishEvent()方法。通常可以创建一个实现ApplicationEventPublisherAware接口的类,然后把它注册成Bean,来用作发布事件:
public class EmailService implements ApplicationEventPublisherAware {

    private List<String> blackList;
    private ApplicationEventPublisher publisher;

    public void setBlackList(List<String> blackList) {
        this.blackList = blackList;
    }

    public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
        this.publisher = publisher;
    }

    public void sendEmail(String address, String text) {
        if (blackList.contains(address)) {
            BlackListEvent event = new BlackListEvent(this, address, text);
            publisher.publishEvent(event);
            return;
        }
        // send email...
    }

}

在进行配置的时候 ,spring容器会检测到实现了ApplicationEventPublisherAware接口的EmailService,并且自动调用setApplicationEventPublisher()方法。实际上,被传进来的参数就是Spring容器本身。你是通过ApplicationEventPublisher接口,和应用容器作一个简单的交互。


如果要接收一个自定义的的ApplicationEvent,需要创建一个实现ApplicationListener接口的类,并把它注册成Bean:
public class BlackListNotifier implements ApplicationListener<BlackListEvent> {

    private String notificationAddress;

    public void setNotificationAddress(String notificationAddress) {
        this.notificationAddress = notificationAddress;
    }

    public void onApplicationEvent(BlackListEvent event) {
        // notify appropriate parties via notificationAddress...
    }

}
注意:使用你的自定义事件,ApplicationListener被参数化了。这意味着onApplicationEvent方法可以保持类型安全,避免任何的向下映射(类型转换)。你可以注册很多事件listener,但注意一点:默认地,事件listener接收事件时,方法是同步方法。这就说明,publishEvent()方法会被锁住,直到所有的listener完成事件处理。这个方式(同步并且单线程)的优点是,当一个listener接收到一个事件时,如果有一个事务Context,它会在publisher的事务context里进行处理(意思是所有的listener处理都是在一个事务里?)。如果你需要其它的事件发布策略,你可以看看ApplicationEventMulticaster 接口。

下面的例子演示了关于注册和配置的Bean定义:
<bean id="emailService" class="example.EmailService">
    <property name="blackList">
        <list>
            <value>known.spammer@example.org</value>
            <value>known.hacker@example.org</value>
            <value>john.doe@example.org</value>
        </list>
    </property>
</bean>

<bean id="blackListNotifier" class="example.BlackListNotifier">
    <property name="notificationAddress" value="blacklist@example.org"/>
</bean>
当emailService的sendEmail()方法被调用时,如果有任何要被做黑名单处理的email的话,自定义的BlackListEvent事件就会被发布。blackListNotifier这个Bean被注册成为一个ApplicationListener,所以收到了BlackListEvent事件。

注意:Spring事件机制的设计是为了,在相同的应用环境内,让Bean之间做简单的通讯。然而,为了更精细的企业整合需要,分离式维护的Spring Integration Porject在轻量级,面向模式,事件驱动架构方面,提供了完整的支持。
Spring Integration Porject:http://projects.spring.io/spring-integration/
面向模式:http://www.enterpriseintegrationpatterns.com/


62,从Spring4.2开始,事件Listeners可以通过EventListener注解,使用Bean的public方法进行注册。BlackListNotifier可以像下面这样写:
public class BlackListNotifier {

    private String notificationAddress;

    public void setNotificationAddress(String notificationAddress) {
        this.notificationAddress = notificationAddress;
    }

    @EventListener
    public void processBlackListEvent(BlackListEvent event) {
        // notify appropriate parties via notificationAddress...
    }

}
正如你看到的,从方法签名推断出监听的类型。这种方式对于泛型也好用。

如果你的方法想要监听几个事件,或者被定义为无参数的方法的话,可以在注解上指定事件类型:
@EventListener({ContextStartedEvent.class, ContextRefreshedEvent.class})
public void handleContextStart() {

}

也可以通过注解的confition属性,增加运行时过滤器。condition属性中使用SpELl表达式,对一个特定的事件,表达式能够匹配实际的方法调用。
例如:通知器可以被重写成:只有事件的test属性等于foo的时候,才会调用方法:
@EventListener(condition = "#event.test == 'foo'")
public void processBlackListEvent(BlackListEvent event) {
    // notify appropriate parties via notificationAddress...
}
可用的SpEL表达式如下:
event:#root.event
args:#root.args[0]
argument name:#iban or #a0 (one can also use #p0 or #p<#arg> notation as an alias).

注意:#root.event允许你,即使在方法签名是任意对象时,也可以访问事件对象。

如果你需要在事件处理结束时把发布另一个事件的话,可以改变方法的签名(把返回值,变成你要发布的事件),例如:
@EventListener
public ListUpdateEvent handleBlackListEvent(BlackListEvent event) {
    // notify appropriate parties via notificationAddress and
    // then publish a ListUpdateEvent...
}
上面的方法在处理完BlackListEvent后,发布一个ListUpdateEvent事件。如果你想要发布几个事件,只要返回一个事件的Collection集合。
注意:这种方法不支持asynchronous listeners。


asynchronous listeners(异步监听器)
如果你想用一个监听器,来异步地事件处理,可以使用@Async注解。
@EventListener
@Async
public void processBlackListEvent(BlackListEvent event) {
    // BlackListEvent is processed in a separate thread
}
注意下面的异步处理的限制:
 - 如果linsener抛出了一个异常,这个异常不会被传递到调用的地方,你需要检查AsyncUncaughtExceptionHandler来了解异常的细节。
 - 这样的异步listener不会做出回应(应该是不能像上面一样,把要发布的事件做为返回值)。如果你要想发布另一个事件作为处理结果,通过注入ApplicationEventPublisher来手动地发布事件。


63,listener的顺序
如果你想listener按顺序地被调用的话,可以使用@Order注解来指定顺序。
@EventListener
@Order(42)
public void processBlackListEvent(BlackListEvent event) {
    // notify appropriate parties via notificationAddress...
}

64,泛型事件
你可以使用泛型来定义事件的结构。例如:EntityCreatedEvent<T>中的T,就是一个实际的被创建的Entity。你可以创建像下面的listener定义,它只接收EntityCreatedEvent<Person>类型的事件。
@EventListener
public void onPersonCreated(EntityCreatedEvent<Person> event) {
    ...
}
Due to type erasure, this will only work if the event that is fired resolves the generic parameter(s) on which the event listener filters on (that is something like class PersonCreatedEvent extends EntityCreatedEvent<Person> { …​ }).(不理解意思,不会翻译了。。。)


在某个环境下,如果所有事件都是相同的结构的话,会变的很乏味(就像上面的例子)。在这样的情况里,你可以实现ResolvableTypeProvider来引导framework,而不使用运行环境提供的。(直译的,不明白什么意思)
public class EntityCreatedEvent<T>
        extends ApplicationEvent implements ResolvableTypeProvider {

    public EntityCreatedEvent(T entity) {
        super(entity);
    }

    @Override
    public ResolvableType getResolvableType() {
        return ResolvableType.forClassWithGenerics(getClass(),
                ResolvableType.forInstance(getSource()));
    }
}
注意:上面的方法不仅适用于ApplicationEvent,而是任意对象(你想要发送的对象)


65,对于low-level resource的方便访问
要了解Application Context的最优使用方法,用户需要熟悉Resource抽象。
一个Application Context就是一个ResourceLoader,ResourceLoader可以用来加载Resource。一个Resource就是一个有更多功能的JDK java.net.URL类。Resource的实现,其实就是包装了java.net.URL的一个实例。一个Resource可以以透明的方式,获得low-level的Resource,从任何地方(包括:类路径,文件,任何可以以标准URL或它的子类来描述的地方)。如果Resource位置字符串是一个简单的路径(不包含任何特殊前缀),对于application context类型来说,这些resource的位置就是清晰的而且是合适的。
你可以把一个实现了特殊的回调接口(ResourceLoaderAware)的Bean,注册到容器里。这些回调方法会被自动调用,在初始化的时候,Application Context会把它自己作为一个ResourceLoader传到回调方法里。你也可以公开一些Resource类型的属性,这些属性被用来去访问静态资源。这些属性可以像其它属性一样被注入。你可以把那些Resource类型的属性的值,指定为简单的字符串路径,然后依靠一个特殊的JavaBean PropertyEditor(它会被容器自动注册),来把字符串转换成Resource对象,在Bean被发布的时候。
位置路径(location path)或作为ApplicationContext构造函数的参数的路径实际上是resource字符串,并且根据具体的Context实现可以做不同的应对。例如:ClassPathXmlApplicationContext把一个简单的位置路径,作为一个classpath位置。你可以把位置路径(resource字符串)加上前缀,强制从classpath或URL来加载,不管实际的Context类型(就像例子中Context类型是ClassPathXmlApplicationContext类型,但你可以加前缀,强制从URL加载)


66,对于web应用的方便的ApplicationContext实例化方式
你可以创建ApplicationContext实现,通过使用ContextLoader。当然,你可以以编程式的方式来创建ApplicationContext实现。你可以注册一个ApplicationContext,使用ContextLoaderListener:
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/daoContext.xml /WEB-INF/applicationContext.xml</param-value>
</context-param>

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
Listener查找名为contextConfigLocation的参数,如果参数不存在,就使用/WEB-INF/applicationContext.xml作为默认值;如果参数存在,listener会用预定义的分隔符(逗号,分号,空格)来分割参数的字符串值,然后使用分割后的值,作为位置让application context进行搜索。Ant样式的路径支持,例如:“/WEB-INF/*Context.xml”表示所有文件名以Context.html结尾的文件;“/WEB-INF/**/*Context.xml”则表示“WEB-INF”文件夹下的所有子目录中的,文件名以Context.html结尾的文件。


67,把Spring ApplicationContext发布成为JavaEE RAR 文件
可以把Spring ApplicationContext发布成为一个RAR文件。这个RAR文件把context和所有需要的Bean类和library JARs压缩到Java EE RAR的deployment unit中。这相当于启动一个独立的ApplicationContext容器,这个容器位于JavaEE环境中,能够访问一些JavaEE Server的功能。相比headless WAR文件,RAR发布方式是一个更加自然的替代方式。WAR文件没有任何HTTP entry point(在JavaEE环境里,能够被用来启动Spring ApplicationContext的point)

RAR发布方式更为理想,在application context不需要HTTP entry points,而是需要message endpoints和scheduled jobs时候。在这样的context里的Bean,可以使用application server resource(例如:JTA transaction manager and JNDI-bound JDBC DataSources and JMS ConnectionFactory实例),也可以注册平台的JMX Server。这一切都通过Spring标准Transaction Management和JNDI和JMX支持功能。应用的compoent也可以同application server的JCA WorkManager交互,通过Spring的TaskExecutor抽象。关于RAR发布的细节,可以看SpringContextResourceAdapter的JavaDoc。

一个简单的把Spring ApplicationContext发布为RAR文件过程:把所有类打包到RAR文件中(RAR文件就是一个标准JAR文件,再加上一些文件扩展)。把所有需要的library JAR添加到RAR文件的根目录下。添加一个“META-INF/ra.xml”发布标识符(可以SpringContextResourceAdapter文档中找到)和相应的Spring XML Bean定义(一般做法:META-INF/applicationContext.xml),并且删除掉在你的应用服务器的发布文件夹内的生成的RAR文件。

注意:这样的RAR deployment unit通常是自包含的。它们不会把Components暴露给外面,更不会给相同应用的其它模块。和基于RAR的ApplicationContext进行的交互,通常通过JMS destination(它会和其它模块进行交互)。基于RAR的ApplicationContext也会安排(schedule)一些job(处理文件系统里的新文件,等功能的JOB)。如果需要允许从外部来的同步访问,它可以导出RMI endpoint,这些endpoint可以被相同机器上的其它应用模块使用。


68,BeanFactory
BeanFactory为Spring的IoC功能提供了基础,但它只是被用来和第三方框架整合,而且对于Spring的用户来说也很有历史性。BeanFactory和相关的接口(BeanFactoryAware, InitializingBean, DisposableBean)仍然被使用,用来向后兼容很多和Spring进行整合的第三方框架。因为很多第三方框架为了保持兼容JDK1.4,而避免依赖于JSR-250,所以无法使用新的类似功能(@PostConstruct or @PreDestroy)。

这个章节提供了一个背景介绍,关于BeanFactory和ApplicationContext之间的不同。并且还介绍如何通过一个singleton lookup,访问IoC容器。


69,BeanFactory还是ApplicationContext
除非你有很好的理由,否则请使用ApplicationContext。因为ApplicationContext了BeanFactory的所有功能。一般都推荐使用ApplicationContext,除非有一个特殊情况,例如:在一个嵌入式应用运行在资源非常紧张的设备上。然而,对于大部分的企业级应用来说,你应该使用ApplicationContext。Spring大量使用后置处理器进行扩展(来影响代理等等)。如果你只使用BeanFactory,一大部分功能(例如:事务和AOP)都无法使用。这种情况可能会使你非常困惑,因为配置上没有任何错误。

相比ApplicationContext,BeanFactory没有功能如下:
 - 自动注册BeanPostProcessor类型的后置处理器
 - 自动注册BeanFactoryPostProcessor类型的后置处理器
 - 方便地访问MessageSource
 - 发布ApplicationEvent
下面就是一个BeanFactory,显示地注册BeanPostProcessor类型的后置处理器的过程:
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
// populate the factory with bean definitions

// now register any needed BeanPostProcessor instances
MyBeanPostProcessor postProcessor = new MyBeanPostProcessor();
factory.addBeanPostProcessor(postProcessor);
// now start using the factory


下面就是一个BeanFactory,显示地注册BeanFactoryPostProcessor 类型的后置处理器的过程:
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinitions(new FileSystemResource("beans.xml"));

// bring in some property values from a Properties file
PropertyPlaceholderConfigurer cfg = new PropertyPlaceholderConfigurer();
cfg.setLocation(new FileSystemResource("jdbc.properties"));

// now actually do the replacement
cfg.postProcessBeanFactory(factory);

上面两个例子的显示声明,写起来都非常不方便(由其是当使用BeanFactoryPostProcessors and BeanPostProcessor类型的后置处理器的时候),这就是推荐使用ApplicationContext的原因之一。BeanFactoryPostProcessors and BeanPostProcessor机制实现了非常重要的功能,例如:property placeholder replacement和AOP

70,




其它:

1,每个Spring容器接口或Bean接口执行的时间点如下:

http://mislay.iteye.com/blog/364698


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1. 简介 1.1. 概览 1.2. 使用场景 2. Spring 2.0 的新特性 2.1. 简介 2.2. 控制反转(IoC)容器 2.2.1. 更简单的XML配置 2.2.2. 新的bean作用域 2.2.3. 可扩展的XML编写 2.3. 面向切面编程(AOP) 2.3.1. 更加简单的AOP XML配置 2.3.2. 对@AspectJ 切面的支持 2.4. 间层 2.4.1. 在XML里更为简单的声明性事务配置 2.4.2. JPA 2.4.3. 异步的JMS 2.4.4. JDBC 2.5. Web层 2.5.1. Spring MVC的表单标签库 2.5.2. Spring MVC合理的默认值 2.5.3. Portlet 框架 2.6. 其他特性 2.6.1. 动态语言支持 2.6.2. JMX 2.6 .3. 任务规划 2.6.4. 对Java 5(Tiger)的支持 2.7. 移植到Spring 2.0 2.7.1. 一些变化 2.7.1.1. Jar包 2.7.1.2. XML配置 2.7.1.3. Deprecated的类和方法 2.7.1.4. Apache OJB 2.7.1.5. iBatis 2.8. 更新的样例应用 2.9. 改进的文档 I. 核心技术 3. 控制反转容器 3.1. 简介 3.2. 容器和bean的基本原理 3.2.1. 容器 3.2.1.1. 配置元数据 3.2.2. 实例化容器 3.2.2.1. 组成基于XML配置元数据 3.2.3. 多种bean 3.2.3.1. 命名bean 3.2.3.2. 实例化bean 3.2.4. 使用容器 3.3. 依赖 3.3.1. 注入依赖 3.3.1.1. Setter注入 3.3.1.2. 构造器注入 3.3.1.3. 一些例子 3.3.2. 构造器参数的解析 3.3.2.1. 构造器参数类型匹配 3.3.2.2. 构造器参数的索引 3.3.3. bean属性及构造器参数详解 3.3.3.1. 直接量(基本类型、Strings类型等。) 3.3.3.2. 引用其它的bean(协作者) 3.3.3.3. 内部bean 3.3.3.4. 集合 3.3.3.5. Nulls 3.3.3.6. XML-based configuration metadata shortcuts 3.3.3.7. 组合属性名称 3.3.4. 使用depends-on 3.3.5. 延迟初始化bean 3.3.6. 自动装配(autowire)协作者 3.3.6.1. 设置Bean使自动装配失效 3.3.7. 依赖检查 3.3.8. 方法注入 3.3.8.1. Lookup方法注入 3.3.8.2. 自定义方法的替代方案 3.4. bean的作用域 3.4.1. Singleton作用域 3.4.2. Prototype作用域 3.4.3. 其他作用域 3.4.3.1. 初始化web配置 3.4.3.2. Request作用域 3.4.3.3. Session作用域 3.4.3.4. global session作用域 3.4.3.5. 作用域bean与依赖 3.4.4. 自定义作用域 3.5. 定制bean特性 3.5.1. Lifecycle接口 3.5.1.1. 初始化回调 3.5.1.2. 析构回调 3.5.2. 了解自己 3.5.2.1. BeanFactoryAware 3.5.2.2. BeanNameAware 3.6. bean定义的继承 3.7. 容器扩展点 3.7.1. 用BeanPostProcessor定制bean 3.7.1.1. 使用BeanPostProcessor的Hello World示例 3.7.1.2. RequiredAnnotationBeanPostProcessor示例 3.7.2. 用BeanFactoryPostProcessor定制配置元数据 3.7.2.1. PropertyPlaceholderConfigurer示例 3.7.2.2. PropertyOverrideConfigurer示例 3.7.3. 使用FactoryBean定制实例化逻辑 3.8. ApplicationContext 3.8.1. 利用MessageSource实现国际化 3.8.2. 事件 3.8.3. 底层资源的访问 3.8.4. ApplicationContext在WEB应用的实例化 3.9. 粘合代码和可怕的singleton 3.9.1. 使用Singleton-helper类 4. 资源 4.1. 简介 4.2. Resource 接口 4.3. 内置 Resource 实现 4.3.1. UrlResource 4.3.2. ClassPathResource 4.3.3. FileSystemResource 4.3.4. ServletContextResource 4.3.5. InputStreamResource 4.3.6. ByteArrayResource 4.4. ResourceLoader 4.5. ResourceLoaderAware 接口 4.6. 把Resource作为属性来配置 4.7. Application context 和Resource 路径 4.7.1. 构造application context 4.7.1.1. 创建 ClassPathXmlApplicationContext 实例 - 简介 4.7.2. Application context构造器资源路径的通配符 4.7.2.1. Ant风格的pattern 4.7.2.2. classpath*: 前缀 4.7.2.3. 其他关于通配符的说明 4.7.3. FileSystemResource 提示 5. 校验,数据绑定,BeanWrapper,与属性编辑器 5.1. 简介 5.2. 使用Spring的Validator接口进行校验 5.3. 从错误代码到错误信息 5.4. Bean处理和BeanWrapper 5.4.1. 设置和获取属性值以及嵌套属性 5.4.2. 内建的PropertyEditor实现 5.4.2.1. 注册用户自定义的PropertyEditor 6. 使用Spring进行面向切面编程(AOP) 6.1. 简介 6.1.1. AOP概念 6.1.2. Spring AOP的功能和目标 6.1.3. Spring的AOP代理 6.2. @AspectJ支持 6.2.1. 启用@AspectJ支持 6.2.2. 声明一个切面 6.2.3. 声明一个切入点(pointcut) 6.2.3.1. 切入点指定者的支持 6.2.3.2. 合并切入点表达式 6.2.3.3. 共享常见的切入点(pointcut)定义 6.2.3.4. 示例 6.2.4. 声明通知 6.2.4.1. 前置通知(Before advice) 6.2.4.2. 返回后通知(After returning advice) 6.2.4.3. 抛出后通知(After throwing advice) 6.2.4.4. 后通知(After (finally) advice) 6.2.4.5. 环绕通知(Around Advice) 6.2.4.6. 通知参数(Advice parameters) 6.2.4.7. 通知(Advice)顺序 6.2.5. 引入(Introductions) 6.2.6. 切面实例化模型 6.2.7. 例子 6.3. Schema-based AOP support 6.3.1. 声明一个切面 6.3.2. 声明一个切入点 6.3.3. 声明通知 6.3.3.1. 通知(Advice) 6.3.3.2. 返回后通知(After returning advice) 6.3.3.3. 抛出异常后通知(After throwing advice) 6.3.3.4. 后通知(After (finally) advice) 6.3.3.5. 通知 6.3.3.6. 通知参数 6.3.3.7. 通知顺序 6.3.4. 引入 6.3.5. 切面实例化模型 6.3.6. Advisors 6.3.7. 例子 6.4. AOP声明风格的选择 6.4.1. Spring AOP还是完全用AspectJ? 6.4.2. Spring AOP使用@AspectJ还是XML? 6.5. 混合切面类型 6.6. 代理机制 6.7. 编程方式创建@AspectJ代理 6.8. 在Spring应用使用AspectJ 6.8.1. 在Spring使用AspectJ来为domain object进行依赖注入 6.8.1.1. @Configurable object的单元测试 6.8.1.2. 多application context情况下的处理 6.8.2. Spring其他的AspectJ切面 6.8.3. 使用Spring IoC来配置AspectJ的切面 6.8.4. 在Spring应用使用AspectJ Load-time weaving(LTW) 6.9. 其它资源 7. Spring AOP APIs 7.1. 简介 7.2. Spring的切入点API 7.2.1. 概念 7.2.2. 切入点实施 7.2.3. AspectJ切入点表达式 7.2.4. 便利的切入点实现 7.2.4.1. 静态切入点 7.2.4.2. 动态切入点 7.2.5. 切入点的基类 7.2.6. 自定义切入点 7.3. Spring的通知API 7.3.1. 通知的生命周期 7.3.2. Spring里的通知类型 7.3.2.1. 拦截around通知 7.3.2.2. 前置通知 7.3.2.3. 异常通知 7.3.2.4. 后置通知 7.3.2.5. 引入通知 7.4. Spring里的advisor(Advisor) API 7.5. 使用ProxyFactoryBean创建AOP代理 7.5.1. 基础 7.5.2. JavaBean属性 7.5.3. 基于JDK和CGLIB的代理 7.5.4. 对接口进行代理 7.5.5. 对类进行代理 7.5.6. 使用“全局”advisor 7.6. 简化代理定义 7.7. 使用ProxyFactory通过编程创建AOP代理 7.8. 操作被通知对象 7.9. 使用“自动代理(autoproxy)”功能 7.9.1. 自动代理bean定义 7.9.1.1. BeanNameAutoProxyCreator 7.9.1.2. DefaultAdvisorAutoProxyCreator 7.9.1.3. AbstractAdvisorAutoProxyCreator 7.9.2. 使用元数据驱动的自动代理 7.10. 使用TargetSources 7.10.1. 热交换目标源 7.10.2. 池化目标源 7.10.3. 原型目标源 7.10.4. ThreadLocal目标源 7.11. 定义新的通知类型 7.12. 更多资源 8. 测试 8.1. 简介 8.2. 单元测试 8.3. 集成测试 8.3.1. Context管理和缓存 8.3.2. 测试fixture的依赖注入 8.3.3. 事务管理 8.3.4. 方便的变量 8.3.5. 示例 8.3.6. 运行集成测试 8.4. 更多资源 II. 间层数据访问 9. 事务管理 9.1. 简介 9.2. 动机 9.3. 关键抽象 9.4. 使用资源同步的事务 9.4.1. 高层次方案 9.4.2. 低层次方案 9.4.3. TransactionAwareDataSourceProxy 9.5. 声明式事务管理 9.5.1. 理解Spring的声明式事务管理实现 9.5.2. 第一个例子 9.5.3. 回滚 9.5.4. 为不同的bean配置不同的事务语义 9.5.5. <tx:advice/> 有关的设置 9.5.6. 使用 @Transactional 9.5.6.1. @Transactional 有关的设置 9.5.7. 插入事务操作 9.5.8. 结合AspectJ使用 @Transactional 9.6. 编程式事务管理 9.6.1. 使用 TransactionTemplate 9.6.2. 使用 PlatformTransactionManager 9.7. 选择编程式事务管理还是声明式事务管理 9.8. 与特定应用服务器集成 9.8.1. BEA WebLogic 9.8.2. IBM WebSphere 9.9. 公共问题的解决方案 9.9.1. 对一个特定的 DataSource 使用错误的事务管理器 9.10. 更多的资源 10. DAO支持 10.1. 简介 10.2. 一致的异常层次 10.3. 一致的DAO支持抽象类 11. 使用JDBC进行数据访问 11.1. 简介 11.1.1. Spring JDBC包结构 11.2. 利用JDBC核心类实现JDBC的基本操作和错误处理 11.2.1. JdbcTemplate类 11.2.2. NamedParameterJdbcTemplate类 11.2.3. SimpleJdbcTemplate类 11.2.4. DataSource接口 11.2.5. SQLExceptionTranslator接口 11.2.6. 执行SQL语句 11.2.7. 执行查询 11.2.8. 更新数据库 11.3. 控制数据库连接 11.3.1. DataSourceUtils类 11.3.2. SmartDataSource接口 11.3.3. AbstractDataSource类 11.3.4. SingleConnectionDataSource类 11.3.5. DriverManagerDataSource类 11.3.6. TransactionAwareDataSourceProxy类 11.3.7. DataSourceTransactionManager类 11.4. 用Java对象来表达JDBC操作 11.4.1. SqlQuery类 11.4.2. MappingSqlQuery类 11.4.3. SqlUpdate类 11.4.4. StoredProcedure类 11.4.5. SqlFunction类 12. 使用ORM工具进行数据访问 12.1. 简介 12.2. Hibernate 12.2.1. 资源管理 12.2.2. 在Spring的application context创建 SessionFactory 12.2.3. HibernateTemplate 12.2.4. 不使用回调的基于Spring的DAO实现 12.2.5. 基于Hibernate3的原生API实现DAO 12.2.6. 编程式的事务划分 12.2.7. 声明式的事务划分 12.2.8. 事务管理策略 12.2.9. 容器资源 vs 本地资源 12.2.10. 在应用服务器使用Hibernate的注意点 12.3. JDO 12.3.1. 建立PersistenceManagerFactory 12.3.2. JdoTemplate和JdoDaoSupport 12.3.3. 基于原生的JDO API实现DAO 12.3.4. 事务管理 12.3.5. JdoDialect 12.4. Oracle TopLink 12.4.1. SessionFactory 抽象层 12.4.2. TopLinkTemplate 和 TopLinkDaoSupport 12.4.3. 基于原生的TopLink API的DAO实现 12.4.4. 事务管理 12.5. iBATIS SQL Maps 12.5.1. iBATIS 1.x和2.x的概览与区别 12.5.2. iBATIS SQL Maps 1.x 12.5.2.1. 创建SqlMap 12.5.2.2. 使用 SqlMapTemplate 和 SqlMapDaoSupport 12.5.3. iBATIS SQL Maps 2.x 12.5.3.1. 创建SqlMapClient 12.5.3.2. 使用 SqlMapClientTemplate 和 SqlMapClientDaoSupport 12.5.3.3. 基于原生的iBATIS API的DAO实现 12.6. JPA 12.6.1. 在Spring环境建立JPA 12.6.1.1. LocalEntityManagerFactoryBean 12.6.1.2. LocalContainerEntityManagerFactoryBean 12.6.1.3. 处理多个持久化单元 12.6.2. JpaTemplate 和 JpaDaoSupport 12.6.3. 基于原生的JPA实现DAO 12.6.4. 异常转化 12.6.5. 事务管理 12.6.6. JpaDialect III. Web 13. Web框架 13.1. 介绍 13.1.1. 与其他web框架的集成 13.1.2. Spring Web MVC框架的特点 13.2. DispatcherServlet 13.3. 控制器 13.3.1. AbstractController 和 WebContentGenerator 13.3.2. 其它的简单控制器 13.3.3. MultiActionController 13.3.4. 命令控制器 13.4. 处理器映射(handler mapping) 13.4.1. BeanNameUrlHandlerMapping 13.4.2. SimpleUrlHandlerMapping 13.4.3. 拦截器(HandlerInterceptor) 13.5. 视图与视图解析 13.5.1. 视图解析器 13.5.2. 视图解析链 13.5.3. 重定向(Rediret)到另一个视图 13.5.3.1. RedirectView 13.5.3.2. redirect:前缀 13.5.3.3. forward:前缀 13.6. 本地化解析器 13.6.1. AcceptHeaderLocaleResolver 13.6.2. CookieLocaleResolver 13.6.3. SessionLocaleResolver 13.6.4. LocaleChangeInterceptor 13.7. 使用主题 13.7.1. 简介 13.7.2. 如何定义主题 13.7.3. 主题解析器 13.8. Spring对分段文件上传(multipart file upload)的支持 13.8.1. 介绍 13.8.2. 使用MultipartResolver 13.8.3. 在表单处理分段文件上传 13.9. 使用Spring的表单标签库 13.9.1. 配置标签库 13.9.2. form标签 13.9.3. input标签 13.9.4. checkbox标签 13.9.5. radiobutton标签 13.9.6. password标签 13.9.7. select标签 13.9.8. option标签 13.9.9. options标签 13.9.10. textarea标签 13.9.11. hidden标签 13.9.12. errors标签 13.10. 处理异常 13.11. 惯例优先原则(convention over configuration) 13.11.1. 对控制器的支持: ControllerClassNameHandlerMapping 13.11.2. 对模型的支持:ModelMap (ModelAndView) 13.11.3. 对视图的支持: RequestToViewNameTranslator 13.12. 其它资源 14. 集成视图技术 14.1. 简介 14.2. JSP和JSTL 14.2.1. 视图解析器 14.2.2. 'Plain-old' JSPs versus JSTL 'Plain-old' JSP与JSTL 14.2.3. 帮助简化开发的额外的标签 14.3. Tiles 14.3.1. 需要的资源 14.3.2. 如何集成Tiles 14.3.2.1. InternalResourceViewResolver 14.3.2.2. ResourceBundleViewResolver 14.4. Velocity和FreeMarker 14.4.1. 需要的资源 14.4.2. Context 配置 14.4.3. 创建模板 14.4.4. 高级配置 14.4.4.1. velocity.properties 14.4.4.2. FreeMarker 14.4.5. 绑定支持和表单处理 14.4.5.1. 用于绑定的宏 14.4.5.2. 简单绑定 14.4.5.3. 表单输入生成宏 14.4.5.4. 重载HTML转码行为并使你的标签符合XHTML 14.5. XSLT 14.5.1. 写在段首 14.5.1.1. Bean 定义 14.5.1.2. 标准MVC控制器代码 14.5.1.3. 把模型数据转化为XML 14.5.1.4. 定义视图属性 14.5.1.5. 文档转换 14.5.2. 小结 14.6. 文档视图(PDF/Excel) 14.6.1. 简介 14.6.2. 配置和安装 14.6.2.1. 文档视图定义 14.6.2.2. Controller 代码 14.6.2.3. Excel视图子类 14.6.2.4. PDF视图子类 14.7. JasperReports 14.7.1. 依赖的资源 14.7.2. 配置 14.7.2.1. 配置ViewResolver 14.7.2.2. 配置View 14.7.2.3. 关于报表文件 14.7.2.4. 使用 JasperReportsMultiFormatView 14.7.3. 构造ModelAndView 14.7.4. 使用子报表 14.7.4.1. 配置子报表文件 14.7.4.2. 配置子报表数据源 14.7.5. 配置Exporter的参数 15. 集成其它Web框架 15.1. 简介 15.2. 通用配置 15.3. JavaServer Faces 15.3.1. DelegatingVariableResolver 15.3.2. FacesContextUtils 15.4. Struts 15.4.1. ContextLoaderPlugin 15.4.1.1. DelegatingRequestProcessor 15.4.1.2. DelegatingActionProxy 15.4.2. ActionSupport 类 15.5. Tapestry 15.5.1. 注入 Spring 托管的 beans 15.5.1.1. 将 Spring Beans 注入到 Tapestry 页面 15.5.1.2. 组件定义文件 15.5.1.3. 添加抽象访问方法 15.5.1.4. 将 Spring Beans 注入到 Tapestry 页面 - Tapestry 4.0+ 风格 15.6. WebWork 15.7. 更多资源 16. Portlet MVC框架 16.1. 介绍 16.1.1. 控制器 - MVC的C 16.1.2. 视图 - MVC的V 16.1.3. Web作用范围的Bean 16.2. DispatcherPortlet 16.3. ViewRendererServlet 16.4. 控制器 16.4.1. AbstractController和PortletContentGenerator 16.4.2. 其它简单的控制器 16.4.3. Command控制器 16.4.4. PortletWrappingController 16.5. 处理器映射 16.5.1. PortletModeHandlerMapping 16.5.2. ParameterHandlerMapping 16.5.3. PortletModeParameterHandlerMapping 16.5.4. 增加 HandlerInterceptor 16.5.5. HandlerInterceptorAdapter 16.5.6. ParameterMappingInterceptor 16.6. 视图和它们的解析 16.7. Multipart文件上传支持 16.7.1. 使用PortletMultipartResolver 16.7.2. 处理表单里的文件上传 16.8. 异常处理 16.9. Portlet应用的部署 IV. 整合 17. 使用Spring进行远程访问与Web服务 17.1. 简介 17.2. 使用RMI暴露服务 17.2.1. 使用 RmiServiceExporter 暴露服务 17.2.2. 在客户端链接服务 17.3. 使用Hessian或者Burlap通过HTTP远程调用服务 17.3.1. 为Hessian配置DispatcherServlet 17.3.2. 使用HessianServiceExporter暴露你的bean 17.3.3. 客户端连接服务 17.3.4. 使用Burlap 17.3.5. 对通过Hessian或Burlap暴露的服务使用HTTP基础认证 17.4. 使用HTTP调用器暴露服务 17.4.1. 暴露服务对象 17.4.2. 在客户端连接服务 17.5. Web服务 17.5.1. 使用JAXI-RPC暴露服务 17.5.2. 访问Web服务 17.5.3. 注册bean映射 17.5.4. 注册自己的处理方法 17.5.5. 使用XFire来暴露Web服务 17.6. 对远程接口不提供自动探测 17.7. 在选择这些技术时的一些考虑 18. Enterprise Java Bean(EJB)集成 18.1. 简介 18.2. 访问EJB 18.2.1. 概念 18.2.2. 访问本地的无状态Session Bean(SLSB) 18.2.3. 访问远程SLSB 18.3. 使用Spring提供的辅助类实现EJB组件 19. JMS 19.1. 简介 19.2. 使用Spring JMS 19.2.1. JmsTemplate 19.2.2. 连接工厂 19.2.3. (消息)目的地管理 19.2.4. 消息侦听容器 19.2.4.1. SimpleMessageListenerContainer 19.2.4.2. DefaultMessageListenerContainer 19.2.4.3. ServerSessionMessageListenerContainer 19.2.5. 事务管理 19.3. 发送一条消息 19.3.1. 使用消息转换器 19.3.2. SessionCallback 和ProducerCallback 19.4. 接收消息 19.4.1. 同步接收 19.4.2. 异步接收 - 消息驱动的POJOs 19.4.3. SessionAwareMessageListener 接口 19.4.4. MessageListenerAdapter 19.4.5. 事务的多方参与 20. JMX 20.1. 介绍 20.2. 输出bean到JMX 20.2.1. 创建一个MBeanServer 20.2.2. 复用现有的MBeanServer 20.2.3. MBean的惰性初始化 20.2.4. MBean的自动注册 20.2.5. 控制注册行为 20.3. 控制bean的管理接口 20.3.1. MBeanInfoAssembler 接口 20.3.2. 使用源码级元数据 20.3.3. 使用JDK 5.0注解 20.3.4. 源代码级的元数据类型 20.3.5. 接口AutodetectCapableMBeanInfoAssembler 20.3.6. 用Java接口定义管理接口 20.3.7. 使用MethodNameBasedMBeanInfoAssembler 20.4. 控制bean的 ObjectName 20.4.1. 从Properties读取ObjectName 20.4.2. 使用 MetadataNamingStrategy 20.5. JSR-160连接器 20.5.1. 服务器端连接器 20.5.2. 客户端连接器 20.5.3. 基于Burlap/Hessian/SOAP的JMX 20.6. 通过代理访问MBeans 20.7. 通知 20.7.1. 为通知注册监听器 20.7.2. 发布通知 20.8. 更多资源 21. JCA CCI 21.1. 介绍 21.2. 配置CCI 21.2.1. 连接器配置 21.2.2. 在Spring配置ConnectionFactory 21.2.3. 配置CCI连接 21.2.4. 使用一个 CCI 单连接 21.3. 使用Spring的 CCI访问支持 21.3.1. 记录转换 21.3.2. CciTemplate 类 21.3.3. DAO支持 21.3.4. 自动输出记录生成 21.3.5. 总结 21.3.6. 直接使用一个 CCI Connection 接口和Interaction接口 21.3.7. CciTemplate 使用示例 21.4. 建模CCI访问为操作对象 21.4.1. MappingRecordOperation 21.4.2. MappingCommAreaOperation 21.4.3. 自动输出记录生成 21.4.4. 总结 21.4.5. MappingRecordOperation 使用示例 21.4.6. MappingCommAreaOperation 使用示例 21.5. 事务 22. Spring邮件抽象层 22.1. 简介 22.2. Spring邮件抽象结构 22.3. 使用Spring邮件抽象 22.3.1. 可插拔的MailSender实现 22.4. 使用 JavaMail MimeMessageHelper 22.4.1. 创建一条简单的MimeMessage,并且发送出去 22.4.2. 发送附件和嵌入式资源(inline resources) 23. Spring的定时调度(Scheduling)和线程池(Thread Pooling) 23.1. 简介 23.2. 使用OpenSymphony Quartz 调度器 23.2.1. 使用JobDetailBean 23.2.2. 使用 MethodInvokingJobDetailFactoryBean 23.2.3. 使用triggers和SchedulerFactoryBean来包装任务 23.3. 使用JDK Timer支持类 23.3.1. 创建定制的timers 23.3.2. 使用 MethodInvokingTimerTaskFactoryBean类 23.3.3. 打包:使用TimerFactoryBean来设置任务 23.4. SpringTaskExecutor抽象 23.4.1. TaskExecutor接口 23.4.2. 何时使用TaskExecutor接口 23.4.3. TaskExecutor类型 23.4.4. 使用TaskExecutor接口 24. 动态语言支持 24.1. 介绍 24.2. 第一个例子 24.3. 定义动态语言支持的bean 24.3.1. 公共概念 24.3.1.1. <lang:language/> 元素 24.3.1.2. Refreshable bean 24.3.1.3. 内置动态语言源文件 24.3.1.4. 理解dynamic-language-backed bean context的构造器注入 24.3.2. JRuby beans 24.3.3. Groovy beans 24.3.4. BeanShell beans 24.4. 场景 24.4.1. Spring MVC控制器脚本化 24.4.2. Validator脚本化 24.5. 更多的资源 25. 注解和源代码级的元数据支持 25.1. 简介 25.2. Spring的元数据支持 25.3. 注解 25.3.1. @Required 25.3.2. Spring的其它@Annotations 25.4. 集成Jakarta Commons Attributes 25.5. 元数据和Spring AOP自动代理 25.5.1. 基本原理 25.5.2. 声明式事务管理 25.5.3. 缓冲 25.5.4. 自定义元数据 25.6. 使用属性来减少MVC web层配置 25.7. 元数据属性的其它用法 25.8. 增加对额外元数据API的支持 A. XML Schema-based configuration A.1. Introduction A.2. XML Schema-based configuration A.2.1. Referencing the schemas A.2.2. The util schema A.2.2.1. <util:constant/> A.2.2.2. <util:property-path/> A.2.2.3. <util:properties/> A.2.2.4. <util:list/> A.2.2.5. <util:map/> A.2.2.6. <util:set/> A.2.3. The jee schema A.2.3.1. <jee:jndi-lookup/> (simple) A.2.3.2. <jee:jndi-lookup/> (with single JNDI environment setting) A.2.3.3. <jee:jndi-lookup/> (with multiple JNDI environment settings) A.2.3.4. <jee:jndi-lookup/> (complex) A.2.3.5. <jee:local-slsb/> (simple) A.2.3.6. <jee:local-slsb/> (complex) A.2.3.7. <jee:remote-slsb/> A.2.4. The lang schema A.2.5. The tx (transaction) schema A.2.6. The aop schema A.2.7. The tool schema A.2.8. The beans schema A.3. Setting up your IDE A.3.1. Setting up Eclipse A.3.2. Setting up IntelliJ IDEA A.3.3. Integration issues A.3.3.1. XML parsing errors in the Resin v.3 application server B. Extensible XML authoring B.1. Introduction B.2. Authoring the schema B.3. Coding a NamespaceHandler B.4. Coding a BeanDefinitionParser B.5. Registering the handler and the schema B.5.1. META-INF/spring.handlers B.5.2. META-INF/spring.schemas C. spring-beans-2.0.dtd D. spring.tld D.1. Introduction D.2. The bind tag D.3. The escapeBody tag D.4. The hasBindErrors tag D.5. The htmlEscape tag D.6. The message tag D.7. The nestedPath tag D.8. The theme tag D.9. The transform tag E. spring-form.tld E.1. Introduction E.2. The checkbox tag E.3. The errors tag E.4. The form tag E.5. The hidden tag E.6. The input tag E.7. The label tag E.8. The option tag E.9. The options tag E.10. The password tag E.11. The radiobutton tag E.12. The select tag E.13. The textarea tag F. Spring 2.0 开发手册文化项目 F.1. 声明 F.2. 致谢 F.3. 参与人员及任务分配 F.4. Spring 2.0 正式版开发手册翻译说明 F.5. 项目历程 F.5.1. Spring 2.0 RC2 开发手册翻译项目 F.5.2. Spring 2.0 正式版开发手册翻译项目
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值