zoukankan      html  css  js  c++  java
  • 二、IOC控制反转

    1、什么是 IOC?

      IOC-Inversion of Control,即控制反转。它不是什么技术,而是一种设计思想。

      传统的创建对象的方法是直接通过 new 关键字,而 spring 则是通过 IOC 容器来创建对象,也就是说我们将创建对象的控制权交给了 IOC 容器。我们可以用一句话来概括 IOC:

      IOC 让程序员不在关注怎么去创建对象,而是关注与对象创建之后的操作,把对象的创建、初始化、销毁等工作交给spring容器来做。

    2、分享Bromon的blog上对IoC与DI浅显易懂的讲解

      IoC(Inversion of Control,控制反转)。这是spring的核心,贯穿始终。所谓IoC,对于spring框架来说,就是由spring来负责控制对象的生命周期和对象间的关系。这是什么意思呢,举个简单的例子,我们是如何找女朋友的?常见的情况是,我们到处去看哪里有长得漂亮身材又好的mm,然后打听她们的兴趣爱好、qq号、电话号、ip号、iq号………,想办法认识她们,投其所好送其所要,然后嘿嘿……这个过程是复杂深奥的,我们必须自己设计和面对每个环节。传统的程序开发也是如此,在一个对象中,如果要使用另外的对象,就必须得到它(自己new一个,或者从JNDI中查询一个),使用完之后还要将对象销毁(比如Connection等),对象始终会和其他的接口或类藕合起来。

      那么IoC是如何做的呢?有点像通过婚介找女朋友,在我和女朋友之间引入了一个第三者:婚姻介绍所。婚介管理了很多男男女女的资料,我可以向婚介提出一个列表,告诉它我想找个什么样的女朋友,比如长得像李嘉欣,身材像林熙雷,唱歌像周杰伦,速度像卡洛斯,技术像齐达内之类的,然后婚介就会按照我们的要求,提供一个mm,我们只需要去和她谈恋爱、结婚就行了。简单明了,如果婚介给我们的人选不符合要求,我们就会抛出异常。整个过程不再由我自己控制,而是有婚介这样一个类似容器的机构来控制。Spring所倡导的开发方式就是如此,所有的类都会在spring容器中登记,告诉spring你是个什么东西,你需要什么东西,然后spring会在系统运行到适当的时候,把你要的东西主动给你,同时也把你交给其他需要你的东西。所有的类的创建、销毁都由 spring来控制,也就是说控制对象生存周期的不再是引用它的对象,而是spring。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被spring控制,所以这叫控制反转。

    3、Spring 容器创建对象的三种方式

      第一步:创建工程,然后导入相应的 jar 包,如下图:(详情参见上面的源码下载)

       

      第二步:创建测试对象 HelloIoc

    1
    2
    3
    4
    5
    6
    7
    8
    package com.ys.ioc;
    //这是测试对象,我们通过 IOC 来创建对象
    public class HelloIoc {
         
        public void sayHello(){
            System.out.println("Hello IOC");
        }
    }

      传统的创建对象的方法:new 关键字

    1
    2
    3
    4
    5
    6
    //传统的创建对象方法----new
        @Test
        public void testTradition(){
            HelloIoc hello = new HelloIoc();
            hello.sayHello();
        }

      

      这里通过 Spring 容器怎么来创建呢?

      第一种方法:利用默认的构造方法

      在 src 目录下新建 applicationContext.xml 文件,这是 spring 配置文件,添加如下代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <?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">
        <!--
        创建对象的第一种方式:利用无参构造器
        id:唯一标识符
        class:类的全类名
          -->
        <bean id="helloIoc" class="com.ys.ioc.HelloIoc" ></bean>  
        <!-- 别名属性  name:和bean的 id 属性对应 -->
        <alias name="helloIoc" alias="helloIoc2"/>
         
    </beans>

      测试代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    /**
         *  Spring 容器利用构造函数创建对象
         */
        @Test
        public void testCreateObjectByConstrutor(){
            //1、启动 spring 容器
            ApplicationContext context =
                    new ClassPathXmlApplicationContext("applicationContext.xml");
            //2、从 spring 容器中取出数据
            HelloIoc IOC = (HelloIoc) context.getBean("helloIoc");
            //3、通过对象调用方法
            IOC.sayHello();
             
            //利用配置文件 alias 别名属性创建对象
            HelloIoc IOC2 = (HelloIoc) context.getBean("helloIoc2");
            IOC2.sayHello();
        }

      我们可以在测试类 HelloIoc.java 中手动添加无参的构造方法,然后执行上面的测试代码,会发现构造方法会在 sayHello()方法执行之前调用。

      第二种方法:利用静态工厂方法

      首先创建静态工厂类 HelloStaticFactory.java

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    package com.ys.ioc;
     
    public class HelloStaticFactory {
        public HelloStaticFactory(){
            System.out.println("HelloStaticFactory constructor");
        }
        //静态工厂方法
        public static HelloIoc getInstances(){
            return new HelloIoc();
        }
     
    }

      接着在 applicationContext.xml 中进行如下配置:

    1
    2
    3
    4
    5
    6
    <!--
            创建对象的第二种方式:利用静态工厂方法
            factory-method:静态工厂类的获取对象的静态方法
            class:静态工厂类的全类名
          -->   
        <bean id="helloStaticFactory" factory-method="getInstances" class="com.ys.ioc.HelloStaticFactory"></bean>

      编写测试类:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    /**
         * Spring 容器利用静态工厂方法创建对象
         */
        @Test
        public void createObjectStaticFactory(){
            ApplicationContext context =
                    new ClassPathXmlApplicationContext("applicationContext.xml");
            HelloIoc staticFactory =
                    (HelloIoc) context.getBean("helloStaticFactory");
            staticFactory.sayHello();
        }

      注意:spring容器只负责调用静态工厂方法,而这个静态工厂方法内部实现由程序员完成

      第三种方法:利用实例工厂方法

      首先创建实例工厂类 HelloInstanceFactory .java

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    package com.ys.ioc;
     
    public class HelloInstanceFactory {
        public HelloInstanceFactory(){
            System.out.println("实例工厂方法构造函数");
        }
     
        //利用实例工厂方法创建对象
        public HelloIoc getInstance(){
            HelloIoc instanceIoc = new HelloIoc();
            return instanceIoc;
        }
    }

      接着在 applicationContext.xml 中进行如下配置:

    1
    2
    3
    4
    5
    6
    7
    8
    <!--
            创建对象的第三种方式:利用实例工厂方法
            factory-bean:指定当前Spring中包含工厂方法的beanID
            factory-method:工厂方法名称
          --> 
        <bean id="instanceFactory" class="com.ys.ioc.HelloInstanceFactory"></bean> 
        <bean id="instance" factory-bean="instanceFactory" factory-method="getInstance"></bean> 
      

      最后编写测试类:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    /**
         * Spring 容器利用实例工厂方法创建对象
         */
        @Test
        public void createObjectInstanceFactory(){
            ApplicationContext context =
                    new ClassPathXmlApplicationContext("applicationContext.xml");
            HelloIoc staticFactory =
                    (HelloIoc) context.getBean("instance");
            staticFactory.sayHello();
        }

      

    4、Spring 容器创建对象的时机

      第一种:默认情况下,启动 spring 容器便创建对象(遇到bean便创建对象)

      测试:

      第一步:我们在 HelloIoc.java 中添加默认构造方法:

      

      第二步:在 applicationContext.xml 文件中添加 bean(由于上面我们通过三种方式来创建对象了,里面已经有三个bean了)

      第三步:启动 Spring 容器,查看无参构造函数的打印次数

      

      控制台打印结果如下:

      

       第二种:在spring的配置文件bean中有一个属性 lazy-init="default/true/false"

         ①、如果lazy-init为"default/false"在启动spring容器时创建对象(默认情况)

         ②、如果lazy-init为"true",在context.getBean时才要创建对象

       我们测试 lazy-init=“true”的情况

      

      我们测试通过断点调试:

      

      然后继续往下面执行:

      

       在第一种情况下可以在启动spring容器的时候,检查spring容器配置文件的正确性,如果再结合tomcat,如果spring容器不能正常启动,整个tomcat就不能正常启动。但是这样的缺点是把一些bean过早的放在了内存中,如果有数据,则对内存来是一个消耗。

      反过来,在第二种情况下,可以减少内存的消耗,但是不容易发现错误

    5、spring的bean中的scope:"singleton/prototype/request/session/global session" 

       一、默认scope的值是singleton,即产生的对象是单例的

       applicationContext.xml 文件中配置:

    1
    <bean id="helloIoc" scope="singleton" class="com.ys.ioc.HelloIoc" ></bean>

      验证:

    1
    2
    3
    4
    5
    6
    7
    8
    //spring 容器默认产生对象是单例的 scope="singleton"
        @Test
        public void test_scope_single_CreateObject(){
            ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
            HelloIoc hello1 = (HelloIoc) context.getBean("helloIoc");
            HelloIoc hello2 = (HelloIoc) context.getBean("helloIoc");
            System.out.println(hello1.equals(hello2)); //true
        }

      二、scope=“prototype”

      多例模式,并且spring容器启动的时候并不会创建对象,而是在得到 bean 的时候才会创建对象

       applicationContext.xml 文件中配置:

    1
    <bean id="helloIoc" scope="prototype" class="com.ys.ioc.HelloIoc" ></bean>

      验证:

    1
    2
    3
    4
    5
    6
    7
    8
    //spring 容器默认产生对象是单例的 scope="prototype"
        @Test
        public void test_scope_prototype_CreateObject(){
            ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
            HelloIoc hello1 = (HelloIoc) context.getBean("helloIoc");
            HelloIoc hello2 = (HelloIoc) context.getBean("helloIoc");
            System.out.println(hello1.equals(hello2)); //false
        }

      

      总结:在单例模式下,启动 spring 容器,便会创建对象;在多例模式下,启动容器并不会创建对象,获得 bean 的时候才会创建对象

    5、Spring 容器生命周期

      创建 SpringLifeCycle.java

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    package com.ys.ioc;
    /**
     * Spring 容器的生命周期
     * @author hadoop
     *
     */
    public class SpringLifeCycle {
        public SpringLifeCycle(){
            System.out.println("SpringLifeCycle");
        }
        //定义初始化方法
        public void init(){
            System.out.println("init...");
        }
        //定义销毁方法
        public void destroy(){
            System.out.println("destroy...");
        }
         
        public void sayHello(){
            System.out.println("say Hello...");
        }
    }

      applicationContext.xml 

    1
    2
    <!-- 生命周期 -->
        <bean id="springLifeCycle" init-method="init" destroy-method="destroy" class="com.ys.ioc.SpringLifeCycle"></bean>

      测试:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    //spring 容器的初始化和销毁
        @Test
        public void testSpringLifeCycle(){
            ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
            SpringLifeCycle hello = (SpringLifeCycle) context.getBean("springLifeCycle");
             
            hello.sayHello();
             
            //销毁spring容器
            ClassPathXmlApplicationContext classContext = (ClassPathXmlApplicationContext) context;
            classContext.close();
        }

      

      控制台打印如下:

      

      分析:spring 容器的声明周期

           1、spring容器创建对象
              2、执行init方法
              3、调用自己的方法
              4、当spring容器关闭的时候执行destroy方法

      注意:当scope为"prototype"时,调用 close() 方法时是不会调用 destroy 方法的

  • 相关阅读:
    eclipse中文乱码问题解决方案
    修改Tomcat的JDK目录
    Tomcat 5.5 修改服务器的侦听端口
    HTML DOM教程 27HTML DOM Button 对象
    HTML DOM教程 24HTML DOM Frameset 对象
    Navicat for MySQL v8.0.27 的注册码
    HTML DOM教程 25HTML DOM IFrame 对象
    Tomcat 5.5 的下载和安装
    android manifest相关属性
    ubuntu10.04 下 eclipse 小结
  • 原文地址:https://www.cnblogs.com/zhoanghua/p/9292250.html
Copyright © 2011-2022 走看看