Spring ApplicationContext

2023/05/13

1. 概述

在本教程中,我们将详细介绍Spring的ApplicationContext接口。

2. ApplicationContext接口

Spring框架的主要特性之一是IoC(控制反转)容器。Spring IoC容器负责管理应用程序的对象。它使用依赖注入来实现控制反转。

BeanFactory和ApplicationContext接口代表Spring IoC容器。 这里,BeanFactory是访问Spring容器的顶层接口。它提供了管理bean的基本功能。

另一方面,ApplicationContext是BeanFactory的子接口。因此,它提供了BeanFactory之外的额外功能。

此外,它提供了更多企业级应用特定的功能。 ApplicationContext的重要特性是消息解析、支持国际化、发布事件和应用层特定的上下文。 这就是我们使用它作为默认Spring容器的原因。

3. 什么是Spring Bean?

在我们深入介绍ApplicationContext容器之前,了解Spring bean很重要。 在Spring中,bean是Spring容器实例化、组装和管理的对象

那么我们应该将应用程序的所有对象都配置为Spring bean吗?作为最佳实践,我们不应该这样做。

一般来说,根据Spring文档,我们应该为Service层对象、数据访问对象(DAO)、表示对象、 基础设施对象(如Hibernate SessionFactories、JMS队列等)定义bean。

此外,通常情况下,我们不应该在容器中配置细粒度的域对象。创建和加载域对象通常是DAO和业务逻辑的职责。

现在,让我们定义一个简单的Java类,我们在本文中将其用作Spring bean:

public class AccountService {

    @Autowired
    private AccountRepository accountRepository;

    // getters and setters ...
}

4. 在容器中配置bean

众所周知,ApplicationContext的主要工作是管理bean。

因此,应用程序必须向ApplicationContext容器提供bean的配置。 Spring bean配置由一个或多个bean definitions组成。此外,Spring支持不同的配置bean的方式。

4.1 基于Java的配置

首先,我们从基于Java的配置开始,因为它是最流行且最受青睐的bean配置方式。它从Spring 3.0开始提供。

Java配置通常使用@Configuration类中的@Bean注解方法。 方法上的@Bean注解表明该方法创建了一个Spring bean。此外,使用@Configuration注解的类表明它包含Spring bean配置。

现在让我们创建一个配置类来将我们的AccountService类定义为Spring bean:


@Configuration
public class AccountConfig {

    @Bean
    public AccountService accountService() {
        return new AccountService(accountRepository());
    }

    @Bean
    public AccountRepository accountRepository() {
        return new AccountRepository();
    }
}

4.2 基于注解的配置

Spring 2.5引入了基于注解的配置,作为在Java中启用bean配置的第一步。

在这种方法中,我们首先通过XML配置启用基于注解的配置。然后我们在Java类、方法、构造函数或字段上使用一组注解来配置bean。 这些注解包含@Component、@Controller、@Service、@Repository、@Autowired和@Qualifier。

值得注意的是,我们也将这些注解与基于Java的配置一起使用。另外值得一提的是,Spring在每个版本中都不断地为这些注解添加更多功能。

现在让我们看一个简单的配置示例。

首先,我们将创建XML配置user-bean-config.xml以启用注解功能:

<?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/>
    <context:component-scan base-package="cn.tuyucheng.taketoday.applicationcontext"/>
</beans>

在这里,annotation-config标签启用基于注解的映射。component-scan标签告诉Spring在哪里寻找带注解的类。

其次,我们将创建UserService类并使用@Component注解将其定义为Spring bean:


@Component
public class UserService {
    // user service code
}

然后我们将编写一个简单的测试用例来测试这个配置:

class ApplicationContextUnitTest {

    @Test
    void givenClasspathXmlAppContext_whenAnnotationConfig_thenMappingSuccess() {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationcontext/user-bean-config.xml");
        UserService userService = context.getBean(UserService.class);
        assertNotNull(userService);
        context.close();
    }
}

4.3 基于XML的配置

最后,让我们看看基于XML的配置。这是在Spring中配置bean的传统方式。

显然,在这种方法中,我们在一个XML配置文件中进行所有bean映射

因此,让我们创建一个XML配置文件account-bean-config.xml,并为我们的AccountService类定义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="accountService" class="cn.tuyucheng.taketoday.applicationcontext.AccountService">
        <constructor-arg name="accountRepository" ref="accountRepository"/>
    </bean>

    <bean id="accountRepository" class="cn.tuyucheng.taketoday.applicationcontext.AccountRepository"/>
</beans>

5. ApplicationContext的类型

Spring提供了适合不同需求的不同类型的ApplicationContext容器。 这些是ApplicationContext接口的实现。因此,让我们来看看ApplicationContext的一些常见类型。

5.1 AnnotationConfigApplicationContext

首先,让我们看一下AnnotationConfigApplicationContext类,它是在Spring 3.0中引入的。 它可以将带有@Configuration、@Component和JSR-330元数据注解的类作为输入参数。

因此,让我们来看一个简单的示例,将AnnotationConfigApplicationContext容器与基于Java的配置一起使用:

class ApplicationContextUnitTest {

    @Test
    void givenAnnotationConfigAppContext_whenSpringConfig_thenMappingSuccess() {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AccountConfig.class);
        AccountService accountService = context.getBean(AccountService.class);
        assertNotNull(accountService);
        assertNotNull(accountService.getAccountRepository());
        context.close();
    }
}

5.2 AnnotationConfigWebApplicationContext

AnnotationConfigWebApplicationContext是AnnotationConfigApplicationContext的基于Web的变体

当我们在web.xml文件中配置Spring的ContextLoaderListener servlet监听器或Spring MVC DispatcherServlet时,我们可能会使用这个类。

此外,从Spring 3.0开始,我们还可以通过编程方式配置此应用程序上下文容器。我们需要做的就是实现WebApplicationInitializer接口:

public class MyWebApplicationInitializer implements WebApplicationInitializer {

    public void onStartup(ServletContext container) throws ServletException {
        AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
        context.register(AccountConfig.class);
        context.setServletContext(container);
        ServletRegistration.Dynamic servlet = container.addServlet("dispatcher", new DispatcherServlet(context));
        servlet.setLoadOnStartup(1);
        servlet.addMapping("/");
    }
}

5.3 XmlWebApplicationContext

如果我们在Web应用程序中使用基于XML的配置,我们可以使用XmlWebApplicationContext类。

事实上,配置这个容器就像AnnotationConfigWebApplicationContext类, 也就是说我们可以在web.xml中配置它,或者实现WebApplicationInitializer接口:

public class MyXmlWebApplicationInitializer implements WebApplicationInitializer {

    public void onStartup(ServletContext container) throws ServletException {
        XmlWebApplicationContext context = new XmlWebApplicationContext();
        context.setConfigLocation("/WEB-INF/spring/applicationContext.xml");
        context.setServletContext(container);
        ServletRegistration.Dynamic servlet = container.addServlet("dispatcher", new DispatcherServlet(context));
        servlet.setLoadOnStartup(1);
        servlet.addMapping("/");
    }
}

5.4 FileSystemXMLApplicationContext

我们使用FileSystemXMLApplicationContext类从文件系统或URL加载基于XML的Spring配置文件。 当我们需要以编程方式加载ApplicationContext时,此类很有用。通常,测试工具和独立应用程序是这方面的可能用例。

例如,让我们看看如何创建这个Spring容器并为基于XML的配置加载bean:

class ApplicationContextUnitTest {

    @Test
    void givenFileXmlAppContext_whenXMLConfig_thenMappingSuccess() {
        String path = "D:/java-workspace/intellij-workspace/java-development-practice/spring-framework-core-4/src/test/resources/applicationcontext/user-bean-config.xml";
        FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext(path);
        AccountService accountService = context.getBean("accountService", AccountService.class);
        assertNotNull(accountService);
        assertNotNull(accountService.getAccountRepository());
        context.close();
    }
}

5.5 ClassPathXmlApplicationContext

如果我们想从类路径加载XML配置文件,我们可以使用ClassPathXmlApplicationContext类。 与FileSystemXMLApplicationContext类似,它对于测试工具以及嵌入在JAR中的应用程序上下文很有用。

以下是使用这个类的例子:

class ApplicationContextUnitTest {

    @Test
    void givenClasspathXmlAppContext_whenXMLConfig_thenMappingSuccess() {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationcontext/account-bean-config.xml");
        AccountService accountService = context.getBean("accountService", AccountService.class);
        assertNotNull(accountService);
        assertNotNull(accountService.getAccountRepository());
        context.close();
    }
}

6. ApplicationContext的其他功能

6.1 消息解析

ApplicationContext接口通过继承MessageSource接口支持消息解析和国际化。 此外,Spring提供了两个MessageSource实现,分别是ResourceBundleMessageSource和StaticMessageSource。

我们可以使用StaticMessageSource以编程方式将消息添加到源;但是,它支持基本的国际化,并且更适合测试而不是生产使用。

另一方面,ResourceBundleMessageSource是MessageSource最常见的实现。它依赖于底层JDK的ResourceBundle实现。 它还使用MessageFormat提供的JDK标准消息解析。

现在让我们看看如何使用MessageSource从属性文件中读取消息。

首先,我们在类路径下创建一个messages.properties文件:

account.name=TestAccount

其次,我们在AccountConfig类中添加一个bean定义:


@Configuration
public class AccountConfig {

    @Bean
    public MessageSource messageSource() {
        ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
        messageSource.setBasename("config/messages");
        return messageSource;
    }
}

第三,我们在AccountService中注入MessageSource:

public class AccountService {

    @Autowired
    private MessageSource messageSource;
}

最后,我们可以在AccountService中的任何位置使用MessageSource#getMessage方法来读取消息:

import java.util.Locale;

public class AccountService {

    @Autowired
    private MessageSource messageSource;

    public String getAccountName() {
        return messageSource.getMessage("account.name", null, Locale.US);
    }
}

Spring还提供了ReloadableResourceBundleMessageSource类,它允许从任何Spring资源位置读取文件,并支持bundle属性文件的热加载。

6.2 事件处理

ApplicationContext在ApplicationEvent类和ApplicationListener接口的帮助下支持事件处理。 它支持ContextStartedEvent、ContextStoppedEvent、ContextClosedEvent和RequestHandledEvent等内置事件。 此外,它还支持业务用例的自定义事件。

7. 总结

在本文中,我们讨论了Spring中ApplicationContext容器的各个方面。 我们还探讨了如何在ApplicationContext中配置Spring bean的不同示例。 最后,我们学习了如何创建和使用不同类型的ApplicationContext。

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

Show Disqus Comments

Post Directory

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