Spring ClassPathXmlApplicationContext介绍

2023/05/13

1. 概述

简单地说,Spring框架的核心就是一个用于管理bean的IoC容器。

Spring中有两种基本类型的容器 - BeanFactory和ApplicationContext。 前者提供基本功能,本文将介绍这些功能;后者是前者的扩展,使用最广泛。

ApplicationContext是org.springframework.context包中的一个接口。它有几个实现类,ClassPathXmlApplicationContext就是其中之一。

在本文中,我们将重点讨论ClassPathXmlApplicationContext提供的有用功能。

2. 基本用法

2.1 初始化容器并管理bean

ClassPathXmlApplicationContext可以从类路径加载XML配置并管理其中定义的bean:

假设我们有一个Student类:


@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student {
    private int no;
    private String name;
}

我们在classpathxmlapplicationcontext-example.xml中配置一个Student bean:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd   http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <context:annotation-config/>

    <bean id="student" class="cn.tuyucheng.taketoday.applicationcontext.Student">
        <property name="no" value="15"/>
        <property name="name" value="Tom"/>
    </bean>
</beans>

现在,我们可以使用ClassPathXmlApplicationContext加载XML配置并获取Student bean:

class ClasspathXmlApplicationContextIntegrationTest {

    @Test
    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);// do not need to cast 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注册一个关机钩子,以确保Spring IoC容器正常关闭,并调用destroy()方法来释放资源。

让我们在Student类中添加一个destroy()方法:

public class Student {

    public void destroy() {
        log.info("Student(no: {}) is destroyed", no);
    }
}

我们现在可以将此方法配置为Student bean的destroy-method:


<bean id="student" class="cn.tuyucheng.taketoday.applicationcontext.Student" destroy-method="destroy">
    <property name="no" value="15"/>
    <property name="name" value="Tom"/>
</bean>

接下来我们注册一个关机钩子:

class ClasspathXmlApplicationContextIntegrationTest {

    @Test
    void testRegisterShutdownHook() {
        ConfigurableApplicationContext context = new ClassPathXmlApplicationContext("classpathxmlapplicationcontext-example.xml");
        context.registerShutdownHook();
    }
}

当我们运行测试时,可以看到destroy()方法被调用。

3.2 使用MessageSource进行国际化

ApplicationContext接口继承了MessageSource,因此提供了国际化功能。

ApplicationContext容器在其初始化时会自动搜索MessageSource bean,该bean必须命名为messageSource。

下面是一个通过MessageSource使用不同语言的示例:

首先,让我们在resources目录中添加一个dialog目录,并在dialog目录中添加两个文件:dialog_en.properties和dialog_zh_CN.properties。

dialog_en.properties:

hello=hello
you=you
thanks=thank {0}

dialog_zh_CN.properties:

hello=你好
you=
thanks=谢谢{0}

接下来在classpathxmlapplicationcontext-internationalization.xml中配置MessageSource bean:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
        <property name="basenames">
            <list>
                <value>dialog/dialog</value>
            </list>
        </property>
    </bean>
</beans>

然后,我们用MessageSource获取不同语言的属性:

class ClasspathXmlApplicationContextIntegrationTest {

    @Test
    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的引用

有时我们需要在代码中获取ApplicationContext的引用,我们可以使用ApplicationContextAware或@Autowired来做到这一点。 让我们看看如何使用ApplicationContextAware:


@Data
@NoArgsConstructor
@AllArgsConstructor
public class Course {
    private String name;
}

我们有一个Teacher类,根据容器的bean来组合Course:

public class Teacher implements ApplicationContextAware {
    private ApplicationContext context;
    private List<Course> courses = new ArrayList<>();

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        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);
        }
    }

    public List<Course> getCourses() {
        return courses;
    }

    public void setCourses(List<Course> courses) {
        this.courses = courses;
    }
}

然后让我们在classpathxmlapplicationcontext-example.xml中配置Course和Teacher bean。

<bean id="math" class="cn.tuyucheng.taketoday.applicationcontext.Course">
    <property name="name" value="math"/>
</bean>

<bean name="teacher" class="cn.tuyucheng.taketoday.applicationcontext.Teacher"/>

最后测试courses属性的注入:

class ClasspathXmlApplicationContextIntegrationTest {

    @Test
    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. 总结

ApplicationContext是一个Spring容器,与BeanFactory相比具有更多的企业特定功能,ClassPathXmlApplicationContext是其最常用的实现之一。

在本文中,我们介绍了ClassPathXmlApplicationContext的几个方面,包括它的基本用法、它的关机注册功能、它的国际化功能以及引用的获取。

与往常一样,本教程的完整源代码可在GitHub上获得。

Show Disqus Comments

Post Directory

扫码关注公众号:Taketoday
发送 290992
即可立即永久解锁本站全部文章