美文网首页Java
Spring IoC 容器

Spring IoC 容器

作者: 简单一点点 | 来源:发表于2019-02-11 20:13 被阅读1次

    IoC 是控制反转的意思,简单来说,就是创建对象的时候不是你主动创建,而是由 Spring 框架负责控制对象的生命周期和对象间的关系。

    Spring 容器是 Spring 框架的核心。容器将创建对象,把它们连接在一起,配置它们,并管理他们的整个生命周期从创建到销毁。Spring 容器使用依赖注入(DI)来管理组成一个应用程序的组件,这些对象被称为 Spring Beans。

    Spring IoC 容器的设计

    Spring IoC 容器的设计主要是基于 BeanFactory 和 ApplicationContext 两个接口,其中 ApplicationContext 是 BeanFactory 的子接口。在绝大部分工作场景下,我们都是使用 ApplicationContext 作为 Spring IoC 的容器。

    被称作 Bean 的对象是构成应用程序的支柱也是由 Spring IoC 容器管理的。bean 是一个被实例化,组装,并通过 Spring IoC 容器所管理的对象。这些 bean 是由用容器提供的配置元数据创建的,可以通过XML或者注解创建。

    Spring IoC 和 Bean 的关系如下图。

    springbean-ioc.jpg

    下面通过一个例子来加深理解。新建一个 Maven 项目,在 pom.xml 中添加 Spring 依赖。

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>com.wyk</groupId>
        <artifactId>springdemo</artifactId>
        <version>1.0-SNAPSHOT</version>
    
        <properties>
            <spring.version>4.3.18.RELEASE</spring.version>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-beans</artifactId>
                <version>${spring.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>${spring.version}</version>
            </dependency>
        </dependencies>
    </project>
    

    在项目中添加一个实体类 Drinks 。

    package com.wyk.springdemo.pojo;
    
    public class Drinks {
        private String fruit; //水果类型
        private String sugar; // 糖分描述
        private Integer size; // 型号
    
        public String getFruit() {
            return fruit;
        }
    
        public void setFruit(String fruit) {
            this.fruit = fruit;
        }
    
        public String getSugar() {
            return sugar;
        }
    
        public void setSugar(String sugar) {
            this.sugar = sugar;
        }
    
        public Integer getSize() {
            return size;
        }
    
        public void setSize(Integer size) {
            this.size = size;
        }
    
        @Override
        public String toString() {
            return "一杯型号为" + this.size + this.sugar + this.fruit;
        }
    }
    

    在 src/main/resources 下面创建一个XML配置文件 spring-cfg.xml 。在文件中定义一个和上面实体类相关的 bean 。

    <?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-4.0.xsd">
        <bean id="orangeJuice" class="com.wyk.springdemo.pojo.Drinks">
            <property name="fruit" value="橙汁" />
            <property name="sugar" value="少糖的" />
            <property name="size" value="2" />
        </bean>
    </beans>
    

    创建一个主应用程序类,调用前面的实体类。运行程序,可以打印出实体类。

    package com.wyk.springdemo;
    
    import com.wyk.springdemo.pojo.Drinks;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class MainApp {
        public static void main(String[] args) {
            // 初始化ApplicationContext 
            ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-cfg.xml");
            // 获取bean
            Drinks orange = (Drinks)ctx.getBean("orangeJuice");
            System.out.println(orange);
        }
    
    }
    

    多个 bean 之间可以相互引用。 新建实体类 JuiceMaker 。

    package com.wyk.springdemo.pojo;
    
    public class JuiceMaker {
        private String beverageShop;
        private Drinks source;
    
        public String getBeverageShop() {
            return beverageShop;
        }
    
        public void setBeverageShop(String beverageShop) {
            this.beverageShop = beverageShop;
        }
    
        public Drinks getSource() {
            return source;
        }
    
        public void setSource(Drinks source) {
            this.source = source;
        }
    
        public String makeJuice() {
            String juice = "这是一杯由" + beverageShop + "饮品店, 提供的型号为" + source.getSize()
                    + source.getSugar() + source.getFruit();
            return juice;
        }
    }
    

    在 spring-cfg.xml 中,添加新实体类的 bean , 并引用原来的 bean 。

    <bean id="juiceMaker" class="com.wyk.springdemo.pojo.JuiceMaker">
        <property name="beverageShop" value="pig" />
        <property name="source" ref="orangeJuice" />
    </bean>
    

    在主程序中获取 bean 。

    public class MainApp {
        public static void main(String[] args) {
            // 初始化ApplicationContext
            ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-cfg.xml");
    
            JuiceMaker maker = (JuiceMaker)ctx.getBean("juiceMaker");
            System.out.println(maker.makeJuice());
        }
    }
    

    运行程序,可以查看结果。

    [图片上传失败...(image-75819c-1549887547771)]

    Spring IoC 容器初始化

    Spring IoC 容器初始化包括2个步骤,即 bean 的定义和依赖注入。bean 的定义分成3步:

    • Resource定位。Spring IoC根据开发者的配置定位资源,包括通过XML或者注解等。
    • BeanDefination 的载入。将Resource定位到的信息,保存到 Bean 定义中。
    • BeanDefination 的注册。 将 BeanDefination 中的信息发布到 Spring IoC 容器中。

    三步完成后, Bean就在 Spring IoC 容器中被定义了,但并没有被初始化。 Bean 有一个配置选项 lazy-init 。其含义是是否初始化 Spring Bean 。默认值为 false ,即默认会自动化初始 Bean。 如果将其设置为 true ,那么只有会在获取的时候才会完成依赖注入。

    Spring Bean

    Bean 中可以包含如下属性。

    属性 描述
    class 这个属性是强制性的,并且指定用来创建 bean 的 bean 类。
    name 这个属性指定唯一的 bean 标识符。在基于 XML 的配置元数据中,你可以使用 ID 和/或 name 属性来指定 bean 标识符。
    scope 这个属性指定由特定的 bean 定义创建的对象的作用域。
    constructor-arg 用来注入依赖关系的。
    properties 用来注入依赖关系的
    autowiring mode 用来注入依赖关系的。
    lazy-initialization mode 延迟初始化的 bean 告诉 IoC 容器在它第一次被请求时,而不是在启动时去创建一个 bean 实例。
    initialization 方法 在 bean 的所有必需的属性被容器设置之后,调用回调方法。
    destruction 方法 当包含该 bean 的容器被销毁时,使用回调方法。

    Spring Bean 生命周期

    Spring Bean 的生命周期由 Spring IoC容器控制,包括从初始化到销毁整个过程。

    springbean-life.png

    具体步骤比较复杂,首先介绍下初始化的步骤。

    • 如果 Bean 实现了接口 BeanNameAware 的 setBeanName 方法,那么它就会调用这个方法。
    • 如果 Bean 实现了接口 BeanFactoryAware 的 setBeanFactory 方法,那么他就会调用这个方法。
    • 如果 Bean 实现了接口 ApplicationContextAware 的 setApplicatonContext 方法,且 Spring IoC 容器也必须是一个 ApplicationContext 接口的实现类,那么才会调用这个方法,否则不会调用。
    • 如果 Bean 实现了接口 BeanPostProcessor 的 postProcessBeforeInitialization 方法,那么它就会调用这个方法。
    • 如果 Bean 实现了接口 BeanFactoryPostProcessor 的 afterPropertiesSet,那么它会调用这个方法。
    • 如果 Bean 自定义了初始化方法,它就会调用已定义的初始化方法。
    • 如果 Bean 实现了接口 BeanPostProcessor 的 postProcessAfterInitialization 方法,完成了这些调用, Bean就完成了初始化。

    当服务器正常关闭,或其它事件关闭 Spring IoC 容器,它就会销毁 Bean。

    • 如果 Bean 实现了接口 DisposableBean 的 destory 方法,那么就会调用它。
    • 如果定义了自定义的销毁方法,那么就会调用它。

    需要注意的一点是,BeanPostProcessor 接口针对所有的 Bean, 而其他接口只针对单个的 Bean。

    下面修改前面的项目,进行测试。

    创建 BeanPostProcessor 的实现类。

    package com.wyk.springdemo.pojo;
    
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.config.BeanPostProcessor;
    
    public class BeanPostProcessorImpl implements BeanPostProcessor {
    
        public Object postProcessBeforeInitialization(Object bean,  String beanName)
            throws BeansException {
            System.out.println("[" + bean.getClass().getSimpleName() + "]对象" + beanName
                + "开始实例化");
            return bean;
        }
    
        public Object postProcessAfterInitialization(Object bean,  String beanName)
                throws BeansException {
            System.out.println("[" + bean.getClass().getSimpleName() + "]对象" + beanName
                    + "实例化完成");
            return bean;
        }
    }
    

    修改 JuiceMaker 实体类, 添加相关接口。

    package com.wyk.springdemo.pojo;
    
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.*;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationContextAware;
    
    public class JuiceMaker implements BeanNameAware, BeanFactoryAware, ApplicationContextAware,
            InitializingBean, DisposableBean {
        private String beverageShop;
        private Drinks source;
    
        public String getBeverageShop() {
            return beverageShop;
        }
    
        public void setBeverageShop(String beverageShop) {
            this.beverageShop = beverageShop;
        }
    
        public Drinks getSource() {
            return source;
        }
    
        public void setSource(Drinks source) {
            this.source = source;
        }
    
        public void init() {
            System.out.println("[" + this.getClass().getSimpleName() + "]执行自定义初始化方法");
        }
    
        public void myDestroy() {
            System.out.println("[" + this.getClass().getSimpleName() + "]执行自定义销毁方法");
        }
    
        public String makeJuice() {
            String juice = "这是一杯由" + beverageShop + "饮品店, 提供的型号为" + source.getSize()
                    + source.getSugar() + source.getFruit();
            return juice;
        }
    
        public void setBeanName(String arg0) {
            System.out.println("[" + this.getClass().getSimpleName()
                + "]调用BeanNameAware接口的setBeanName方法");
        }
    
        public void setBeanFactory(BeanFactory arg0) throws BeansException {
            System.out.println("[" + this.getClass().getSimpleName()
                    + "]调用BeanFactoryAware接口的setBeanFactory方法");
        }
    
        public void setApplicationContext(ApplicationContext arg0) throws BeansException {
            System.out.println("[" + this.getClass().getSimpleName()
                    + "]调用ApplicationContextAware接口的setApplicationContext方法");
        }
    
        public void afterPropertiesSet() throws Exception {
            System.out.println("[" + this.getClass().getSimpleName()
                    + "]调用InitializingBean接口的afterPropertiesSet方法");
        }
    
        public void destroy() {
            System.out.println("调用DisposableBean的destroy方法");
        }
    }
    

    修改 Bean 的配置文件 spring-cfg.xml, 添加 beanPostProcessor 和实体的初始化和销毁方法。

    <?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-4.0.xsd">
        <!-- BeanPostProcessor定义 -->
        <bean id="beanPostProcessor" class="com.wyk.springdemo.pojo.BeanPostProcessorImpl" />
        <bean id="orangeJuice" class="com.wyk.springdemo.pojo.Drinks">
            <property name="fruit" value="橙汁" />
            <property name="sugar" value="少糖的" />
            <property name="size" value="2" />
        </bean>
        <bean id="juiceMaker" class="com.wyk.springdemo.pojo.JuiceMaker"
            init-method="init" destroy-method="myDestroy">
            <property name="beverageShop" value="pig" />
            <property name="source" ref="orangeJuice" />
        </bean>
    </beans>
    

    修改主程序进行测试。

    package com.wyk.springdemo;
    
    import com.wyk.springdemo.pojo.JuiceMaker;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class MainApp {
        public static void main(String[] args) {
            // 改为ClassPathXmlApplicationContext,方便后面关闭
            ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("spring-cfg.xml");
    
            JuiceMaker maker = (JuiceMaker)ctx.getBean("juiceMaker");
            System.out.println(maker.makeJuice());
            //关闭
            ctx.close();
        }
    }
    

    运行程序,查看控制台的输出。

    二月 06, 2019 11:01:51 上午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
    信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@156ce6a: startup date [Wed Feb 06 11:01:51 CST 2019]; root of context hierarchy
    二月 06, 2019 11:01:51 上午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
    信息: Loading XML bean definitions from class path resource [spring-cfg.xml]
    [Drinks]对象orangeJuice开始实例化
    [Drinks]对象orangeJuice实例化完成
    [JuiceMaker]调用BeanNameAware接口的setBeanName方法
    [JuiceMaker]调用BeanFactoryAware接口的setBeanFactory方法
    [JuiceMaker]调用ApplicationContextAware接口的setApplicationContext方法
    [JuiceMaker]对象juiceMaker开始实例化
    [JuiceMaker]调用InitializingBean接口的afterPropertiesSet方法
    [JuiceMaker]执行自定义初始化方法
    [JuiceMaker]对象juiceMaker实例化完成
    这是一杯由pig饮品店, 提供的型号为2少糖的橙汁
    二月 06, 2019 11:01:52 上午 org.springframework.context.support.ClassPathXmlApplicationContext doClose
    信息: Closing org.springframework.context.support.ClassPathXmlApplicationContext@156ce6a: startup date [Wed Feb 06 11:01:51 CST 2019]; root of context hierarchy
    调用DisposableBean的destroy方法
    [JuiceMaker]执行自定义销毁方法
    
    Process finished with exit code 0
    

    相关文章

      网友评论

        本文标题:Spring IoC 容器

        本文链接:https://www.haomeiwen.com/subject/dmhceqtx.html