• 7 -- Spring的基本用法 -- 3... Spring 的核心机制 : 依赖注入


        7.3 Spring 的核心机制 : 依赖注入

          Spring 框架的核心功能有两个。

            Spring容器作为超级大工厂,负责创建、管理所有的Java对象,这些Java对象被称为Bean。

            Spring容器管理容器中Bean之间的依赖关系,Spring使用一种被称为“依赖注入”的方式来管理Bean之间的依赖关系。

          7.3.1 理解依赖注入

            当某个java对象需要调用另一个Java对象的方法时,在传统模式下通常有如下两种做法:

            1. 原始做法 : 调用者主动创建被依赖对象,然后在调用被依赖对象的方法。

            2. 简单工厂模式 : 调用者先找到被依赖对象的工厂,然后主动通过工厂去获取被依赖对象,最后在调用被依赖对象的方法。

            解1:由于调用者需要通过形如“new Person()”的代码创建对象,因此必然导致调用者与被依赖对象实现类的硬编码耦合,非常不利于项目升级的维护。

            解2:对于简单工厂的方式,大致需要把握三点:①调用者面向被依赖对象的接口编程;②将被依赖对象的创建交给工厂完成;③调用者通过工厂来获得被依赖组件。调用者只需与被依赖对象的接口耦合,避免了类层次的硬编码耦合。缺点:调用组件需要主动通过工厂去获取被依赖对象,这就会带来调用组件与被依赖对象工厂的耦合。

            使用Spring框架的两个主要改变是:

              程序无须使用 new 调用构造器去创建对象。所有的Java对象都可交给 Spring 容器去创建。

              当调用者需要调用被依赖对象的方法时,调用者无须主动获取被依赖对象,只要等待 Spring 容器注入即可。

            依赖注入通常有两种:

              设值注入 :  IoC 容器使用成员变量的setter方法来注入被依赖对象。

              构造注入 :  IoC 容器使用构造器来注入被依赖对象。

          7.3.2 设值注入

            设值注入是指IoC容器通过成员变量的setter方法来注入被依赖对象。

            Spring 推荐面向接口编程。不管是调用者,还是被依赖对象,都应该为之定义接口,程序应该面向它们的接口,而不是面向实现类编程,这样以便程序后期的升级、维护。

            Spring 推荐面向接口编程,这样可以更好地让规范和实现分离,从而提供更好的解耦。对于一个Java  EE应用,不管是DAO组件,还是Service组件,都应该先定义一个接口,该接口定义了该组件应该实现的功能,但功能的实现则由实现类提供。

            Interface : Person

    package edu.pri.lime._7_3_2.bean;
    
    //定义Person对象应遵守的规范
    public interface Person {
    
        // 定义一个使用斧头的方法
        public void useAxe();
        
    }

            Interface :Axe

    package edu.pri.lime._7_3_2.bean;
    
    //定义Axe对象应遵守的规范
    public interface Axe {
    
        // Axe 接口中定义一个chop()方法
        public String chop();
    }

            Class :Chinese

    package edu.pri.lime._7_3_2.bean.impl;
    
    import edu.pri.lime._7_3_2.bean.Axe;
    import edu.pri.lime._7_3_2.bean.Person;
    
    public class Chinese implements Person {
    
        private Axe axe;
        // 设值注入所需的setter()方法
        public void setAxe(Axe axe) {
            this.axe = axe;
        }
        //实现Person接口的userAxe()方法
        public void useAxe() {
    //        调用axe的 chop()方法
    //        表明Person对象依赖于axe对象
            System.out.println(axe.chop());
        }
    }

            Class : StoneAxe

    package edu.pri.lime._7_3_2.bean.impl;
    
    import edu.pri.lime._7_3_2.bean.Axe;
    
    public class StoneAxe implements Axe {
    
        public String chop() {
            // TODO Auto-generated method stub
            return "石斧砍柴好慢";
        }
    
    }

             配置文件

    <?xml version="1.0" encoding="UTF-8"?>
    <!-- Spring 配置文件的根元素,使用Spring-beans-4.0.xsd语义约束 -->
    <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns="http://www.springframework.org/schema/beans"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
    
        <!-- 配置名为chinese的Bean。其实现类是edu.pri.lime._7_3_2.bean.impl.Chinese类 -->
        <bean id="person" class="edu.pri.lime._7_3_2.bean.impl.Chinese">
            <!-- 驱动调用chinese的setAxe()方法,将容器中的stoneAxe Bean 作为传入参数 -->
            <property name="axe" ref="stoneAxe"/>
        </bean>
        
        <!-- 配置名为StoneAxe的Bean,其实现类是edu.pri.lime._7_3_2.bean.impl.StoneAxe类 -->
        <bean id="stoneAxe" class="edu.pri.lime._7_3_2.bean.impl.StoneAxe"/>
        
    </beans>

             Class : TestBean

    package edu.pri.lime._7_3_2.mian;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    import edu.pri.lime._7_3_2.bean.Person;
    import edu.pri.lime._7_3_2.bean.impl.Chinese;
    
    public class BeanTest {
    
        public static void main(String[] args){
            // 创建Spring容器
            ApplicationContext ctx = new ClassPathXmlApplicationContext("app_7_3_2.xml");
            // 获取chinese 实例
            Person per = ctx.getBean("chinese", Chinese.class);
            // 调用 useAxe()方法
            per.useAxe();
        }
    }

             Class : SteelAxe

    package edu.pri.lime._7_3_2.bean.impl;
    
    import edu.pri.lime._7_3_2.bean.Axe;
    
    public class SteelAxe implements Axe {
    
        public String chop() {
            // TODO Auto-generated method stub
            return "钢斧砍柴真快";
        }
    
    }

             Class : TestBean

    package edu.pri.lime._7_3_2.main;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    import edu.pri.lime._7_3_2.bean.Person;
    import edu.pri.lime._7_3_2.bean.impl.Chinese;
    
    public class BeanTest {
    
        public static void main(String[] args){
            //创建Spring容器
            ApplicationContext ctx = new ClassPathXmlApplicationContext("app_7_3_2.xml");
            //获取chinese 实例
            Person per = ctx.getBean("chinese", Chinese.class);
            //调用 useAxe()方法
            per.useAxe();
            
            Person chi = ctx.getBean("china",Chinese.class);
            chi.useAxe();
        }
    }

             Spring IoC 容器的三个基本要点:

              应用程序的各组件面向接口编程。面向接口编程可以将组件之间的耦合关系提升到接口层次,从而有利于项目后期的扩展。

              应用程序的各组件不再由程序主动创建,而是有Spring容器来负责产生并初始化。

              Spring 采用配置文件或注解来管理 Bean 的实现类、依赖关系,Spring容器则根据配置文件或注解,利用反射来创建实例,并为之注入依赖关系。

          7.3.3 构造注入

            在构造实例时,已经为其完成依赖关系的初始化,这种利用构造器来设置依赖关系的方式,被称为构造注入。

            驱动Spring 在底层以反射方式执行带指定参数的构造器,当执行带参数的构造器时,就可利用构造器参数对成员变量执行初始化------这就是构造注入的本质。

            <bean.../> 元素默认驱动Spring调用无参数的构造器来创建对象。使用<constructor-arg.../>子元素代表一个构造器参数,如果<bean.../>元素包含N个<contructor-arg.../>子元素,就会驱动Spring调用带N个参数的构造器来创建对象。

            Class :Chinese

    package edu.pri.lime._7_3_3.bean.impl;
    
    import edu.pri.lime._7_3_2.bean.Axe;
    import edu.pri.lime._7_3_2.bean.Person;
    
    public class Chinese implements Person {
    
        private Axe axe;
        
        public Chinese() {
            super();
            // TODO Auto-generated constructor stub
        }
    
        //    未有setter()方法,使用构造方法注入
        public Chinese(Axe axe) {
            super();
            this.axe = axe;
        }
    
        public void useAxe() {
            System.out.println(axe.chop());
        }
    
    }

            配置文件 :

    <?xml version="1.0" encoding="UTF-8"?>
    <!-- Spring 配置文件的根元素,使用Spring-beans-4.0.xsd语义约束 -->
    <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns="http://www.springframework.org/schema/beans"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
    
        <!-- 配置名为chinese的Bean。其实现类是edu.pri.lime._7_3_3.bean.impl.Chinese类 -->
        <bean id="chinese" class="edu.pri.lime._7_3_3.bean.impl.Chinese">
            <!-- 驱动Spring调用Chinese的带一个参数的构造器来创建对象 -->
            <constructor-arg ref="stoneAxe"/>
        </bean>
    
        <!-- 配置名为StoneAxe的Bean,其实现类是edu.pri.lime._7_3_2.bean.impl.StoneAxe类 -->
        <bean id="stoneAxe" class="edu.pri.lime._7_3_2.bean.impl.StoneAxe" />
        
        <bean id="steelAxe" class="edu.pri.lime._7_3_2.bean.impl.SteelAxe"/>
    </beans>

            Class : BeanTest

    package edu.pri.lime._7_3_3.main;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    import edu.pri.lime._7_3_2.bean.Person;
    import edu.pri.lime._7_3_3.bean.impl.Chinese;
    
    public class BeanTest {
    
        public static void main(String[] args){
            ApplicationContext ctx = new ClassPathXmlApplicationContext("app_7_3_3.xml");
            Person per = ctx.getBean("chinese",Chinese.class);
            per.useAxe();
        }
    }

            Spring底层,反射机制

            String idStr = ... // Spring 解析<bean.../> 元素得到id属性值为chinese
            String refStr = ... // Spring 解析 <constructor-arg.../> 元素得到ref属性值为steelAxe
            Object paramBean = container.get(refStr);
            // Spring 会用反射方式执行下面代码(仅仅示例,为用反射)
            Object obj = new Chinese(paramBean);
            // container 代表Spring 容器
            container.put(idStr,obj);
    package edu.pri.lime._7_3_3.main;
    
    import java.lang.reflect.Constructor;
    
    import edu.pri.lime._7_3_2.bean.Axe;
    import edu.pri.lime._7_3_2.bean.Person;
    
    public class ReflectTest {
    
        public static void main(String[] args) throws Exception{
            Class<?> clazzChi = Class.forName("edu.pri.lime._7_3_3.bean.impl.Chinese");
            Class<?> clazzAxe = Class.forName("edu.pri.lime._7_3_2.bean.impl.SteelAxe");
            Class<?> clazz = Class.forName("edu.pri.lime._7_3_2.bean.Axe"); 
            Axe axe  = (Axe) clazzAxe.newInstance();
            Constructor<?> con = clazzChi.getConstructor(clazz);
            Person per = (Person) con.newInstance(axe);
            per.useAxe();
        }
    }

            配置<constructor-arg .../> 元素是可指定一个index属性,用于指定该构造参数值将作为第几个构造参数数值:index=0表明该构造参数值将作为第一个构造参数值。

            <constructor-arg value="23" type="int"/>:用于更明确的指定数据类型。

          7.3.4  两种注入方式的对比

            

  • 相关阅读:
    【自制操作系统03】读取硬盘中的数据
    【自制操作系统02】环境准备与启动区实现
    【自制操作系统01】硬核讲解计算机的启动过程
    【30天自制操作系统】day06:中断
    java8 stream ,filter 等功能代替for循环
    如何评估工作量--三点估算
    python 错误AttributeError: 'module' object has no attribute 'AF_INET'
    python入门学习
    mysql5.7.10和mysql5.5.39两个版本对于group by函数的处理差异
    jenkins 构建时,取消构建测试类
  • 原文地址:https://www.cnblogs.com/ClassNotFoundException/p/6231209.html
Copyright © 2020-2023  润新知