首页 > 代码库 > Spring 容器
Spring 容器
1.Spring容器
Spring容器最基本的接口就是BeanFactory, 负责配置,创建和管理bean。我们通常不直接使用BeanFactory接口,而是使用其子接口ApplicationContext.
接口ApplicationContext常用实现类是FileSystemXmlApplicationContext和ClassPathXmlApplicationContext等。后者最常用。
ApplicationContext的实例就是一个容器,Spring容器的意义在于创建和初始化类对象,管理类的依赖关系。
2.ApplicationContext的事件机制
AppicationContext的事件机制是观察者模式的实现,按以下方式可以实现。
- 首先我们需要自定义一个事件类,此事件类是需要继承ApplicationEvent类
- 然后我们定义一个监听类,监听类作为一个Spring容器中的bean,同时需要实现ApplicationListner接口
- 然后我们就可以使用ApplicationContext的实例发布事件,相应监听类的实例(bean)负责监听具体的事件
下面是一个简单的例子,
事件类,必须继承ApplicationEvent,是否为Spring容器的bean无所谓,
1 package spi; 2 3 import org.springframework.context.ApplicationEvent; 4 5 public class EmailEvent extends ApplicationEvent { 6 private String address; 7 public String getAddress() { 8 return address; 9 } 10 public void setAddress(String address) { 11 this.address = address; 12 } 13 public String getText() { 14 return text; 15 } 16 public void setText(String text) { 17 this.text = text; 18 } 19 private String text; 20 public EmailEvent(Object source) { 21 super(source); 22 } 23 public EmailEvent(Object source, String address, String text) { 24 super(source); 25 this.address = address; 26 this.text = text; 27 } 28 }
监听类,必须作为Spring容器的bean,同时需要实现ApplicationListener接口,重写onApplicationEvent方法
1 package spi; 2 3 import org.springframework.context.ApplicationEvent; 4 import org.springframework.context.ApplicationListener; 5 6 public class EmailNotifier implements ApplicationListener { 7 8 @Override 9 public void onApplicationEvent(ApplicationEvent evt) { 10 if (evt instanceof EmailEvent) { 11 EmailEvent emailEvent = (EmailEvent)evt; 12 System.out.println("邮件地址:"+emailEvent.getAddress()); 13 System.out.println("邮件内容:"+emailEvent.getText()); 14 } else { 15 System.out.println("其他事件:"+evt); 16 } 17 } 18 19 }
将监听类配置进Spring容器配置文件中,并没有什么特殊之处,
<bean class="spi.EmailNotifier" />
下面写一个测试类,
1 public static void test2() { 2 ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml"); 3 EmailEvent ele = new EmailEvent("test", "test@test.com", "this is a test"); 4 ctx.publishEvent(ele); 5 }
执行测试类,发现我们不仅监听到了想要监听的事件EmailEvent,同时还有一个系统事件ContextRefreshedEvent也被监听到了,输出如下,
1 其他事件:org.springframework.context.event.ContextRefreshedEvent[source=org.springframework.context.support.ClassPathXmlApplicationContext@1873eb2: startup date [Fri Feb 03 01:19:21 CST 2017]; root of context hierarchy] 2 邮件地址:test@test.com 3 邮件内容:this is a test
3.ApplicationContext的国际化支持
ApplicationContext接口继承了MessageSource接口,因此具有国际化功能。MessageSource接口提供了getMessage(...)方法用来进行字符串转换。
Spring要实现国际化,需要将MessageSource的实例配置成Spring容器中的bean,在bean的属性(即依赖注入)中配置国际化文件,
1 <!-- ApplicationContext的实例将会查找是否有messageSource的实例并初始化它 --> 2 <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource"> 3 <!-- 这里即依赖注入 --> 4 <property name="basenames"> 5 <list> 6 <!-- 在这里添加国际化配置文件 --> 7 <value>message</value> 8 </list> 9 </property> 10 </bean>
为此我们需要创建两份国际化配置文件,第一份为英语,文件名 message_en_US.properties,内容如下,
1 str1=welcome,{0} 2 str2=now is : {0}
第二份为中文,文件名为message.properties,内容如下,
1 str1=欢迎,{0} 2 str2=现在时间是:{0}
由于这个文件包含了非西欧字符,因此我们用java自带的native2ascii进行转换,命令为
1 cd C:\Program Files (x86)\Java\jdk1.7.0_79\bin2 native2ascii C:\PROJECT\JavaBasic\PROJECT_JavaBasic\src\message.properties C:\PROJECT\JavaBasic\PROJECT_JavaBasic\src\message_zh_CN.properties
转换后得到message_zh_CN.properties文件即可。
接着写一个测试类,
1 public static void test3() { 2 ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml"); 3 String str1 = ctx.getMessage("str1", new String[]{"孙悟空"}, 4 Locale.getDefault(Locale.Category.FORMAT)); 5 String str2 = ctx.getMessage("str2", new Object[]{new Date()}, 6 Locale.getDefault(Locale.Category.FORMAT)); 7 System.out.println(str1); 8 System.out.println(str2); 9 10 String str3 = ctx.getMessage("str1", new String[]{"孙悟空"}, 11 Locale.US); 12 String str4 = ctx.getMessage("str2", new Object[]{new Date()}, 13 Locale.US); 14 System.out.println(str3); 15 System.out.println(str4); 16 }
我们分别在中文环境和英文环境测试了两个字符串str1和str2,得到结果如下,
1 欢迎,孙悟空 2 现在时间是:17-2-3 上午1:31 3 welcome,孙悟空 4 now is : 2/3/17 1:31 AM
上面的文字“孙悟空”因为是直接写在代码里面,不属于properties配置文件的,因此在中英文结果都保留原来的文字。
4.让Bean获取Spring容器
如果需要让Bean主动获取它所在的Spring容器的引用,可以让该Bean实现BeanFactoryAware接口,并实现setBeanFactory(BeanFactory beanFactory)方法。
下面是一个例子,
1 package spi; 2 3 import java.util.Locale; 4 5 import org.springframework.beans.BeansException; 6 import org.springframework.context.ApplicationContext; 7 import org.springframework.context.ApplicationContextAware; 8 9 public class GetContextViaBean implements ApplicationContextAware { 10 private ApplicationContext ctx; 11 @Override 12 public void setApplicationContext(ApplicationContext ctx) 13 throws BeansException { 14 this.ctx = ctx; 15 } 16 public void SayHi(String name) { 17 System.out.println(ctx.getMessage("str1", new String[]{name}, Locale.US)); 18 } 19 20 }
将这个类作为一个普通bean配置进Spring中,
<bean id="getContextViaBean" class="spi.GetContextViaBean" />
下面写一个测试类,来实现前面的国际化的功能,
1 public static void test4() { 2 ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml"); 3 GetContextViaBean bean = ctx.getBean("getContextViaBean", GetContextViaBean.class); 4 bean.SayHi("孙悟空"); 5 }
执行结果为,
1 welcome,孙悟空
与前面国际化方式相比,其实本质上没有区别,只不过前面的国际化是在测试类中,直接使用ApplicationContext的实例调用MessageSource的getMessage()方法,
而这里是在Bean中,主动去获取所在容器(ApplicationContext)的引用,并在beanzhong通过ApplicationContext的引用调用了MessageSource的getMessage()方法。
Spring 容器