21 Spring 应用上下文
21 Spring 应用上下文
一、背景
在最初Spring使用阶段,业务代码只需要按照业务本身的流程,走啊走啊,走到哪里,需要另外的对象来协助了,就给Spring说,我想要个对象--于是Spring就很贴心的帮我们创建出对象。随着好奇心越来越重,疑问就有了Spring是从哪里把对象给我们创建出来的呢?
Spring既然要负责应用程序中那么多对象的创建、管理,就像苹果要生产那么多手机一样,肯定有一个专门创建对象的地方。类比苹果生产手机的地方叫工厂,比如富士康。但放在软件开发中,对于Spring创建对象的地方我们就不叫工厂了,而叫做容器。在Java中容器的概念最熟悉的莫过于tomcat了,它是一个运行servlet的web容器,即产生处理servlet的东西。而Spring要想实现依赖注入功能,就离不开对象生产的容器--如果没有容器负责对象的创建管理,你的程序代码只是喊要对象,Spring也无处给我们创建。实际上,容器是Spring框架实现功能的核心,容器不只是帮我们创建对象那么简单,它负责了对象整个生命周期的管理--创建、装配、销毁。关于Spring这个容器我们最常听到的一个术语就是IOC容器。所谓IOC,是一种控制反转的编程思想。总之一句话,我们应用程序不用在过问对象的创建和管理对象之间的依赖关系了,都让IOC容器给代劳了,即我们应用程序中对象的创建、管理的控制权都交给Spring容器,这是一种控制权的反转,所以Spring容器才能称为IOC容器。不过这里要理清一点:并不是说只有Spring的容器才叫IOC容器,基于IOC容器的框架还有很多,并不是Spring特有的。
容器听起来好像很厉害,😂但是实际情况上光有容器我们其实什么都干不了!!!实际上,容器里面什么都没有,决定容器里放什么对象的是我们自己,决定对象之间的依赖关系的,也是我们自己,容器只是给我们提供一个管理对象的空间而已。那么,我们怎么向容器中放入我们需要容器代为管理的对象呢?这就涉及到Spring的应用上下文了。应用上下文:我们可以这样理解:我们需要Spring帮我们管理对象即我们的对象需要放入到容器,应用上下文就是将这些对象放入容器的一种对象。比如我们常见的ApplicationContext本质上就是一个维护Bean定义以及对象之间协作关系的高级接口。Spring的核心是容器,而容器并不唯一,框架本身就提供了很多个容器的实现,大概分为两种类型:一种是不常用的BeanFactory,这是最简单容器,只能提供基本的DI功能;还有一种就是继承了BeanFactory后派生而来的应用上下文,其抽象接口也就是刚刚提到的ApplicationContext,它能提供更多企业级的服务,例如解析配置文本信息等等,这也是应用上下文实例对象最常用的应用场景。
有了上下文对象,我们就能向容器注册需要Spring管理的对象了。基于上下文抽象接口,Spring也为我们提供了多种类型的容器实现,供我们在不同的应用场景选择:
- AnnotationConfigApplicationContext:从一个或多个基于Java的配置类中加载上下文定义,适用于Java注解的方式
- ClassPathXmlApplicationContext:从类路径下的一个或多个xml配置文件中加载上下文定义,使用于xml配置的方式
- FileSystemXmlApplicationContext:从文件系统下的一个或多个xml配置文件中加载上下文定义,也就是说系统盘符中加载xml配置文件
- AnnotationConfigWebApplicationContext:专门为web应用准备的,适用于注解方式
- XmlWebApplicationContext:从web应用下的一个或多个xml配置文件加载上下文定义,应用于xml配置方式
有了以上理解,我们将需要IOC容器替我们管理的对象之间的协作关系配置好(基于xml也罢,Java注解也好),然后利用应用上下文对象加载进我们的Spring容器,容器就能为应用程序提供对象的管理服务了。
二、talk is cheap ,show me the code
2.1 基于xml的配置
有两个类QQCar、Man,分别代表QQ汽车、人;关系是人开QQ汽车。代码如下:
package com.lucky.spring.entity;
/**
 * Created by zhangdd on 2020/8/26
 */
public class Man {
    private Car car;
    public Man() {
    }
    public Man(Car car) {
        this.car = car;
    }
    public void work() {
        car.drive();
    }
}
package com.lucky.spring.entity;
import lombok.extern.slf4j.Slf4j;
/**
 * Created by zhangdd on 2020/8/26
 */
@Slf4j
public class QQCar implements Car {
    @Override
    public void drive() {
        log.info("开QQ车");
    }
}
基于xml的bean和bean之间的协作关系如下
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">
    <bean id="man" class="com.lucky.spring.entity.Man">
        <constructor-arg ref="qqCar"/>
    </bean>
    <bean id="qqCar" class="com.lucky.spring.entity.QQCar"/>
</beans>
分别作为基于类路径的xml和文件系统的xml去获取,测试代码如下:
   /**
     * 加载配置文件,获取bean
     */
    private void getBeanFromResourcesFile() {
        ApplicationContext context = new ClassPathXmlApplicationContext(new
                String[]{"applicationContext.xml"});
        Man bean = context.getBean(Man.class);
        bean.work();
        Man man = context.getBean("man", Man.class);
        man.work();
        //是单例模式创建的Bean
        log.info("bean.equals(man):{}", bean.equals(man));
        log.info("bean car status:{}", man.getCar().equals(context.getBean("qqCar")));
    }
    /**
     * FileSystemXmlApplicationContext方法对路径进行了处理:
     * 用绝对路径时,第一个斜杠会被去掉(也是不知道为什么,我勒个去),所以如果是在Linux系统或者max系统下的绝对路径需要
     * 给在绝对路径前再加个斜杠解决问题,代码通过
     * @see FileSystemXmlApplicationContext#getResourceByPath
     */
    private void getBeanFromSystemFile() {
        ApplicationContext context = new FileSystemXmlApplicationContext(
                "//Users/lucky/java/spring/21spring-context/src/main/resources/applicationContext.xml");
        Man bean = context.getBean(Man.class);
        bean.work();
        Man man = context.getBean("man", Man.class);
        man.work();
        //是单例模式创建的Bean
        log.info("bean.equals(man):{}", bean.equals(man));
        log.info("bean car status:{}", man.getCar().equals(context.getBean("qqCar")));
    }
打印结果如下:
2020-08-29 23:14:49.643  INFO 24502 --- [           main] o.s.b.f.xml.XmlBeanDefinitionReader      : Loading XML bean definitions from class path resource [applicationContext.xml]
2020-08-29 23:14:49.799  INFO 24502 --- [           main] com.lucky.spring.entity.QQCar            : 开QQ车
2020-08-29 23:14:49.799  INFO 24502 --- [           main] com.lucky.spring.entity.QQCar            : 开QQ车
2020-08-29 23:14:49.799  INFO 24502 --- [           main] com.lucky.spring.Application             : bean.equals(man):true
2020-08-29 23:14:49.799  INFO 24502 --- [           main] com.lucky.spring.Application             : bean car status:true
2020-08-29 23:15:32.846  INFO 24530 --- [           main] o.s.b.f.xml.XmlBeanDefinitionReader      : Loading XML bean definitions from file [/Users/lucky/java/spring/21spring-context/src/main/resources/applicationContext.xml]
2020-08-29 23:15:32.994  INFO 24530 --- [           main] com.lucky.spring.entity.QQCar            : 开QQ车
2020-08-29 23:15:32.994  INFO 24530 --- [           main] com.lucky.spring.entity.QQCar            : 开QQ车
2020-08-29 23:15:32.995  INFO 24530 --- [           main] com.lucky.spring.Application             : bean.equals(man):true
2020-08-29 23:15:32.995  INFO 24530 --- [           main] com.lucky.spring.Application             : bean car status:true
从上面的打印结果我们看到,已经获取到了对象间的协作关系;同时Spring容器帮我们管理的是单例模式的对象。
2.2 基于注解的配置
基于注解使用@Bean注解声明对象
package com.lucky.spring.config;
import com.lucky.spring.entity.Car;
import com.lucky.spring.entity.Man;
import com.lucky.spring.entity.QQCar;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
 * Created by zhangdd on 2020/8/26
 */
@Configuration
public class ManConfig {
    @Bean
    public Man man() {
        return new Man(car());
    }
    @Bean
    public Car car() {
        return new QQCar();
    }
}
测试代码如下:
   /**
     * 基于注解,获取bean
     */
    private void getBeanFromAnnotation() {
        ApplicationContext context = new AnnotationConfigApplicationContext(ManConfig.class);
        Man bean = context.getBean(Man.class);
        bean.work();
        Man man = context.getBean("man", Man.class);
        man.work();
        //是单例模式创建的Bean
        log.info("bean.equals(man):{}", bean.equals(man));
        log.info("bean car status:{}", man.getCar().equals(context.getBean("car")));
    }
打印结果如下:
2020-08-29 23:15:58.258  INFO 24543 --- [           main] com.lucky.spring.entity.QQCar            : 开QQ车
2020-08-29 23:15:58.258  INFO 24543 --- [           main] com.lucky.spring.entity.QQCar            : 开QQ车
2020-08-29 23:15:58.258  INFO 24543 --- [           main] com.lucky.spring.Application             : bean.equals(man):true
2020-08-29 23:15:58.258  INFO 24543 --- [           main] com.lucky.spring.Application             : bean car status:true
    
三、总结
到这里Spring容器和上下文的解释就结束了。知其然还要其所以然😂
 
                    
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号