5

注意LifecycleProcessor接口继承了Lifcycle接口。同时,增加了2个方法,用于处理容器的refreshedclosed事件。

startupshutdown方法调用次序非常重要。若两个对象有依赖关系,依赖方会在依赖启动之后启动,会在依赖停止之前停止。然而,有时依赖并不直接。也许你仅知道某些类型对象优先于另外一种类型启动。此场景中,SmartLifecycle接口也许是个好主意,该接口有个方法getPhase(),此方法是其父接口Phased中的方法:

public interface Phased {

    int getPhase();

}
public interface SmartLifecycle extends Lifecycle, Phased {

    boolean isAutoStartup();

    void stop(Runnable callback);

}

启动时,最低层次的phase最先启动,停止时,该次序逆序执行。因此,若对象实现了SmartLifecycle接口,它的getPhase()方法返回Integer.MIN_VALUE,那么该对象最先启动,最后停止。若是返回了Integer.MAX_VALUE,那么该方法最后启动最先停止(因为该对象依赖其他bean才能运行)。关于phase的值,常规的并未实现SmartLifecycle接口的Lifecycle对象,其值默认为0。因此,负phase值表示要在常规Lifecycle对象之前启动(在常规Lifecycyle对象之后停止),使用 正值则恰恰相反。

如你所见,SmartLifecyclestop()方法有一个回调参数。所有的实现在关闭处理完成后会调用回调的run()方法。TODO 。它相当于开启了异步关闭功能,和LifecycleProcessor接口默认实现DefaultLifecycleProcessor类的异步,该类会为每个phase的回调等待超时。每个phase默认的超时是30秒。可以重写该类默认的实例,该类在容器内默认bean名称是lifecycleProcessor。如果你仅想修改超时,这么写就足够了。


    
    

As mentioned, the LifecycleProcessor interface defines callback methods for the refreshing and closing of the context as well. The latter will simply drive the shutdown process as if stop() had been called explicitly, but it will happen when the context is closing. The refresh callback on the other hand enables another feature of SmartLifecycle beans. When the context is refreshed (after all objects have been instantiated and initialized), that callback will be invoked, and at that point the default lifecycle processor will check the boolean value returned by each SmartLifecycle object’s isAutoStartup() method. If "true", then that object will be started at that point rather than waiting for an explicit invocation of the context’s or its own start() method (unlike the context refresh, the context start does not happen automatically for a standard context implementation). The "phase" value as well as any "depends-on" relationships will determine the startup order in the same way as described above.

TODO 书接前文,LifecycleProcessor接口也定义了容器的refreshingclosing事件。后者会驱动shutdown处理,就像是明确的调用了stop()方法,但是它是发生在容器关闭期间。refresh回调开启了SmartLifecyclebean的另一个功能 。当上下文环境刷新时(在所有的对象实例化和初始化之后),则会调用refresh回调,同时,默认的lifecycle processor检查每个SmartLifecycle对象的isAutoStartup()方法返回的布尔值。若为true,对象则会在那时启动,而不是等待容器显示调用之后或者是他自己的start()方法调用之后(这和容器刷新不同,标准的容器实现启动不会自动发生)。phase值和depends-on关系一样,都使用了相同的方法决定了的启动次序。

非web应用中安全的关闭Spring IoC容器


注意

本章适用于非web应用。基于Spring web的应用的ApplicationContext实现类,已经提供了支持,用于在应用关闭时安全的关闭Spring IoC容器。

在一个非web应用的环境中使用Spring IoC容器;比如,在一个富客户端桌面的环境中;得在JVM中注册一个shutdown钩子。这么做是为了安全的关闭,在关闭时保证所单例bean的相关的destroy方法会被调用,这样就可以释放所有的资源。当然了,你必须得正确的配置和实现销毁回调。

要注册shutdown钩子,得调用registerShutdownHood()方法,该方法在AbstractApplicationContext类中。

import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public final class Boot {

    public static void main(final String[] args) throws Exception {

        AbstractApplicationContext ctx = new ClassPathXmlApplicationContext(
                new String []{"beans.xml"});

        // add a shutdown hook for the above context...
        ctx.registerShutdownHook();

        // app runs here...

        // main method exits, hook is called prior to the app shutting down...

    }
}

ApplicationContextAware and BeanNameAware


org.springframework.context.ApplicationContextAware接口实现类的实例将会持有ApplicationContext的引用:

public interface ApplicationContextAware {

    void setApplicationContext(ApplicationContext applicationContext) throws BeansException;

}

因此可以编程式的使用ApplicationContext手动的创建bean,通过ApplicationContext接口或者是该接口的子类,比如ConfigurableApplicationContext,该类还增加了方法。用途之一是编程式的检索bean,有时非常有用。然而,大多数情况下,要避免编程式检索bean,这样的话你的代码就会和Spring耦合,这不是IoC的风格,Ioc的风格是协作类作为bean的属性。ApplicationContext类的其他方法提供了文件资源的访问接口、发布应用事件、访问MessageSource消息资源。这些附加的功能请参看Section 5.15, “Additional Capabilities of the ApplicationContext”

自Spring2.5起,可以使用自动装配获取ApplicationContext引用。传统的constructorbyType自动装配模式(详情参看 Section 5.4.5, “Autowiring collaborators”能为构造参数或者setter方法提供一个ApplicationContext类的依赖注入。为了更加灵活,还增加了自动注入的注解功能,它能自动注入属性和自动注入多参数方法。使用注解,ApplicationContext可以自动注入到ApplicationContext类型的属性、构造参数、方法参数。详情参看Section 5.9.2, “@Autowired”.

org.springframework.beans.factory.BeanNameAware接口的实现类,若是由ApplicationContext创建了该类的实例,该实例将会持有相关的对象定义的引用。

public interface BeanNameAware {

    void setBeanName(string name) throws BeansException;

}

The callback is invoked after population of normal bean properties but before an initialization callback such as InitializingBean afterPropertiesSet or a custom init-method.
TODO这个回调在设置属性之后调用,但是在initialization回调之前,比如InitializingBeanafterPropertiesSet或者 自定义的init-method

Other Aware interfaces


Besides ApplicationContextAware and BeanNameAware discussed above, Spring offers a range of Aware interfaces that allow beans to indicate to the container that they require a certain infrastructure dependency. The most important Aware interfaces are summarized below - as a general rule, the name is a good indication of the dependency type:

名称 注入依赖 详情
ApplicationContextAware ApplicationContext Section 5.6.2, “ApplicationContextAware and BeanNameAware”
ApplicationEventPublisherAware 发布事件 Section 5.15, “Additional Capabilities of the ApplicationContext”
BeanClassLoaderAware 加载bean的类加载器 Section 5.3.2, “Instantiating beans”
BeanFactoryAware 声明BeanFactory Section 5.6.2, “ApplicationContextAware and BeanNameAware”
BeanNameAware 生命bean 的名字 Section 5.6.2, “ApplicationContextAware and BeanNameAware”
BootstrapContextAware Resource adapter BootstrapContext the container runs in. Typically available only in JCA aware ApplicationContexts Chapter 26, JCA CCI
LoadTimeWeaverAware Defined weaver for processing class definition at load time Section 9.8.4, “Load-time weaving with AspectJ in the Spring Framework”
MessageSourceAware Configured strategy for resolving messages (with support for parametrization and internationalization) Section 5.15, “Additional Capabilities of the ApplicationContext”
NotificationPublisherAware Spring JMX notification publisher Section 25.7, “Notifications”
PortletConfigAware Current PortletConfig the container runs in. Valid only in a web-aware Spring ApplicationContext Chapter 20, Portlet MVC Framework
PortletContextAware Current PortletContext the container runs in. Valid only in a web-aware Spring ApplicationContext Chapter 20, Portlet MVC Framework
ResourceLoaderAware Configured loader for low-level access to resources Chapter 6, Resources
ServletConfigAware Current ServletConfig the container runs in. Valid only in a web-aware Spring ApplicationContext Chapter 17, Web MVC framework

除上面讨论过的ApplicationContextAwareBeanNameAware,Spring提供了一些了Aware接口,这些接口可以提供容器中相关的基础(SpringAPI)依赖。最重要的Aware接口参看下面的摘要,命名相当规范,看名字就能知道依赖类型:
Table 5.4. Aware interfaces

名称 注入依赖 详情
ApplicationContextAware ApplicationContext Section 5.6.2, “ApplicationContextAware and BeanNameAware”
ApplicationEventPublisherAware 发布事件 Section 5.15, “Additional Capabilities of the ApplicationContext”
BeanClassLoaderAware 加载bean的类加载器 Section 5.3.2, “Instantiating beans”
BeanFactoryAware 声明BeanFactory Section 5.6.2, “ApplicationContextAware and BeanNameAware”
BeanNameAware 生命bean 的名字 Section 5.6.2, “ApplicationContextAware and BeanNameAware”
BootstrapContextAware Resource adapter BootstrapContext the container runs in. Typically available only in JCA aware ApplicationContexts Chapter 26, JCA CCI
LoadTimeWeaverAware Defined weaver for processing class definition at load time Section 9.8.4, “Load-time weaving with AspectJ in the Spring Framework”
MessageSourceAware Configured strategy for resolving messages (with support for parametrization and internationalization) Section 5.15, “Additional Capabilities of the ApplicationContext”
NotificationPublisherAware Spring JMX notification publisher Section 25.7, “Notifications”
PortletConfigAware Current PortletConfig the container runs in. Valid only in a web-aware Spring ApplicationContext Chapter 20, Portlet MVC Framework
PortletContextAware Current PortletContext the container runs in. Valid only in a web-aware Spring ApplicationContext Chapter 20, Portlet MVC Framework
ResourceLoaderAware Configured loader for low-level access to resources Chapter 6, Resources
ServletConfigAware Current ServletConfig the container runs in. Valid only in a web-aware Spring ApplicationContext Chapter 17, Web MVC framework

注意,这些接口的用法使代码与Spring API耦合,这不符合IoC风格。同样,除非有需求的基础bean才使用编程式访问容器。

Spring Bean的继承


Spring bean定义包含各种配置信息,包括构造参数,属性值,容器特定信息例如初始化方法、静态工厂方法等等。Spring子bean定义继承父bean定义配置。子bean能覆盖值,若有需要还能增加其他配置。使用继承能少打好多字。这是模板的一种形式,讲究的就是效率。

编程式的方式使用ApplicationContext场景,子bean的定义代表ChildBeanDefinition类。大多数用户不需要使用如此底层的SpringAPI,通常是使用类似ClassPathXmlApplicationContext的bean声明。若用XML配置,通过parent属性表示子bean定义,指定父bean的标识作为parent属性值。


    
    



    
    

若子bean中未指定class属性,则子bean集成父bean的class属性,子bean可以重写覆盖此属性。若要覆盖重写class属性,子bean的class类型必须兼容父bean的class,也就是,子bean必须能接收父bean的属性值。

其他的属性也是通常取自子bean的配置:depends on, autowire mode, dependency check, singleton, lazy init.

前面样例中,使用abstract属性指定了父bean为抽象定义。如父bean中未指定class,则必须指定父bean为抽象bean。看代码:


    
    



    
    

上述的父bean不能实例化,因为她不完整,是抽象的bean,作为子bean的纯模板时,它是非常有用的。试试通过属性引用或者使用getBean()方法调用该bean,会抛错。容器内部的preInstantiateSingletons()方法会忽略抽象bean。

注意

ApplicationContext类默认会预先实例化所有的单例bean。因此,如果有做模板用的父bean,父bean定义中指定了classs属性,则必须指定abstracttrue,这是非常重要的,否则容器会预先实例化该bean。

容器扩展点


通常开发者无需自己实现APplicationContext,而是使用插件扩展Spring IoC容器,插件是某些指定的集成接口的实现。下面记账讲解这些集成接口。

使用BeanPostProcessor自定义bean


BeanPostProcessor接口定义了实例化逻辑、依赖逻辑等回调方法,即可以自定义也可以覆盖容器默认方法。若果要在Spring容器完成实例化、配置、初始化bean之后执行自定义逻辑,则以插件方式实现BeanPostProcessor

可以配置多个BeanPostProcessor实例,可以设置BeanPostProcessorsorder属性来控制其执行次序。让BeanPostProcessor实现Ordered接口,就能设置次属性。如果使用自定义BeanPostProcessor,也得考虑实现Ordered接口。更多的细节,参阅BeanPostProcessorOrdered接口的javadocs。也可以查阅programmatic registration of BeanPostProcessors译注,SPring参考手册中这个链接确实没有

注意

NOTE
BeanPostProcessors操作bean的实例;也就是,Spring IoC容器实例化bean的实例时BeanPostProcessors开始运行。

BeanPostProcessors在各自容器内有效。当使用容器继承时,BeanPostProcessors缺不会继承。如果在某容器内定义了BeanPostProcessor,近在本容器中生效。或句话说,一个容器中的bean不会使用另一个容器内的BeanPostProcessor处理,继承的容器也不行。

要改变bean定义(也就是,bean定义的蓝图,译注蓝图应该是指各种配置元数据,比如xml、注解等),你得使用BeanFactorPostProcessor,详情参看in Section 5.8.2, “Customizing configuration metadata with a BeanFactoryPostProcessor”

org.springframework.beans.factory.config.BeanPostProcessor接口有2个回调方法组成。当这样的类在容器内注册为post-processor,容器创建所有bean,在容器初始化方法(比如InitializingBeanafterProperieSet()方法和其他所有的声明的init方法)和所有bean 初始化回调之前,运行post-processor回调。

ApplicationContext自动探测在配置元数据中定义的BeanPostProcessorApplicationContext注册这些bean为post-processors,这样就可以在bean创建之前调用。Bean的post-processors可以像其他bean那样部署到容器里。

注意,在configuration类中,使用@Bean工厂方法声明BeanPostProcessor,该工厂方法的返回类型必须是该实现类或者至少得是org.springframework.beans.factory.config.BeanPostProcessor接口,清楚的标识出post-processor。否则,ApplicationContext不会开启根据类型自动探测。因为BeanPostProcessor需要尽早的实例化,这样在容器中即可用于其他bean的初始化,因此这种尽早的类型探测至关重要。

注意
编程式注册BeanPostProcessor
尽管推荐的BeanPostProcessor的注册方式是通过ApplicationContext的自动探测机制,但是也可以使用ConfigurableBeanFactory类调用其addBeanPostProcessor实现编程式的注册。编程式注册是非常有用的,比如用于在注册之前实现等价的逻辑,再比如跨容器复制post processors。注意使用编程式注册BeanPostProcessors并不会遵守Ordered接口的次序。注册的顺序就是执行的次序。此外还得记得,编程式的注册BeanPostProcessors会在自动探测注册的BeanPostProcessors之前处理,无论自动探测注册的BeanPostProcessors指定了多么优先的次序。
注意
BeanPostProcessor和AOP的自动代理
Classes that implement the BeanPostProcessor interface are special and are treated differently by the container. All BeanPostProcessors and beans that they reference directly are instantiated on startup, as part of the special startup phase of the ApplicationContext. Next, all BeanPostProcessors are registered in a sorted fashion and applied to all further beans in the container. Because AOP auto-proxying is implemented as a BeanPostProcessor itself, neither BeanPostProcessors nor the beans they reference directly are eligible for auto-proxying, and thus do not have aspects woven into them.
容器会特殊对待BeanPostProcessor接口。所有的BeanPostProcessors及引用了BeanPostProcessors的bean会在启动时实例化,作为ApplicationContext特殊的启动阶段。接下来,所有的BeanPostProcessors都会按照次序注册到容器中,在其他bean使用BeanPostProcessors处理时也会使用此顺序。因为AOP的auto-proxying自动代理是BeanPostProcessor的默认实现,它既不引用BeanPostProcessors也不引用其他bean,不会发生auto-proxying自动代理,因此不会有切面织入。TODO

对于BeanPostProcessor类型的bean,会看到这样一条日志:"Bean foo is not eligible for getting processed by all BeanPostProcessor interfaces (for example: not eligible for auto-proxying)"
注意,如果有bean通过自动注入或者@Resource(可能会导致自动注入)注入到BeanPostProcessor,在使用类型匹配检索依赖bean时Spring也许会访问到不期望的bean,导致生成不合适的auto-proxying自动代理或者其他post-processing。举个栗子,如果使用@Resouce依赖注解,而且field/setter上注解的名字和bean中声明名字不一致时,Spring将会使用类型匹配访问其他bean。

下面 示例中讲解了在ApplicationContext中如何撰写、注册、使用BeanPostProcessors

栗子:Hello World,BeanPostProcessor风格
第一个示例,讲解基础用法。栗子展示了一个自定义BeanPostProcessor实现,功能是在容器创建bean时,调用每一个bean的toString()方法并输出到控制台。
上干活,fuck goods

package scripting;

import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.BeansException;

public class InstantiationTracingBeanPostProcessor implements BeanPostProcessor {

    // simply return the instantiated bean as-is
    public Object postProcessBeforeInitialization(Object bean,
            String beanName) throws BeansException {
        return bean; // we could potentially return any object reference here...
    }

    public Object postProcessAfterInitialization(Object bean,
            String beanName) throws BeansException {
        System.out.println("Bean '" + beanName + "' created : " + bean.toString());
        return bean;
    }

}



    
        
    

    
    


注意InstantiationTracingBeanPostProcessor是如何定义的。它甚至没有名字,因为它能像其他bean那样依赖注入。(上面的配置中,使用Groovy script创建了个bean。Spring动态语言支持的详细讲解参看Chapter 29, Dynamic language support

下面的java应用使用上面的配置和代码执行,

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.scripting.Messenger;

public final class Boot {

    public static void main(final String[] args) throws Exception {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("scripting/beans.xml");
        Messenger messenger = (Messenger) ctx.getBean("messenger");
        System.out.println(messenger);
    }

}

将会输出:
Bean messenger created : org.springframework.scripting.groovy.GroovyMessenger@272961
org.springframework.scripting.groovy.GroovyMessenger@272961

Example: The RequiredAnnotationBeanPostProcessor
对于扩展Spring IoC容器,使用回调函数或者注解联结一个自定义BeanPostProcessor实现类是常用的手段。例如Spring的RequiredAnnotationBeanPostProcessor,是个BeanPostProcessor实现类,spring内置,作用是确保Spring bean定义上的带注解的JavaBean属性确实被注入了值。

使用BeanFactoryPostProcessor自定义配置元数据


接下来的扩展点讲一讲org.springframework.beans.factory.config.BeanFactoryPostProcessor。此接口的语法和BeanPostProcessor类似,有一个主要的不同之处:BeanFactoryPostProcessor操作bean的配置元数据;也就是,Spring IoC容器允许BeanFactoryPostProcessor读取配置元数据并且在容器实例化bean之前可能修改配置。

可以配置多个BeanFactoryPostProcessors,通过设置order属性控制它们的执行次序。BeanFactoryPostProcessor若是实现了Ordered接口,则可设置该属性。若是自定义BeanFactorPostProcessor,同时得考虑实现Ordered接口。详情参阅BeanFactoryPostProcessor的javadocs。

注意

NOTE
如果要改变bean实例(根据配置元数据创建的对象),那么就需要使用BeanPostProcessor(上一章描述的in Section 5.8.1, “Customizing beans using a BeanPostProcessor”)。当使用BeanFactoryPostProcessor处理实例时(使用BeanFactory.getBean()方法),如此早的处理bean实例,违反了标准的容器生命周期。通过bean post processing也许会引起负面影响。
BeanFactoryPostProcessors的作用域也是在各自的容器内。如果使用容器继承,这一点也是应该注意的。如果在某容器内定义了BeanFactoryPostProcessor,则仅应用于本容器。某容器内的bean定义,不会使用另一个容器的BeanFactoryPostProcessors处理,容器之间有继承关系也不行。

为了让配置元数据的改变应用,声明在ApplicationContext内的bean工厂post-processor都是自动执行。Spring包含一系列的预先定义的bean工厂post-processors,比如PropertyOverrideConfigurerPropertyPlaceholderConfigurer。也可以使用自定义BeanFactoryPostProcessor,比如注册一个自定义属性编辑器。

ApplicationContext自动探测BeanFactoryPostProcessor接口的实现类。容器使用这些bean作为bean工厂post-processors。可以像其他bean那样将post-processor部署在容器内。

注意

NOTE
若使用BeanPostProcessors,通常不会给BeanFactoryPostProcessors配置延迟初始化。如果没有其他bean引用BeanFactoryPostProcessor,则post-processor根本不会实例化。因此设置延迟初始化将会被忽略,BeanFactoryPostProcessor将会及时实例化,甚至在元素设置了default-lazy-init属性为true也不行。

Example: the Class name substitution PropertyPlaceholderConfigurer

可以使用PropertyPlaceholderConfigurer将bean的属性值使用标准的Java Properties格式定义在一个单独的文件中。这样可以将应用的自定义环境配置属性隔离出来,比如数据库URLs和密码,这样就降低了修改容器内XML配置或者Java 代码的的复杂性和风险。

考虑下面的XML配置片段,使用了placeholder值定义了DataSource。样例展示了一个外部的Properties文件的属性配置。运行时,PropertyPlaceholderConfigurer会应用到配置元数据中,替换指定格式的placeholders,格式为${property-name},这样的格式与Ant/log4j/JSP EL风格相同。


    



    
    
    
    

在标准java Properties格式文件中实际的值:

jdbc.driverClassName=org.hsqldb.jdbcDriver
jdbc.url=jdbc:hsqldb:hsql://production:9002
jdbc.username=sa
jdbc.password=root

因此,字串${jdbc.username}在运行时赋值为sa,其他的${key}都会被替换为文件中与key对应的值。PropertyPlaceholderConfigurer检查bean定义中大多数的placeholders占位符,placeholder的前缀和后缀都是自定义的。

使用Spring2.5引入的上下文命名空间,就可以用一个专用配置元素配置属性placeholders占位符。可以指定多个locations,多个locations使用,逗号分割。


PropertyPlaceholderConfigurer不仅仅检索指定的Properties文件。默认情况,若是在指定的Properties配置文件中找不到指定的属性property,也会检查Java 的系统属性System properties。通过设置systemPropertiesMode属性的值,定义默认查找行为,该属性值有几个取值:

  • never:不检查系统属性
  • fallback:如果未在指定文件中解析出属性值,则检查系统属性。此项为默认行为。
  • override:先检查系统属性。系统属性会覆盖其他配置文件中的属性。

PropertyPlaceholderConfigurer更多详情参看javadocs

注意

TIP
可以使用PropertyPlaceholderConfigurer替换类名,有时,某些类在运行时才能确定,那么这将非常有用。


    
        classpath:com/foo/strategy.properties
    
    
        custom.strategy.class=com.foo.DefaultStrategy
    


若类在运行时期间不能解析为合法类,ApplicationContext创建非延迟初始化bean的preInstantiateSingletons()期间抛错误,

Example: the PropertyOverrideConfigurer

PropertyOverrideConfigurer,是另一个ben工厂的post-processor,类似于PropertyPlaceholderConfigurer,但是有不同之处,bean源定义可以设置默认值或者根本不设置值。若一个overriding Properties文件不包含某个bean属性,就使用默认的上下文定义。

注意bean定义并不知道它会被重写,所以使用了重写配置在XML配置中并不直观。如果有多个PropertyOverrideConfigurer实例为相同的bean属性配置了不同的值,最后一个实例配置生效。

Properties文件配置格式如下

beanName.property=value

举例:

dataSource.driverClassName=com.mysql.jdbc.Driver
dataSource.url=jdbc:mysql:mydb

上述文件中的配置,将会赋值给在容器中的定义的bean的相应属性 ,bean的名字是datasource,有driver属性和url属性

Compound property names are also supported, as long as every component of the path except the final property being overridden is already non-null (presumably initialized by the constructors). In this example…

同样支持复合属性,属性路径可以要多长有多长,但是属性不能为null(),看样例:

foo.fred.bob.sammy=123

bean foo有属性fred,fred有属性bobbob有属性sammysammy赋值为123

注意

Note
指定重写值都是字面值;不会解析为bean引用。就算是指定的值,在XML的bean定义中bean的名字,也不会解析为该引用,而是解析为字面值。

使用Spring 2.5中引入的上下文命名空间,可以为配置属性指定专用配置元素


使用FactoryBean自定义实例化逻辑


对象实现org.springframework.beans.factory.FactoryBean接口,则成为它本身的工厂。

FactoryBean接口是Spring IoC容器实例化逻辑的扩展点。假如初始化代码非常复杂,此时使用java编码比使用XML配置更容易表达。这种场景中,就可以自定义FactoryBean,在类中撰写复杂的初始化程序,并将其作为插件加入到容器中。

FactoryBean接口有3个方法:

  • Object getObject():返回本工厂创建的对象实例。此实例也许是共享的,依赖于该工厂返回的是单例或者是原型。
  • boolean isSingleton():如果FactoryBean返回的是单例,该方法返回值为true,否则为false
  • Class getObjectType():返回对象类型。对象类型是getObject()方法返回的对象的类型,如果不知道的类型则返回null。

FactoryBean概念和接口在Spring框架中大量使用。Spring内置的有超过50个实现。

当使用ApplicationContextgetBean()方法获取FactoryBean实例本身而不是它所产生的bean,则要使用&符号+id。比如,现有FactoryBean,它有id,在容器上调用getBean("myBean")将返回FactoryBean所产生的bean,调用getBean("&myBean")将返回FactoryBean它本身的实例。

基于注解的把配置元数据


注解比XML好么?

注解比XML好么,简单的说得看情况。详细的说,各有优缺点。因为定义的方式,注解在声明处提供了大量的
上下文信息,所以注解配置要更简洁。然而,XML擅长在不接触源码或者无需反编译的情况下组装组件。
虽然有这样的争议:注解类不再是`POJO`,并且配置更加分散难以控制,
但是还是有人更喜欢在源码上使用注解配置。

无论选择哪一样,Spring都能很好的支持,甚至混合也行。值得指出的是,
使用`[JavaConfig](#beans-java)`选项,Spring能在不接触目标组件源码的情况下
无侵入的使用注解,这可以通过IDE完成 [Spring Tool Suite](https://spring.io/tools/sts)

对于XML配置,还有另外一个选择,基于注解的配置,它是依赖于字节码元数据,替代XML组装组件。码农码畜可以使用注解替代XML描述bean的组装,开发者将配置撰写到组件类上,使用注解标注相关的类、方法、域上。就像前面提到的 in the section called “Example: The RequiredAnnotationBeanPostProcessor”,使用BeanPostProcessor联结注解是常见的扩展Spring IoC容器的手段。举个栗子,Spring2.0引入的通过@Required注解强制检查必须属性值。Spring 2.5采用了类似的手法使用注解处理依赖注入。本质上,@Autowired注解提供了相同的能力,在这一章有详解Section 5.4.5, “Autowiring collaborators”,但是@Autowired提供了更细粒度的控制和更强的能力。Spirng 2.5也增加了对JSR-250注解的支持,比如@PostConstruct,@PreDestory。Srping3.0增加支持了JSR-330(JAVA依赖注入)注解,这些注解在javax.inject包内,例如@Inject@Named。详情参看那些注解的相关章节。

注意

注意
注解注入在XML注入之前执行,因此同时使用这两种方式注入时,XML配置会覆盖注解配置。

同样的Spring风格,就像特别的bean定义那样注册他们,但是也能像下面这样隐式注册(注意包含context namespace上下文命名空间)




    


(隐式注册的post-processors包括AutowiredAnnotationBeanPostProcessor,CommonAnnotationBeanPostProcessor,PersistenceAnnotationBeanPostProcessor,还有前面提到的RequiredAnnotationBeanPostProcessor)

注意

注意
仅会检索它所在的应用context上下文中bean上的注解。也就是,如果在WebApplicationContext中为DispatcherServlet设置,它仅会检查controllers@Autowired的bean,并不会检查service。详情参看Section 17.2, “The DispatcherServlet”

@Required


@Required注解应用于bean的setter方法,像这样:

public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Required
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // ...

}

这个注解意思是受到影响的bean属性在配置时必须赋值,在bean定义中明确指定其属性值或者通过自动注入。若该属性未指定值,容器会抛异常。这导致及时明确的失败,避免NullPointerExceptions或者晚一些时候才发现。仍然推荐,你在编码过程中使用断言,举个栗子,在init方法,做了这些强制的必须引用的检查,但是属性值甚至不再容器范围内。

@Autowired


如你所料,@Autowired注解也是应用在"传统的"setter方法上:

public class SimpleMovieLister {

    private MovieFinder movieFinder;

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

    // ...

}
注意

注意
在下面的样例中,使用JSR 330的@Inject注解可以替代@autowired注解。详情参看这里

也可以将注解用于带一个或多个参数的其他方法上,看样例:

public class MovieRecommender {

    private MovieCatalog movieCatalog;

    private CustomerPreferenceDao customerPreferenceDao;

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

    // ...

}

@Autowired也可以应用于构造函数上或者属性上:

public class MovieRecommender {

    @Autowired
    private MovieCatalog movieCatalog;

    private CustomerPreferenceDao customerPreferenceDao;

    @Autowired
    public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
        this.customerPreferenceDao = customerPreferenceDao;
    }

    // ...

}

你可能感兴趣的:(5)