Spring有连个核心接口:BeanFactory和ApplicationContext。而ApplicationContext是BeanFactory的子接口,都可以代表Spring容器,用于产生管理Bean们。我们经常使用ApplicationContext以及它的实现类ClassPathXmlApplicationContext。当创建ApplicationContext时,会实例化所有的单例Bean,花销较大,但之后性能较好。如果为bean设置lazy-init为true,则不会在开始初始化。
国际化支持
ApplicationContext集成了MessageSource接口,具有国际化功能。当程序创建ApplicationContext容器时,架构会去找messageSource这个Bean,然后接口中的getMessage(String code,Object[] args, Locale loc)与getMessage(String code, Object[] args, String default, Local loc)就会被交给messageSource这个bean使用。如果没找到,则会穿件一个StaticMessageSource,由它调用两个方法。其中第二个参数的对象组是为了填补国际化文件中的{0}、{1}这些的。
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource"> <property name="basenames"> <list> <value>包名/资源文件组1</value> <value>包名1/包名2/资源文件组2</value> </list> </property> </bean>
使用方法:
String hello=act.getMessage("hello", new String[] {"A"}, Locale.getDefault());
ApplicationContext的事件机制
通过ApplicationEvent(必须由ApplicationContext发布)和ApplicationListener(容器中任意的bean承担)接口可以实现ApplicationContext的事件处理。如果容器中有一个ApplicationListener Bean,每当ApplicaitonContext发布ApplicationEvent,ApplicationLIstener Bean将会自动被触发。我们都知道事件机制需要事件源,事件和监听器。事件源此处是ApplicationContext,必须由java程序显式触发。
事件如下,必须继承ApplicationEvent父类
package com.cm.event; import org.springframework.context.ApplicationEvent; public class EmailEvent extends ApplicationEvent { private String address; private String text; public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public String getText() { return text; } public void setText(String text) { this.text = text; } public EmailEvent(Object source,String address,String text) { super(source); this.address=address; this.text=text; // TODO Auto-generated constructor stub } public EmailEvent(Object source) { super(source); // TODO Auto-generated constructor stub } }
监听器如下,需要实现ApplicationListener接口:
public class EmailNotifier implements ApplicationListener { @Override public void onApplicationEvent(ApplicationEvent arg0) { // TODO Auto-generated method stub if(arg0 instanceof EmailEvent){ EmailEvent emailEvent=(EmailEvent)arg0; System.out.println("需要发送邮件的接收地址"+emailEvent.getAddress()); System.out.println("需要发送邮件的征文"+emailEvent.getText()); }else{ System.out.println("other!!!!!!!!!!!"+arg0); }} }
注意我们在xml中配置的只有监听器,而事件是显式触发的(当然有些时候也需要配置,不过本例中我们还是使用了new,因为没有用到依赖关系)
<bean class="com.cm.event.EmailNotifier">
显式触发事件
ApplicationContext act=new ClassPathXmlApplicationContext("beans.xml"); EmailEvent ele=new EmailEvent("test","[email protected]","my email"); act.publishEvent(ele);
总结如下:当程序发布了一个事件(内置事件不需要手动发布,会自动监听),xml中的所有监听器就会自动被启动,并执行相应程序。
让Bean获取Spring容器
之前的例子中我们都是先new一个ApplicationContext实例,然后通过实例操作。这种情况下,程序中总是持有Spring容器的引用。Web应用中,Spring容器通常采用声明方式配置产生。开发者需要在web.xml中设一个listener,它会负责初始化容器,前段mvc框架可以直接调用bean,无需通过容器。但是有的时候,bean需要实现某个功能比如国际化(这个bean中需要使用ctx,使用的是容器实例的getMessage方法),而他们必须借助容器才能完成,因此还是要先得到容器,再实现功能。
为了能让某一个bean得到他的爸爸:容器,可以让这个bean实现BeanFactoryAware接口,它里面youyigesetBeanFactory方法,它的参数就是指向创建它的容器。这个set方法是由Spring调用的。
public class Aperson implements ApplicationContextAware { private ApplicationContext ctx; @Override public void setApplicationContext(ApplicationContext arg0) throws BeansException { // TODO Auto-generated method stub this.ctx=arg0; } public void sayHi(String name){ System.out.println(ctx.getMessage(arg0, arg1)); } }
架构会扫面所有的bean看哪个有接口ApplicationCAware,然后使用set方法传入容器实例。此bean的测试程序省略。。。测试中调用了sayHi方法,而这个方法自己没法完成功能,需要ctx的getMessage,因此它得想法得到实例。那就实现一个接口吧,Srping会为这个接口的set方法传入一个容器实例,将传入值赋给自己的成员变量吧!