1. 概述
Spring框架的核心简单来说就是一个用于管理bean的依赖注入容器。Spring中有两种基本类型的容器:Bean Factory和ApplicationContext。前者提供了基础功能,这部分内容在这里介绍;后者是前者的扩展版本,应用最为广泛。
ApplicationContext
是org.springframework.context
包下的一个接口,它有多个实现类,其中ClassPathXmlApplicationContext
就是其中之一。
本文将重点介绍ClassPathXmlApplicationContext
提供的实用功能。
2. 基本使用
2.1. 初始化容器与管理bean
ClassPathXmlApplicationContext
可以从类路径加载XML配置,并管理其中的bean:
我们有一个Student
类:
public class Student {
private int no;
private String name;
// standard constructors, getters and setters
}
我们在classpathxmlapplicationcontext-example.xml
中配置了一个Student
bean,并将其添加到类路径中:
<beans ...>
<bean id="student" class="com.baeldung.applicationcontext.Student">
<property name="no" value="15"/>
<property name="name" value="Tom"/>
</bean>
</beans>
现在我们可以使用ClassPathXmlApplicationContext
加载XML配置并获取Student
bean:
@Test
public void testBasicUsage() {
ApplicationContext context
= new ClassPathXmlApplicationContext(
"classpathxmlapplicationcontext-example.xml");
Student student = (Student) context.getBean("student");
assertThat(student.getNo(), equalTo(15));
assertThat(student.getName(), equalTo("Tom"));
Student sameStudent = context.getBean("student", Student.class);
assertThat(sameStudent.getNo(), equalTo(15));
assertThat(sameStudent.getName(), equalTo("Tom"));
}
2.2. 多个XML配置
有时我们需要使用多个XML配置来初始化Spring容器。在这种情况下,只需在构造ApplicationContext
时添加多个配置位置:
ApplicationContext context
= new ClassPathXmlApplicationContext("ctx.xml", "ctx2.xml");
3. 其他能力
3.1. 温和关闭Spring IoC容器
在Web应用中使用Spring IoC容器时,Spring的基于Web的ApplicationContext
实现会在应用关闭时温和地关闭容器。但在非Web环境,如独立桌面应用中,我们需要自行注册JVM的退出钩(shutdown hook)以确保Spring IoC容器能温和关闭并调用destroy
方法释放资源。
让我们在Student
类中添加一个destroy()
方法:
public void destroy() {
System.out.println("Student(no: " + no + ") is destroyed");
}
现在我们可以将这个方法配置为student
bean的destroy
方法:
<beans ...>
<bean id="student" class="com.baeldung.applicationcontext.Student"
destroy-method="destroy">
<property name="no" value="15"/>
<property name="name" value="Tom"/>
</bean>
</beans>
接下来,我们将注册一个退出钩:
@Test
public void testRegisterShutdownHook() {
ConfigurableApplicationContext context
= new ClassPathXmlApplicationContext(
"classpathxmlapplicationcontext-example.xml");
context.registerShutdownHook();
}
当我们运行测试方法时,可以看到destroy()
方法被调用了。
3.2. 国际化与MessageSource
ApplicationContext
接口继承自MessageSource
接口,因此提供了国际化功能。ApplicationContext
容器在初始化时会自动搜索名为messageSource
的MessageSource
bean。
以下是使用MessageSource
实现不同语言的例子:
首先,在类路径下添加一个dialog
目录,并在其中添加两个文件:dialog_en.properties
和dialog_zh_CN.properties
。
dialog_en.properties
:
hello=hello
you=you
thanks=thank {0}
dialog_zh_CN.properties
:
hello=\u4f60\u597d
you=\u4f60
thanks=\u8c22\u8c22{0}
在classpathxmlapplicationcontext-internationalization.xml
中配置messageSource
bean:
<beans ...>
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basenames">
<list>
<value>dialog/dialog</value>
</list>
</property>
</bean>
</beans>
然后,我们可以使用MessageSource
获取不同语言的对话字词:
@Test
public void testInternationalization() {
MessageSource resources
= new ClassPathXmlApplicationContext(
"classpathxmlapplicationcontext-internationalization.xml");
String enHello = resources.getMessage(
"hello", null, "Default", Locale.ENGLISH);
String enYou = resources.getMessage(
"you", null, Locale.ENGLISH);
String enThanks = resources.getMessage(
"thanks", new Object[] { enYou }, Locale.ENGLISH);
assertThat(enHello, equalTo("hello"));
assertThat(enThanks, equalTo("thank you"));
String chHello = resources.getMessage(
"hello", null, "Default", Locale.SIMPLIFIED_CHINESE);
String chYou = resources.getMessage(
"you", null, Locale.SIMPLIFIED_CHINESE);
String chThanks = resources.getMessage(
"thanks", new Object[] { chYou }, Locale.SIMPLIFIED_CHINESE);
assertThat(chHello, equalTo("你好"));
assertThat(chThanks, equalTo("谢谢你"));
}
4. ApplicationContext
引用
有时,我们需要在由其管理的bean中获取ApplicationContext
的引用,可以使用ApplicationContextAware
或@Autowired
来实现。让我们看看如何使用ApplicationContextAware
:
我们有一个带有名称的Course
类:
public class Course {
private String name;
// standard constructors, getters and setters
}
有一个Teacher
类,根据容器中的bean组装课程:
public class Teacher implements ApplicationContextAware {
private ApplicationContext context;
private List<Course> courses = new ArrayList<>();
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.context = applicationContext;
}
@PostConstruct
public void addCourse() {
if (context.containsBean("math")) {
Course math = context.getBean("math", Course.class);
courses.add(math);
}
if (context.containsBean("physics")) {
Course physics = context.getBean("physics", Course.class);
courses.add(physics);
}
}
// standard constructors, getters and setters
}
我们在classpathxmlapplicationcontext-example.xml
中配置了course
bean和teacher
bean:
<beans ...>
<bean id="math" class="com.baeldung.applicationcontext.Course">
<property name="name" value="math"/>
</bean>
<bean name="teacher" class="com.baeldung.applicationcontext.Teacher"/>
</beans>
然后测试courses
属性的注入:
@Test
public void testApplicationContextAware() {
ApplicationContext context
= new ClassPathXmlApplicationContext(
"classpathxmlapplicationcontext-example.xml");
Teacher teacher = context.getBean("teacher", Teacher.class);
List<Course> courses = teacher.getCourses();
assertThat(courses.size(), equalTo(1));
assertThat(courses.get(0).getName(), equalTo("math"));
}
除了实现ApplicationContextAware
接口,使用@Autowired
注解也有相同效果。
让我们将Teacher
类改为如下:
public class Teacher {
@Autowired
private ApplicationContext context;
private List<Course> courses = new ArrayList<>();
@PostConstruct
public void addCourse() {
if (context.containsBean("math")) {
Course math = context.getBean("math", Course.class);
courses.add(math);
}
if (context.containsBean("physics")) {
Course physics = context.getBean("physics", Course.class);
courses.add(physics);
}
}
// standard constructors, getters and setters
}
运行测试,可以看到结果相同。
5. 总结
相比于BeanFactory
,ApplicationContext
具有更多面向企业级的功能,而ClassPathXmlApplicationContext
是其最常用的实现之一。
本文介绍了ClassPathXmlApplicationContext
的一些方面,包括基本使用、退出钩注册、国际化功能以及获取其引用的方法。
如往常一样,示例的完整源代码可以在GitHub上找到。