需要解决的问题:
1.如何使用多个Spring-context的配置文件来生成Spring的上下文?
2.各种上下文的使用方式?


文章目录

  • 一.Spring容器介绍
  • 二.Spring的ApplicationContext(上下文)
  • 三.Spring中Bean的Lifecycle(生命周期)



一.Spring容器介绍

在基于Spring的应用中,你的应用对象生存于Spring容器(container)中。如图1.4所示,Spring容器负责创建对象,装配它们,配置它们并管理它们的整个生命周期,从生存到死亡(在这里,可能就是new到finalize())。

spring mvc context上下文路径 spring上下文容器_配置文件

Spring容器分成两类:

  • bean工厂容器
  • Spring上下文容器 (主要研究对象)

Spring容器并不是只有一个。Spring自带了多个容器实现,可以归为两种不同的类型。bean工厂(由org.springframework. beans. factory.eanFactory接口定义)是最简单的容器,提供基本的DI支持。应用上下文(由org.springframework.context.ApplicationContext接口定义)基于BeanFactory构建,并提供应用框架级别的服务,例如从属性文件解析文本信息以及发布应用事件给感兴趣的事件监听者。虽然我们可以在bean工厂和应用上下文之间任选一种,但bean工厂对大多数应用来说往往太低级了,因此,应用上下文要比bean工厂更受欢迎。我们会把精力集中在应用上下文的使用上,不再浪费时间讨论bean工厂。


二.Spring的ApplicationContext(上下文)

Spring自带了多种类型的应用上下文。 下面是几种常见的上下文:(5种)

  • AnnotationConfigApplicationContext: 从一个或多个基于Java的配置类中加载Spring应用上下文。
  • AnnotationConfigWebApplicationContext: 从一个或多个基于Java的配置类中加载Spring Web应用上下文。
  • ClassPathXmlApplicationContext: 从类路径下的一个或多个XML配置文件中加载上下文定义, 把应用上下文的定义文件作为类资源。
  • FileSystemXmlapplicationcontext: 从文件系统下的一个或多个XML配置文件中加载上下文定义。
  • XmlWebApplicationContext: 从Web应用下的一个或多个XML配置文件中加载上下文定义。

Web应用中两种模式:(第8章节详细介绍)

  • 1.AnnotationConfigWebApplicationContext
  • 2.XmlWebApplicationContext

非Web下的三种使用方式如下:

首先看一下本次测试的项目结构:

spring mvc context上下文路径 spring上下文容器_spring_02

每个测试都会有一个 spring-content-4XXX.xml 的配置,其基本内容如下,每个配置文件只是user实例属性中name的注入值有区别,这里是为了方便观察输出的测试结果

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

	<bean id="user" class="com.yveshe.chapter1.User">
		<constructor-arg name="name" value="yveshe-XXX" />
		<constructor-arg name="age" value="18" />
		<constructor-arg name="role" ref="role" />
	</bean>

	<bean id="role" class="com.yveshe.chapter1.Role">
		<property name="id" value="4d53a9ab-e858-4369-8240-46dd497ace3e"></property>
		<property name="name" value="系统管理员"></property>
		<property name="desc" value="拥有整个系统的管理权限"></property>
	</bean>

</beans>

  • 1.文件系统的方式:
ApplicationContext context = new FileSystemXmlApplicationContext("spring-content-4xxx.xml");

资源存放结构如下:

spring mvc context上下文路径 spring上下文容器_配置文件_03

测试类和测试结果如下:(注意观察name属性的值)

/**
 * 从文件系统下的一个或多个XML配置文件中加载上下文定义
 *
 * @author YvesHe
 *
 */
public class FileSystemXmlapplicationcontextTest {

    public static void main(String[] args) {
        String configLocation = "conf//chapter1//spring-content-4FileSystemXmlapplicationcontextTest.xml"; // 相对路径,也支持绝对路径
        configLocation = "D://git//spring-study//conf//chapter1//spring-content-4FileSystemXmlapplicationcontextTest.xml";
        FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext(configLocation);
        User bean = context.getBean(User.class);
        context.close();

        System.out.println(JsonUtil.toJson(bean, true)); // 输出用户信息
    }
}


/**输出结果:
{
    "age":18,
    "name":"yveshe-FileSystemXmlapplicationcontextTest",
    "role":{
        "desc":"拥有整个系统的管理权限",
        "id":"4d53a9ab-e858-4369-8240-46dd497ace3e",
        "name":"系统管理员"
    }
}
**/
  • 2.类路径方式(包含Jar中配置文件)
ApplicationContext context = new ClassPathXmlApplicationContext("spring-content-4xxx.xml");

使用FileSystemXmlApplicationContext和使用ClassPathXmlApp-licationContext的区别在于: FileSystemXmlApplicationContext在指定的文件系统路径下查找spring上下文的配置文件; 而ClassPathXmlApplicationContext是在所有的类路径(包含JAR文件)下查找spring上下文配置文件

2.1首先我们来看看直接使用classpath下的配置文件来构建Spring上下文的例子:

资源存放结构在classpath下:

spring mvc context上下文路径 spring上下文容器_配置文件_04


测试类和测试结果如下:(注意观察name属性的值)

/**
 * 类路径下的一个或多个XML配置文件中加载上下文定义
 *
 * @author YvesHe
 *
 */
public class ClassPathXmlApplicationContextTest {

    public static void main(String[] args) {
        String configLocation = "spring-content-4ClassPathXmlApplicationContextTest.xml";
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(configLocation);
        User bean = context.getBean(User.class);
        context.close();
        System.out.println(JsonUtil.toJson(bean, true)); // 输出用户信息
    }
}

/**输出结果:
{
    "age":18,
    "name":"yveshe-ClassPathXmlApplicationContextTest",
    "role":{
        "desc":"拥有整个系统的管理权限",
        "id":"4d53a9ab-e858-4369-8240-46dd497ace3e",
        "name":"系统管理员"
    }
}
**/

2.2接下里看下使用ClassPathXmlApplicationContext来读取Jar包中配置文件来构建Spring的上下文方式:

spring-context.jar中只有一个配置文件没有其他多余的内容

spring mvc context上下文路径 spring上下文容器_配置文件_05


测试代码以及结果如下: (注意观察name属性的值)

/**
 * 类路径下的一个或多个XML配置文件中加载上下文定义
 *
 * @author YvesHe
 *
 */
public class ClassPathXmlApplicationContextInJarTest {

    public static void main(String[] args) {
        String configLocation = "spring-content-4ClassPathXmlApplicationContextInJarTest.xml";
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(configLocation);
        User bean = context.getBean(User.class);
        context.close();
        System.out.println(JsonUtil.toJson(bean, true)); // 输出用户信息
    }
}

/**输出结果:
{
    "age":18,
    "name":"yveshe-ClassPathXmlApplicationContextInJarTest",
    "role":{
        "desc":"拥有整个系统的管理权限",
        "id":"4d53a9ab-e858-4369-8240-46dd497ace3e",
        "name":"系统管理员"
    }
}
**/
  • 3.Java配置类方式
    如果你想从Java配置中加载应用上下文, 那么可以使用AnnotationConfigApplicationContext, 在这里不需要指定加载Spring应用上下文所需的XML文件, 而是通过一个配置类加载bean的。配置结构如下
@Configuration
@ComponentScan(basePackageClasses = com.yveshe.chapter1.AnnotationConfigApplicationContextTest.class)
public class SpringContextConf {

    @Bean
    public Role someName() {
        Role role = new Role();
        role.setDesc("拥有整个系统的管理权限");
        role.setId("4d53a9ab-e858-4369-8240-46dd497ace3e");
        role.setName("系统管理员");
        return role;
    }

    @Bean
    public User someName2() {
        User user = new User();
        user.setAge(18);
        user.setName("yveshe-AnnotationConfigApplicationContextTest");
        user.setRole(this.someName());
        return user;
    }
}

@Configuration:
表明这个类是一个配置类, 该类应该包含在Spring应用上下文中如何创建bean的细节。

@Bean:
类似XMl配置中Bean标签的配置

@ComponentScan:
类似XML配置中配置扫描包的设置(basePackageClasses: 配置扫描包的路径为该类所在包),方法名称可以随意取.

测试代码以及结果如下: (注意观察name属性的值)

/**
 * 从一个或多个基于Java的配置类中加载Spring应用上下文
 *
 * @author YvesHe
 *
 */
public class AnnotationConfigApplicationContextTest {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringContextConf.class);
        User bean = context.getBean(User.class);
        context.close();
        System.out.println(JsonUtil.toJson(bean, true)); // 输出用户信息
    }
}


/**输出结果:
{
    "age":18,
    "name":"yveshe-AnnotationConfigApplicationContextTest",
    "role":{
        "desc":"拥有整个系统的管理权限",
        "id":"4d53a9ab-e858-4369-8240-46dd497ace3e",
        "name":"系统管理员"
    }
}
**/

三.Spring中Bean的Lifecycle(生命周期)

在传统的Java应用中, bean的生命周期很简单。 使用Java关键字new进行bean实例化, 然后该bean就可以使用了。 一旦该bean不再被使用,则由Java自动进行垃圾回收。

相比之下, Spring容器中的bean的生命周期就显得相对复杂多,下图展示的是 bean装载到Spring应用上下文中的一个典型的生命周期过程:

spring mvc context上下文路径 spring上下文容器_spring_06


bean在Spring容器中从创建到销毁经历了若干阶段, 每一阶段都可以针对Spring如何管理bean进行个性化定制.

正如你所见, 在bean准备就绪之前, bean工厂执行了若干启动步骤。接下来对上图进行详细描述:

  • 1. Spring对bean进行实例化;
  • 2. Spring将值和bean的引用注入到bean对应的属性中;
  • 3. 如果bean实现了BeanNameAware接口, Spring将bean的ID传递给setBean-Name()方法;
  • 4. 如果bean实现了BeanFactoryAware接口, Spring将调用setBeanFactory()方法, 将BeanFactory容器实例传入;
  • 5. 如果bean实现了ApplicationContextAware接口, Spring将调用setApplicationContext()方法, 将bean所在的应用上下文引用传入进来;
  • 6. 如果bean实现了BeanPostProcessor接口, Spring将调用它们的post-ProcessBeforeInitialization()方法;
  • 7. 如果bean实现了InitializingBean接口, Spring将调用它们的after-PropertiesSet()方法。 类似地, 如果bean使用initmethod声明了初始化方法, 该方法也会被调用;
  • 8. 如果bean实现了BeanPostProcessor接口, Spring将调用它们的post-ProcessAfterInitialization()方法;
  • 9. 此时, bean已经准备就绪, 可以被应用程序使用了, 它们将一直驻留在应用上下文中, 直到该应用上下文被销毁;
  • 10. 如果bean实现了DisposableBean接口, Spring将调用它的destroy()接口方法。 同样, 如果bean使用destroy-method声明了销毁方法, 该方法也会被调用。

现在你已经了解了如何创建和加载一个Spring容器。 但是一个空的容器并没有太大的价值, 在你把东西放进去之前, 它里面什么都没有。为了从Spring的DI中受益, 我们必须将应用对象装配进Spring容器中。 接下来我们将对bean装配进行更详细的探讨。