spring_ioc的其余功能 (7.14节以后)

###7.14 Registering a LoadTimeWeaver 当类被加载到JVM中时,LoadTimeWeaver将动态转化类. 要使用运行时织入器,要在你的@Configuration类里添加@EnableLoadTimeWeaving注解.html

@Configuration
@EnableLoadTimeWeaving
public class AppConfig {

}

另外一种方式是在xml配置里使用context:load-time-weaver元素:java

<beans>
    <context:load-time-weaver/>
</beans>

当ApplicationContext一旦配置,任何在ApplicationContext内的实现了LoadTImeWeaverAware接口的Bean,会获得一个加载期织入器实例的引用.在聚合Spring jpa support 时很是有用, 由于对JPA的类转译而言,运行时织入是必须的.查找LocalContainerEntityManagerFactoryBean的文档来了解更多.对于更多 AspectJ 加载期织入的,参看 11.8.4 "load-time weaving with AspectJ in the Spring Framework".web

##7.15 Additional Capabilities of the ApplicationContext(应用上下文的其余功能) 如同咱们在章节介绍中讨论的,org.springframework.beans.factory包提供了管理和操做beans的基本功能,包括用编程方式.这个org.springframework.context包添加了ApplicationContext,它扩展了BeanFactory接口,并以更多的应用面相框架风格来提供更多的接口以实现更多的功能.许多人以彻底声明的方式使用ApplicationContext,甚至不用程序建立它,而是依赖相关的额外支持类,如ContextLoader,用来自动实例化ApplicationContext,以做为java EE web应用正常启动流程的一部分.spring

要保证BeanFactory的功能更面向框架,那么上下文包也须要提供如下功能:编程

  • 以 i18n-style(国际化) 的风格访问消息,经过MessageSource接口.
  • 访问资源,例如以URLS或文件,经过ResourceLoader接口.
  • 经过使用 ApplicationEventPublisher接口,事件可发布到以实现了ApplicationListener接口的已命名的bean中
  • 多层次上下文加载时,经过HierarchicalBeanFactory 接口,容许每一个人只关注一个特定的层次,例如一个应用的web层;

###7.15.1 Internationalization using MessageSource(经过消息资源实现国际化) ApplicationContext接口扩展了一个名为MessageSource的接口,并提供了国际化功能.spring还提供了HierarchicalMessagesource接口,它能够层次化处理消息.这些接口在一块儿构成了spring相应消息处理的基础.这些接口的方法包括:windows

  • String getMessage(String code,Object[] args,String default,Local loc):这是从MessageSource抓取消息的基本方法.当特定的位置没有发现消息时,默认的消息会使用.任何经过的参数变成特定值,可使用标准类库里的messageFormat功能.设计模式

  • String getMessage(String code,Object[] args,Locale loc):和上一个方法基本上是相同的,但有一点不一样:因没有指定默认值,全部当没有消息被发现,一个NoSuchMessageException会被抛出.数组

  • String getMessage(MessageSourceResolvable resolvable,Locale locale):上面方法全部的属性都会被包裹进一个名为MessageSourceResolvable的类里,你也可使用这个方法替代.安全

当一个ApplicationContext被加载时,它会自动搜索每个在上下文中定义的MessageSource的bean.这个Bean必须命名为messageSource.若是该bean被发现,上面全部对该消息资源的方法的调用都是代理形式.若是没有发现消息资源,那么ApplicationContext将尝试寻找其上级的包含了相同名字的bean.若是这样,那么它会使用名为MessageSource的bean.若是ApplicationContext仍是没法发现消息资源,一个空的DelegatingMessageSource将会实例化以用来接受上面方法的调用.服务器

Spring提供了两种MessageSource的实现,ResourceBundleMessageSource和StaticMessageSource.两者都实现了HierarchicalMessageSource以处理嵌套消息.StaticMessageSource不多使用,但它提供了编程的方式向资源中添加消息.而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>

上面的例子中代表你在类路径里有三种资源绑定定义,名字为:format,exceptions ,windows.任何释放消息的请求,会被经过ResourceBundeles以JDK标准释放消息的方式来处理.对于这些例子的目标,假设如下两种资源绑定文件的内容以下:

# in format.properties
message=Alligators rock!

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

下面的例子展现的是一段执行MessageSOurce功能的程序.记住全部ApplicationCOntext的实现同时也是MessageSource的实现,能够转化为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!

全部来回顾下,在beans.xml里定义的MessageSource,该文件存在于类路径的根路径下.messageSource的bean定义经过它的basenames属性指向大量的资源定义.这三个在beannames属性列表里的的文件存在于你的类路径的根节点下,分别是format.properties,exceptions.properties,windows.properties.

下面的例子让我看下消息查找的参数,这些参数会转化到String中,并插入到查找消息的占位符里.

<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>

经过xml,设置参数依赖,将messageSource注入到example的Bean中;

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.

关于国际化,spring的各类MessageSource实现遵循和标准JDK ResourceBundle同样的场景方案和回滚规则.继续上面的messageSource的定义.若是你要在英文场景下释放这些消息,简单的说,你须要分别建立这三个文件,format_en_GB.properties,exceptions_en_GB.properties,windows_en_GB.properties.

通常而言,场景解决主要有应用的周边环境来管理.在本例中,消息的英文场景会手动指定;

# 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.

你可使用MessageSourceAware接口来得到一个已经定义了的MessageSource接口的引用.定义在ApplicationContext中实现了MessageSourceAware接口的bean,当它们建立时会自动注入应用上下文的MessageSource.

做为ResourceBundleMessageSource的替换,spring提供了一个ReloadableResourceBundleMessageSource类.这个变种支持相同的文件绑定格式,但比基于标准JDK的ResourceBundleMessageSource实现要更加灵活.特别,它容许从任何spring的资源位置(不只仅类路径)读取文件,并支持对绑定属性文件的热重载(重载期间有效的缓冲).查看ReloadableResourceBundleMessageSource文档来肯定更多细节.

###7.15.2 Standard and Custom Events (标准和自定义事件) 在ApplicationCOntext中的事件处理是经过ApplicationEvent类和ApplicationListener接口来实现的.若是有一个实现了ApplicationListener接口的bean部署到上下文中,每当一个ApplicationEvent发布到ApplicationContext时,这个bean会被调用.基本上,这就是标准的Observer(观察者)设计模式;

在spring4.2后,事件组件获得了明显的提高,并提供了基于注解的模式已发布任意事件,也就说一个对象没有必要继承ApplicationEvent类.当符合条件的对象发布时,咱们为你将该对象包装到一个事件里;

spring提供了一下标准事件. ###Built-in Events 输入图片说明 输入图片说明 ####ContextRefreshedEvent 上下文刷新事件

当ApplicationContext已初始化或已刷新时,会发布.例如,使用ConfigurableApplicationContext接口中的refresh()方法."initialized"意味着全部的bean都已被加载,后处理器的bean都被检测和启动,单例bean都被提早实例化,且ApplicationContext对象已作好准备使用.只要上下文没被关闭,有特定的ApplicationContext提供的热刷新,能够被触发不少次.

####ContextStartedEvent 上下文启动事件 发布:当ApplicationContext启动,会使用ConfigurableApplicationContext接口里的start()方法."Started"意味着全部生命周期的bean收到了一个明确的启动信号.通常而言,这个信号只用来在一个明确的中止后重启beans,可是它能够被用来启动一些没被设置为自动启动的bean,例如一些在初始化中没被启动的组件. ####ContextStoppedEvent 上下文中止事件

当使用ConfigurableApplicationContext接口中的stop()方法时,上下文就会中止,事件发布."Stopped"这里指全部的生命周期的beans都会受到一个明确的中止信号.一个中止的上下文能够经过start()方法重启.

ContextClosedEvent 上下文关闭事件

当调用ConfigurableApplicationContext接口的close()方法时,ApplicationContext就会关闭,事件就会发布."Closed"这里指的是全部的单例bean都会被销毁.一个关闭的上下文已到达它生命的端点,他不可能被刷新或重启.

RequestHandledEvent 请求处理事件

一个特定的web事件,告诉全部的bean要为一个HTTP请求提供服务.当请求拼完后这个事件就会发布.这个事件只有当一个web应用使用spring的DispatcherServlet服务时才适用.

你能够建立和发布你的自定义事件.这个例子展现了一个扩展了Spring 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...

}

要发布一个自定义事件,须要调用ApplicationEventPublisher实例上的publishEvent()方法.通常而言,能够经过实现ApplicationEventPublisherAware来建立一个类并将它注册为一个spring 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容器会检测到EmailService实现了ApplicationEventPublisherAware接口,并自动调用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一般是经过你的自定义事件的类型(BlackListEvent类)来泛型化参数的.这意味着onApplicationEvent()方法能够保持类型安全,避免进行向下转型.你能够注册很是多的监听器,但要记住事件监听器默认会进行同步监听.这意味着只有当全部的监听器处理完这个事件以后,publishEvent()方法才会中止.这种同步和单线程处理的优势之一是:若是一个监听器接受到事件,当它能从发布者那里得到一个事务上下文,那它会在上下文里处理事件.若是有必要使用其余的事件发布策略,查看spring的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的bean的sendEmail()方法被调用时,若是有一个email是黑名单中,一个BlackListEvent类型的自定义事件就会被发布.而blackListNotifier的bean被注册为ApplicationListener(应用监听器)被会接受这个BlackListEvent事件,此时它就会通知适当的部分.

spring的事件机制主要用于同一应用上下文中spring bean之间的简单交互.可是,对于更加复杂的企业集成需求,这个分布式的spring集成项目提供了对基于spring编程模式来搭建轻量级,面向模式,事件驱动的架构的全面支持.

####Annotation--based Event Listeners 基于注解的事件监听器 在spring4.2以后,一个事件监听器能够经过在一个受管理的bean里的一个公共方法上添加EventListener注解来注册.这个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() {

}

咱们能够经过注解的condition属性来添加额外的运行时拦截条件,该属性定义了一个SpEL表达式,你须要匹配这个特定事件,才能调用相应的方法.

例如,咱们的通知器能够重写为只有当事件的test属性等于foo时才会调用.

@EventListener(condition = "#event.test == 'foo'")
public void processBlackListEvent(BlackListEvent event) {
    // notify appropriate parties via notificationAddress...
}

每一次SpEL表达式评估都有专门的上下文.下面的表格展现了你在该上下文中能得到的选项,全部你可使用他们进行有条件的事件处理:

####Event Spel available metadata 输入图片说明 名称:event 位置:root object 描述:实际的ApplicationEvent(应用事件),例子:#root.event

名称:args 位置:root Object 描述:调用目标使用的参数(数组) 例子:#root.args[0]

名称:argument name 位置:evaluation context 描述:方法中每一个参数的名字.若是因某些缘由这些名字不可得到,这个参数的名称仍能够经过#a<#arg>来获取,这个#arg表明的是参数的下标(从0开始). 例子:#iban或#a0(也可使用#p0或使用#p<#arg>标志做为别名)

记住#root.event容许你访问对应的事件,即便你的方法签名实际上对应的是任意要发布的对象.

若是你由于处理完某个事件后须要发布一个新事件,只须要把方法签名的返回修改成你须要发布的事件的类型,像这样:

@EventListener
public ListUpdateEvent handleBlackListEvent(BlackListEvent event) {
    // notify appropriate parties via notificationAddress and
    // then publish a ListUpdateEvent...
}

该功能不支持异步的监听器;

这个新方法会为每一个该方法处理过的blackListEvent发布一个新的ListUpdateEvent事件.若是你须要发布几个事件,只须要返回这些事件的集合.

####Asynchronous Listeners 同步监听器

若是你想要一个特别的监听器来异步处理事件,简单的使用常规的@Async支持:

@EventListener
@Async
public void processBlackListEvent(BlackListEvent event) {
    // BlackListEvent is processed in a separate thread
}

记住使用异步事件时注意如下几个局限:

  • 1.若是监听器抛出一个异常,它不会传播到调用者那里.查看AsyncUncaughtExceptionHandler以应对更多细节.
  • 2.这些事件监听器没法发送回复.若是你须要发送其余事件做为处理结果,注入ApplicationEventPublisher进行手动发送.

####Ordering Listeners 监听器排序 若是你想要一个监听器先于其余调用.只须要在方法参数上添加@Order注解;

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

####Generic Events 泛型事件 你可使用泛型更深刻的定义你的事件的结构.思考一个EntityCreatedEvent<T>,其中T是被建立实体的实际类型.你能够建立一个只用来接受Person类的EntityCreatedEvent事件监听器;

@EventListener
public void onPersonCreated(EntityCreatedEvent<Person> event) {
    ...
}

因为类型擦除,只有当事件无缺符合事件监听器拦截的泛型参数,它才工做.(这相似如class PersonCreatedEvent extends EntityCreatedEvent<Person>{...});

在特定的环境下,若是有全部的事件都遵循相同的结构(它多是上面提到的那种事件)会变得至关乏味.这样的话,你可用实现ResolvableTypeProvider来指导运行时环境提供的架构.

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事件其做用,还适用于你做为事件发布的任意对象.

###7.15.3 Convenient access to low-level resources (有效的访问低级资源) 做为应用上下文的可选使用和理解,用户应该基本上经过Spring Resource abstraction 抽象来熟悉它们;同第8章所讲的;

一个应用上下文是一个资源加载器,能够用来加载Resource.一个资源本质上是JDK类中的java.net.URL类的更多功能的变种.实际上,java.net.Resource的实现以合适的方式来包装URL的实例.一个Resource能够以透明的方获取低层次的资源,包括类路径,文件系统位置,任何能被标准RUL描述的资源,或者其余类型.若是这个资源位置字符串是一个不带任何前缀的简单路径,这些资源来自于特定和合适的实际应用上下文类型.

你能够配置一个bean部署到应用上下文中来实现指定的回调接口->ResourceLoaderAware,在应用上下文初始化时会做为ResourceLoader而自动回调.你能够暴露Resource类型的属性,以用来访问静态资源;他们也能够像其余属性同样被注入.你能够将这些Resource属性指定为简单的字符串路径,它依赖的一个特定的JavaBean是PropertyEditor已被自动注册到上下文中,该javaBean部署以后能够将文本字符串转化为时间的资源对象.

这个提供给ApplicationContext构造器的位置路径是真实的资源字符串,并以简单的方式妥善的被特定的上下文实现进行处理.ClassPathXmlApplicationContext能够把一个简单的位置路径做为一个类路径位置.你也可使用带具体前缀的位置路径来从类路径或URL来强制加载定义,而忽略实际的上下文类型.

###7.15.4 Convienent ApplicationContext instantiation for web applications 例如,你可使用一个ContextLoader建立一个ApplicationContext实例.固然你也可使用ApplicationContext的某个实现来动态建立ApplicationContext的一个实例.

你能够以下使用ContextLoaderListener来注册一个ApplicationContext:

<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>

监听器会检验上下文配置位置的参数.若是参数不存在,那么监听器会默认使用/WEB-INF/applicationContext.xml.当参数存在,那这个监听器户会经过已定义的分隔符(逗号,分号,空白符)来分割该字符串,并将这些值做为做为应用上下文要查询的位置.Ant风格的匹配符一样支持.例如/WEB-INF/*Context.xml可用于全部以Context.xml结尾的文件.若是存在于"WEB_INF"目录,并为/WEB-INF/**/*Context.xml,那么使用于全部"WEB-INF"下的子目录;

###7.15.5 Deploying a Spring ApplicationContext as a Java EE RAR file (以java EE RAR文件来部署spring应用上下文) 你能够把spring应用上下文做为一个RAR文件来部署,包括这个上下文和它全部必须的bean类,以及部署在java EE RAR部署单元里的类库JAR包.这等同在一个单独的ApplicationContext,在一个java EE环境里,能够访问java EE服务设施.RAR部署的是无需处理的WAR文件部署的另外一个更加天然的替代物,实际上,一个WAR文件无需任何HTTP接触点就能够启动并只能用于在java EE环境里启动一个spring应用上下文.

RAR部署对于应用上下文来讲很理想,不须要http触发点,只须要一些消息端点和定时任务.上下文中的Beans也可使用应用服务器的资源,例如JTA事务管理,JNDI-bound JDBC DataSources,JMS ConnectionFactory instance,或许还有已经过平台注册的JMX服务器;都是经过spring标准事务管理和JNDI,JMX支持组件实现的.应用组件也能够经过spring的TaskExecutor抽象同应用服务器的JCA工做管理器交互.

查看SpringContextResourceAdapter类的文档来获取RAR部署的更多细节.

简单把一个spring应用上下文作为一个java EE RAR文件部署:打包全部的应用类到一个RAR文件里,这是一个有不一样文件扩展名的标准JAR文件.在RAR结构里的根位置里添加全部必须的类库JARs.添加一个"META-INF/ra.xml"部署描述器(在SpringContextResourceAdapter的文档里有描述)和对应的spring xml bean定义的文件(通常是META-INF/applicationContext.xml文件),并把生成的RAR文件剪贴到应用服务器的部署目录.

这些RAR部署单元一般有很强的独立性,它们不会向外界暴露接口,就是同一应用的其余模块也不行.(像电网UAP项目里的模块项目同样);基于RAR的应用上下文之间的交互是经过与其余模块共享的JMS端点来实现的.一个基于RAR的系统也能够有其余功能:例如,安排些任务,对文件系统里的新文件作些反应.若是它须要和外界进行同步交互,它容许你导出RMI 端点,固然这也适用于同一机制下的其余应用模块.

##7.16 The BeanFactory BeanFactory提供了spring ioc功能的基础,但如今它只在集成第三方框架和spring的老用户直接使用.BeanFactory和相关的接口,例如BeanFactoryAware,InitializingBean,DisposableBean等依然在spring中存在,主要是为了照顾spring中集成的第三方框架的向后兼容性.通常第三方的组件没法使用太新的的东西,好比@PostConstruct或@PreDestroy,所以要保持对jdk1.4的兼容或避免使用JSR-250的依赖.

这部分介绍了BeanFactory和ApplicationContext的差别的 额外背景,以及如何经过一个类的单例查找来访问IOC容器. ###7.16.1 BeanFactory or ApplicationContext? 除非你有更好的理由,不然请使用ApplicationContext;

由于ApplicationContext包含了全部BeanFactory的功能;建议使用BeanFactory的状况通常是:在一个资源受限的应用上运行项目,由于此时资源开销会很是重要,即便几KB也会致使极大不一样.可是,对于绝大多数典型的企业应用和系统来言,你仍是须要ApplicationContext的.spring大量使用了BeanPostProcessor 的extension point(进行高效代理或其余).假如你只使用简单的BeanFactory,如事务管理和AOP等不少支持将不起做用,至少在你的代码里不会有额外的步骤.这种状况会令你困惑,由于你的配置里没有其余错误.

下面列表提供了BeanFactory和ApplicationContext接口和实现提供的功能;

BeanFactory : 1; ApplicationContext:2

  • Bean instantiation/wiring 1:yes 2:yes
  • Automatic BeanPostProcessor registration 1:NO, 2:yes
  • Automatic BeanFactoryPostProcessor registration 1:no 2:yes
  • Convenient MessageSource access(for i18n) 1:no 2:yes -ApplicationEvent publication 1:no 2:yes

要明确使用BeanFactory实现来注册一个后处理器,你须要这样编码:

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);

在这些步骤中,具体的注册步骤并不重要;这也是大部分各类ApplicatinContext实现更倾向基于使用BeanFactory来实现,特别是当使用BeanFactoryPostProcessor和BeanPostProcessor时.这些机制实现了很是重要的功能,如属性替代符替换和AOP;

###7.16.2 Glue code and the evil singleton (大泥球代码和讨厌单例) 你最好使用依赖注入(DI)的风格来写应用代码,这些代码将由spring ioc提供服务,在其建立时就会获得容器提供的依赖,且彻底不须知道容器.可是,对于一小部分胶水层的代码,它们须要和其余代码链接在一块儿,有时你须要单例(等同单例)风格来访问spring ioc的容器.例如,第三方的代码会无论是否能从spring的ioc容器里得到这些对象而直接尝试建立新的对象(Class.forName()风格).若是被第三方代码构造的是一个小的存根或代理,它可使用单例风格来访问spring ioc容器来获得它要代理的实际对象,那么控制反转对于大部分代码(容器以外生成的对象)来讲仍是可使用.由于这些代码仍不须要关心容器或关心如何访问它,保持了和其余代码的解耦,并保持必要的便利.EJBs也可使用这种stub/proxy方式从spring ioc容器里获取 一个简单java对象的代理.然而spring ioc容器自身最好不是必须是个单例bean,这不现实;由于每一个bean来使用本身容器的内存开销或初始化时间是(当使用spring IOC容器里的bean例如一个hibernate的SessionFactory)不同,全部最好是非单例的spring IOC容器.

以服务定位的风格来查找应用上下文有时是访问公共spring-managed 组件的惟一方式,例如在一个EJB2.1环境,又或者当你想分享一个单例应用上下文做为WAR文件里的父级webApplicationContexts.这种状况下你应该使用springteam blog entry里描述的有用类ContextSingleBeanFactoryLocator定位器


Resource抽象,java.net.URL,ResourceLoader,ResourceLoaderAware;contextConfigLocation;RAR文件,JTA;BeanFactory,glue code ,evil singleton

相关文章
相关标签/搜索