zoukankan      html  css  js  c++  java
  • Spring02_基于XML的IOC

    本教程源码请访问:tutorial_demo

    上篇教程我们学习了如何使用工厂模式解耦,把对象的创建由程序员交给自定义的工厂类,在这篇教程我们将学到如何使用Spring的IOC解决程序的耦合问题。

    一、什么是IOC

    IOC:Inversion of Control,控制反转,将创建对象的权力交给框架。过去创建对象由开发人员通过new的方式创建,有了IOC之后,开发人员不需要new了,只需要从Spring容器(我们可以认为是保存对象的容器)中获取就可以了,创建对象的控制权发生了转移,由开发人员转移给了Spring容器或者说转移给了Spring框架。这种控制权的转移,我们称之为控制反转。

    目的:减少计算机程序的耦合,解除代码之间的依赖关系。

    二、使用IOC(第一个Spring程序)

    2.1、创建项目

    1. 在Idea中新建Maven工程;

    2. 工程创建完成后添加相应的坐标。

      <?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>org.example</groupId>
          <artifactId>ioc</artifactId>
          <version>1.0-SNAPSHOT</version>
          <packaging>jar</packaging>
      
          <dependencies>
              <dependency>
                  <groupId>org.springframework</groupId>
                  <artifactId>spring-context</artifactId>
                  <version>5.2.6.RELEASE</version>
              </dependency>
          </dependencies>
      </project>
      

    2.2、添加相关类

    2.2.1、创建持久层接口

    package org.codeaction.dao;
    
    public interface IAccountDao {
        void saveAccount();
    }
    

    2.2.2、创建持久层接口实现类

    package org.codeaction.dao.impl;
    
    import org.codeaction.dao.IAccountDao;
    
    public class AccountDaoImpl implements IAccountDao {
        @Override
        public void saveAccount() {
            System.out.println("账户保存成功");
        }
    }
    

    2.2.3、创建业务层接口

    package org.codeaction.service;
    
    public interface IAccountService {
        void saveAccount();
    }
    

    2.2.4、创建业务层接口实现类

    package org.codeaction.service.impl;
    
    import org.codeaction.dao.IAccountDao;
    import org.codeaction.dao.impl.AccountDaoImpl;
    import org.codeaction.service.IAccountService;
    
    public class AccountServiceImpl implements IAccountService {
        private IAccountDao accountDao = new AccountDaoImpl();
        @Override
        public void saveAccount() {
            accountDao.saveAccount();
        }
    }
    

    注意这个类的实现,本教程最后会说明。

    2.3、添加XML配置文件

    XML文件在resource目录下。

    <?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
            https://www.springframework.org/schema/beans/spring-beans.xsd">
        <!--把对象的创建交给spring来管理-->
        <bean id="accountDao" class="org.codeaction.dao.impl.AccountDaoImpl"></bean>
        <bean id="accountService" class="org.codeaction.service.impl.AccountServiceImpl"></bean>
    </beans>
    

    bean标签作用:配置让Spring创建对象。默认情况下调用无参构造函数,如果没有无参构造函数则不能创建成功。

    bean标签属性

    • id:为对象在容器中提供一个唯一标识,用于获取对象;
    • class:指定类的全限定类名,用于反射创建对象,默认情况下调用无参构造函数;
    • scope:指定对象的作用范围,默认是singleton(单例),3.1中会讲到;
    • init-method:指定类中的初始化方法名称;
    • destroy-method:指定类中销毁方法名称。

    2.4、添加测试类

    创建带有main方法的类,用来进行测试

    package org.codeaction.ui;
    
    import org.codeaction.dao.IAccountDao;
    import org.codeaction.service.IAccountService;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class AccountUI {
        public static void main(String[] args) {
            //1.获取核心容器对象
            ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
            //2.根据id获取Bean对象,这个id是在bean标签中配置的id
            IAccountService accountService = (IAccountService) context.getBean("accountService");
            IAccountDao accountDao = context.getBean("accountDao", IAccountDao.class);
    
            System.out.println(accountService);
            System.out.println(accountDao);
            accountService.saveAccount();
        }
    }
    

    运行main方法,控制台输出如下:

    org.codeaction.service.impl.AccountServiceImpl@754ba872
    org.codeaction.dao.impl.AccountDaoImpl@146ba0ac
    账户保存成功
    

    2.4.1、BeanFactory和ApplicationContext的区别

    1. BeanFactory是Spring容器中的顶层接口,ApplicationContext是它的子接口;
    2. ApplicationContext只要一读取配置文件,默认情况下就会创建对象;
    3. BeanFactory什么时候使用时候创建对象;
    4. ApplicationContext用来创建单例对象,BeanFactory用来创建多例对象。

    2.4.2、ApplicationContext接口的实现类

    1. ClassPathXmlApplicationContext可以加载类路径下的配置文件,要求配置文件必须在类路径下,不在的话,加载不了。
    2. FileSystemXmlApplicationContext可以加载磁盘任意路径下的配置文件(必须有访问权限)
    3. AnnotationConfigApplicationContext用于读取注解创建容器的,后面的文章会讲到。

    三、深入说明

    3.1、bean的作用范围和生命周期

    bean的作用范围由bean标签中的scope属性设置,scope属性可以有如下值:

    • singleton:默认值,单例的;
    • prototype:多例的;
    • request:web项目中,Spring创建一个Bean的对象,将对象存入到request域中;
    • session:web项目中,Spring创建一个Bean的对象,将对象存入到session域中;
    • global session:web项目中,应用在Portlet环境。如果没有Portlet环境那么globalSession相当于session。

    下面我们重点说一下singleton和prototype。

    3.1.1、singleton

    一个应用只有一个对象的实例,它的作用范围就是整个应用。
    生命周期:

    • 对象出生:当应用加载,创建容器时,对象就被创建了;
    • 对象活着:只要容器在,对象一直活着;
    • 对象死亡:当应用卸载,销毁容器时,对象就被销毁了。

    3.1.2、prototype

    每次访问对象时,都会重新创建对象实例。
    生命周期:

    • 对象出生:当使用对象时,创建新的对象实例;
    • 对象活着:只要对象在使用中,就一直活着;
    • 对象死亡:当对象长时间不用时,被Java的垃圾回收器回收了。

    3.1.3、案例(代码基于第一个Spring程序)

    3.1.3.1、修改业务层实现类

    添加init和destroy方法

    package org.codeaction.dao.impl;
    
    import org.codeaction.dao.IAccountDao;
    
    public class AccountDaoImpl implements IAccountDao {
        @Override
        public void saveAccount() {
            System.out.println("账户保存成功");
        }
    
        public void init() {
            System.out.println("dao init");
        }
    
        public void destroy() {
            System.out.println("dao destroy");
        }
    }
    
    3.1.3.2、修改持久层实现类

    添加init和destroy方法

    package org.codeaction.dao.impl;
    
    import org.codeaction.dao.IAccountDao;
    
    public class AccountDaoImpl implements IAccountDao {
        @Override
        public void saveAccount() {
            System.out.println("账户保存成功");
        }
    
        public void init() {
            System.out.println("dao init");
        }
    
        public void destroy() {
            System.out.println("dao destroy");
        }
    }
    
    3.1.3.3、修改XML配置文件
    <?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
            https://www.springframework.org/schema/beans/spring-beans.xsd">
        <!--把对象的创建交给spring来管理-->
        <!--配置bean时指定初始化和销毁方法及作用范围-->
        <bean
                id="accountDao"
                class="org.codeaction.dao.impl.AccountDaoImpl"
                scope="singleton"
                init-method="init"
                destroy-method="destroy"></bean>
        <bean
                id="accountService"
                class="org.codeaction.service.impl.AccountServiceImpl"
                scope="prototype"
                init-method="init"
                destroy-method="destroy"></bean>
    </beans>
    
    3.1.3.4、修改测试类
    package org.codeaction.ui;
    
    import org.codeaction.dao.IAccountDao;
    import org.codeaction.service.IAccountService;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class AccountUI {
        public static void main(String[] args) {
            //1.获取核心容器对象
            ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
            //2.根据id获取Bean对象,这个id是在bean标签中配置的id
            IAccountService accountService1 = (IAccountService) context.getBean("accountService");
            IAccountDao accountDao1 = (IAccountDao) context.getBean("accountDao");
            IAccountService accountService2 = (IAccountService) context.getBean("accountService");
            IAccountDao accountDao2 = (IAccountDao) context.getBean("accountDao");
    
            System.out.println("accountDao1 == accountDao2 ? " + (accountDao1 == accountDao2));
            System.out.println("accountService1 == accountService2 ? " + (accountService1 == accountService2));
            //容器销毁
            context.close();
        }
    }
    

    单步调试该程序,输出如下:

    dao init
    service init
    service init
    accountDao1 == accountDao2 ? true
    accountService1 == accountService2 ? false
    dao destroy
    

    通过输出我们验证了:

    • singleton的bean对象在容器中只会创建一次,并且创建容器时,就被创建了;
    • prototype的bean对象在容器中能够创建多次,当使用时,就创建新的对象;
    • singleton的bean对象在容器销毁时,也被销毁;
    • prototype的bean对象会被垃圾回收(通过控制台观察不到)。

    3.2、实例化bean的三种方式

    3.2.1、三种方式说明

    1. 使用默认无参构造函数;
    2. 使用静态工厂的方法创建对象;
    3. 使用实例工厂的方法创建对象。

    3.2.2、案例(代码基于第一个Spring程序)

    3.2.2.1、创建静态工厂类
    package org.codeaction.factory;
    
    import org.codeaction.service.IAccountService;
    import org.codeaction.service.impl.AccountServiceImpl;
    
    /**
     * 模拟一个静态工厂,创建业务层实现类
     */
    public class StaticFactory {
        public static IAccountService  createAccountService() {
            return new AccountServiceImpl();
        }
    }
    
    3.2.2.2、创建实例工厂类
    package org.codeaction.factory;
    
    import org.codeaction.service.IAccountService;
    import org.codeaction.service.impl.AccountServiceImpl;
    
    /**
     * 模拟一个实例工厂,创建业务层实现类
     * 此工厂创建对象,必须现有工厂实例对象,再调用方法
     */
    public class InstanceFactory {
        public IAccountService createAccountService(){
            return new AccountServiceImpl();
        }
    }
    
    3.2.2.3、修改XML配置文件
    <?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
            https://www.springframework.org/schema/beans/spring-beans.xsd">
        <!-- 方式1:使用默认的无参构造方式 -->
        <bean id="accountService1" class="org.codeaction.service.impl.AccountServiceImpl"></bean>
    
        <!--
            方式2:使用静态工厂的方法创建对象
            id属性:指定bean的id,用于从容器中获取
            class属性:指定静态工厂的全限定类名
            factory-method属性:指定生产对象的静态方法
        -->
        <bean
    	id="accountService2"
            class="org.codeaction.factory.StaticFactory"
            factory-method="createAccountService"></bean>
        <!--
            方式3:使用实例工厂的方法创建对象
            先把工厂的创建交给spring来管理。
            然后在使用工厂的bean来调用里面的方法。
            factory-bean属性:用于指定实例工厂bean的id。
            factory-method属性:用于指定实例工厂中创建对象的方法。
        -->
        <bean id="factory" class="org.codeaction.factory.InstanceFactory"></bean>
        <bean id="accountService3" factory-bean="factory" factory-method="createAccountService"></bean>
    </beans>
    
    3.2.2.4、修改测试类
    package org.codeaction.ui;
    
    import org.codeaction.dao.IAccountDao;
    import org.codeaction.service.IAccountService;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class AccountUI {
        public static void main(String[] args) {
            //1.获取核心容器对象
            ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
            //2.根据id获取Bean对象,这个id是在bean标签中配置的id
            IAccountService accountService1 = (IAccountService) context.getBean("accountService1");
            IAccountService accountService2 = (IAccountService) context.getBean("accountService2");
            IAccountService accountService3 = (IAccountService) context.getBean("accountService3");
    
            System.out.println(accountService1);
            System.out.println(accountService2);
            System.out.println(accountService3);
        }
    }
    

    运行测试类,控制台输出如下:

    org.codeaction.service.impl.AccountServiceImpl@42dafa95
    org.codeaction.service.impl.AccountServiceImpl@6500df86
    org.codeaction.service.impl.AccountServiceImpl@402a079c
    

    四、目前存在的问题

    通过这篇教程的学习,我们对IOC有了一个初步的认识。通过IOC将对象创建的权力交给Spring容器,实现控制权的转移。本篇教程2.2.4中,创建业务层接口的实现类,里面的属性依然使用new对象的方式赋值,在这里依然没有解耦,service和dao的对象依然搅在一起,那么怎么解决这个问题呢?下一篇我们将学习DI(依赖注入),DI就可以解决目前存在的问题。

  • 相关阅读:
    Android Push Notification实现信息推送使用
    线段树 Interval Tree
    树状数组
    LCA和RMQ
    RMQ (Range Minimal Query) 问题 ,稀疏表 ST
    winner tree 胜者树
    ORA-64379: Action cannot be performed on the tablespace assigned to FastStart while the feature is enabled
    mybatis 查询优化主子表查询之association和collection
    Oracle 11gR2 用户重命名(rename user)
    redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketException: 断开的管道 (Write failed)
  • 原文地址:https://www.cnblogs.com/codeaction/p/12953274.html
Copyright © 2011-2022 走看看