美文网首页框架原理
Spring IoC快速入门

Spring IoC快速入门

作者: 花无缺_0159 | 来源:发表于2019-02-04 10:53 被阅读43次

    【目录】
    2.1 Spring的开发包
    2.2 开发环境测试搭建
    2.3 业务代码编写
    2.4 IoC和DI
      2.4.1 最基本的IoC控制反转的实现
      2.4.2 DI依赖注入的实现
    2.5 Spring的工厂

    2 Spring IoC快速入门

    Spring核心内容的基本开发步骤:

    • 下载开发包,导入jar包
    • 编写代码(基础代码和调用代码)
    • 编写配置文件

    2.1 Spring的开发包

    下载网址

    开发包目录结构

    2.2 开发环境测试搭建

    回顾利用Maven搭建第一个Spring测试项
    新建Web工程:spring_day1。pom.xml代码如下。

    <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/maven-v4_0_0.xsd">
      <modelVersion>4.0.0</modelVersion>
      <groupId>com.test</groupId>
      <artifactId>spring_day1</artifactId>
      <packaging>war</packaging>
      <version>0.0.1-SNAPSHOT</version>
      <name>spring_day1 Maven Webapp</name>
      <url>http://maven.apache.org</url>
      <dependencies>
        <dependency>
          <groupId>junit</groupId>
          <artifactId>junit</artifactId>
          <version>3.8.1</version>
          <scope>test</scope>
        </dependency>
       <!-- Spring依赖 -->
        <!-- 1.Spring核心依赖 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>4.3.22.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>4.3.22.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>4.3.22.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-expression</artifactId>
            <version>4.3.22.RELEASE</version>
        </dependency>
      <!-- 2.Spring 日志依赖 -->
        <!-- 日志框架 -->
        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.1.3</version>
        </dependency>
        <!-- 日志 -->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
      </dependencies>
      <build>
        <finalName>spring_day1</finalName>
      </build>
    </project>
    

    在接下来的学习中,日志统一采用log4j,日志框架根据整合情况再决定。

    2.3 业务代码编写

    采用传统方式模拟用户登录,思路如下:

    示意图

    下面开始具体编写步骤。


    在src/main/java目录下创建包和类

    具体代码见下:
    数据层接口 UserDAO.java

    package dao;
    //用户操作dao层
    public interface UserDAO {
        //从数据库查询数据
        public void findUserByUsernameAndPassword();
    }
    

    数据层实体类UserDAOImpl.java

    package dao;
    
    public class UserDAOImpl implements UserDAO{
        @Override
        public void findUserByUsernameAndPassword() {
            System.out.println("dao层,向数据库查询数据了");
        }
    
    }
    

    业务层接口UserService .java

    package service;
    //用户操作
    public interface UserService {
        //模拟用户登录
        public void login();
    }
    

    业务层实体类UserServiceImpl.java

    package service;
    
    import dao.UserDAO;
    import dao.UserDAOImpl;
    
    public class UserServiceImpl implements UserService{
    public void login() {
        System.out.println("service层被调用了");
        UserDAO userDAO = new UserDAOImpl();
        userDAO.findUserByUsernameAndPassword();
        }
    }
    

    测试类SpringTest.java

    package test;
    
    import org.junit.Test;
    
    import service.UserService;
    import service.UserServiceImpl;
    
    public class SpringTest {
        @Test
        public void test(){
            //测试serivce
            UserService userService = new UserServiceImpl();
            userService.login();
        }
    }
    

    完成以上代码以后,进行测试。

    测试结果

    虽然完成了功能,但是可以看到,代码过于耦合,上层代码过度依赖于下一层代码的实现。例如,通过业务层去查询存在于数据层的数据,先必须现在业务层准备一个数据层的实例对象。

    UserDAO userDAO = new UserDAOImpl(); 
    

    在传统的代码中,经常是这样的情况:如果有某个对象A依赖于其他对象B,在使用时,需要先创建所依赖的对象B,传入对象A,然后才能对对象A进行操作。这种方式提高了代码的耦合度,十分不利于重构和维护。(为什么要高内聚低耦合?

    为了解决这种耦合,Spring采用了IoC(Inverse of Control,控制反转)的思想。这种思想,简单来说,就是引入工厂(也称第三者/IoC容器),将原来在程序中手动创建管理的对象,交给工厂来创建管理。在Spring框架中,这个工厂就是Spring中的工厂。

    示意图

    2.4 IoC和DI

    提到IoC(控制反转),就不得不提到DI(依赖注入)。
    以下是常见的两个问题。

    Q1:怎样理解DI?
    依赖:所有类的对象,都依赖于IoC容器来进行管理。
    注入:类中的某个对象,需要某种外部资源(包括对象、资源、常量数据),无需自己去引用,IoC容器直接注入给它。这个对象不需要关心具体的资源来怎么来的,反正是IoC容器统统搞定。(提一句:Spring通过反射机制来实现注入。)
    综上,下个定义,应用程序依赖于IoC容器来注入某个对象所需要的外部资源(包括对象、资源、常量数据),这就是DI(通过配置的方式为对象赋值/初始化/注入)。

    Q2:许多人把IoC和DI这两个术语理解为同一个事物。怎样恰当的理解它们的关系?
    笔者认为,可以这样理解:
    IoC是一种思想,也是一种设计模式——把我们自己创建对象的控制权交给IoC容器,让IoC容器负责创建对象。
    在IoC容器创建过程中,有一个重点——在系统运行中,动态的向某个对象提供它所需要的其他外部资源(包括对象、资源、常量数据)。
    怎样能完成这个重点?可以通过DI来实现——给对象动态的注入外部资源(包括对象、资源、常量数据)。
    综上,DI和IoC的关系就是这样——IoC的实现需要DI技术的支持。

    PS:给对象注入一个对象,就实现了对对象间依赖关系的“一键”管理,不需要多个相互依赖的类new来new去。什么是依赖关系,看下面的示意代码。

    class B {
               private A a;   //A是B的依赖对象,两者间存在依赖关系
    }
    

    在spring_day1工程中,userService依赖于userDAO。现在,仍然使用spring_day1工程来进行下面两个小节的演示。
    2.4.1小节展示最基本的IoC控制反转的实现,通过Spring的工厂获取一个Bean,不实现动态的资源管理(UserService得到Spring创建好的bean对象userDAO,而SpringTest仍然使用传统方式新建对象userService)。
    2.4.2小节展示DI依赖注入的实现,通过Spring的工厂获取自动注入好的资源的Bean,依赖关系的管理由配置文件实现。(SpringTest直接获取bean对象userService,而在userService这个bean里,已经注入好了userDAO)

    2.4.1 最基本的IoC控制反转的实现

    效果:UserService得到Spring创建好的bean对象userDAO,而SpringTest仍然使用传统方式新建对象userService。
    过程:先添加配置文件,让Spring工厂创建一个bean对象userDAO。再修改UserServiceImpl实体类,从Spring工厂获取bean对象userDAO。

    有两种spring配置方式:基于XML配置方式、基于注解配置方式。我们在这里采用前一种进行演示。对于这两种配置方式,后续会有更详细的讲解。

    下面开始具体的演示。

    1.新建配置文件:applicationContext.xml(位置:src/main/resources目录或者 WEB-INF目录、习惯性命名:applicationContext.xml)。

    ApplicationContext直译为应用上下文,用来加载Spring框架配置文件,构建Spring的工厂对象,它也称之为Spring容器的上下文对象,也称之为Spring的容器。

    引入xml的头部信息bean schema约束,可以参考规范文档中的的xsd-config.html。在引入之前先本地配置开发包中的xsd文件。

    配好以后,在xml文件内的<beans></beans>标签内,按alt加斜杠,会出现提示,说明配置成功了。

    i出现提示

    也可以参看这里Eclipse安装STS插件使用更便捷的导入方式。

    这里提供applicationContext.xml代码:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
            xmlns:p="http://www.springframework.org/schema/p"
           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">
    <!-- 快速入门 -->
    <!-- IoC的配置 -->
    <bean id="userDAO" class="dao.UserDAOImpl"></bean>
    </beans>
    

    在后续讲解中,将详细说明配置文件xml的编写。在这里可以先了解一下Bean元素基本属性

    2.通过Spring的工厂获取Bean完成相关操作,基本过程是:在程序中读取Spring配置文件,得到Spring的Bean工厂,通过Spring框架获得Bean,完成相应操作。

    修改UserServiceImpl.java代码如下:

    package service;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    import org.springframework.context.ConfigurableApplicationContext;
    import dao.UserDAO;
    
    
    public class UserServiceImpl implements UserService{
    
        @Override
        public void login() {
            System.out.println("service层被调用了");
            //传统方式
            //UserDAO userDAO = new UserDAOImpl();
            
           //IoC方式
           //ApplicationContext认为就是Spring工厂
            //ClassPathXmlApplicationContext:参数就是spring的核心配置文件的名字(我们写的那个xml),这个函数的作用就是自动寻找xml文件
            ApplicationContext applicationContext =new ClassPathXmlApplicationContext("applicationContext.xml");
            //获取bean对象(spring帮忙new的对象),参数就是我们写的bean的id
            //applicationContext.getBean("userDAO");然后换对象格式
           UserDAO userDAO=(UserDAO) applicationContext.getBean("userDAO");
           //消除警告: - Resource leak: 'applicationContex' is never closed的处理
           //导入包import org.springframework.context.ConfigurableApplicationContext;
           //添加这条语句
           ((ConfigurableApplicationContext)applicationContext).close();       
           userDAO.findUserByUsernameAndPassword();
    
        }
    }
    

    提示:出现new ClassPathXmlApplicationContext错误,是因为Eclipse默认为我们导入一个不符合当前创建容器所需的ApplicationContext 包,需要手动替换后解除错误。
    默认导入的包:

    import org.apache.catalina.core.ApplicationContext;
    

    需要手动替换的包:

    import org.springframework.context.ApplicationContext;
    

    添加log4j.properties文件后(内容参考这里),进行测试。

    测试结果

    2.4.2 DI依赖注入的实现

    • 注入方式:set方法属性注入、构造器参数注入、接口注入 (后续再详细展开讲解);
    • 注入类型:值类型(八大基本数据类型)、引用类型(String、自定义对象等)、复杂类型(Array、List、Set、Map、Properties)。

    在spring_day1工程中,userService依赖于userDAO,也就说依赖对象是userDAO、注入对象userService,现在用set方法实现DI依赖注入,把bean对象userDAO注入给bean对象userService。

    效果:SpringTest直接获取bean对象userService,而在userService这个bean里,已经注入好了userDAO。
    过程:先在UserServiceImpl实体类中加一个setUserDAO(),在SpringTest中获取spring工程加工好的userService。然后通过配置文件,让Spring工厂实例化好一个bean对象userDAO、一个bean对象userService,同时,用属性把bean对象userDAO注入给bean对象userService,就结束了。

    下面开始具体的演示。

    修改业务层代码UserServiceImpl.java如下:

    package service;
    
    import dao.UserDAO;
    
    
    public class UserServiceImpl implements UserService{
          //这里写依赖对象的set方法
        private UserDAO userDAO;
        
        public void setUserDAO(UserDAO userDAO){
            this.userDAO=userDAO;
    }
                
        @Override
        public void login() {
            System.out.println("service层被调用了");
           //传统方式
            //UserDAO userDAO = new UserDAOImpl();
            
           //IoC方式
           //ApplicationContext认为就是Spring工厂
            //ClassPathXmlApplicationContext:参数就是spring的核心配置文件的名字(我们写的那个xml),这个函数的作用就是自动寻找xml文件
            //ApplicationContext applicationContext =new ClassPathXmlApplicationContext("applicationContext.xml");
            //获取bean对象(spring帮忙new的对象),参数就是我们写的bean的id
            //applicationContext.getBean("userDAO");然后换对象格式
           //UserDAO userDAO=(UserDAO) applicationContext.getBean("userDAO");
           //消除警告: - Resource leak: 'applicationContex' is never closed的处理
           //导入包import org.springframework.context.ConfigurableApplicationContext;
           //添加这条语句
           //((ConfigurableApplicationContext)applicationContext).close();       
           userDAO.findUserByUsernameAndPassword();
        }
    }
    

    修改测试类SpringTest.java如下:

    package test;
    
    import org.junit.Test;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    import org.springframework.context.ConfigurableApplicationContext;
    
    import service.UserService;
    
    public class SpringTest {
        @Test
        public void test(){
            //传统创建
            //UserService userService = new UserServiceImpl();
            
            //IoC方式
            //1.获取Spring工厂
             ApplicationContext applicationContext =new ClassPathXmlApplicationContext("applicationContext.xml");
            //2.获取工厂制造的bean对象
            UserService userService = (UserService) applicationContext.getBean("userService");
            //消除警告: - Resource leak: 'applicationContex' is never closed的处理
            //导入包import org.springframework.context.ConfigurableApplicationContext;
            //添加这条语句
           ((ConfigurableApplicationContext)applicationContext).close();
            userService.login();
        }
    }
    

    修改applicationContext.xml如下:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
            xmlns:p="http://www.springframework.org/schema/p"
           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">
    <!-- 快速入门 -->
    <!-- IoC的配置 -->
    <bean id="userDAO" class="dao.UserDAOImpl"></bean>
    <!-- 将DAO传入Service -->
        <!-- 将service也交给spring管理(new) -->
        <bean id="userService" class="service.UserServiceImpl">
            <!-- 依赖关系,在这里维护-->
            <!-- userService依赖于userDAO,userService需要用到userDAO,那么创建一个userDAO的bean,并注入给userService的bean里
            name:UserServiceImpl类中被注入对象的setter的名字:例如:setXxx,名字xxx(小写的)
            ref:要注入的具体bean对象的引用,写被依赖的bean的名字
             -->
            <property name="userDAO" ref="userDAO"/>
        </bean>
    </beans>
    

    进行测试。

    测试结果 示意图

    2.5 Spring的工厂

    Spring的工厂

    前文我们用到的ApplicationContext,直译为应用上下文,它被用来加载Spring框架配置文件,构建Spring的工厂对象。它也称之为Spring容器的上下文对象、Spring的容器。
    从图中可以看到,ApplicationContext 只是BeanFactory(Bean工厂,Bean就是一个java对象) 一个子接口,但由于ApplicationContext比它的顶层接口对象BeanFactory 更加强大, 所以现在开发基本没人使用它的顶层接口对象BeanFactory。
    提示:后面还有个FactoryBean,注意区别。

    Spring工厂有两种直接获取,见下图:

    Spring工厂的直接获取(两种方式)

    补充:bean的获取除了根据名称获取,也可以根据类型(具体类型、接口类型)获取,见下图:

    bean获取的两种方式

    相关文章

      网友评论

        本文标题:Spring IoC快速入门

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