saowu's Blog

IoC and DI

IoC and DI
2020-05-20 · 7 min read

将代码解耦进行到底...

一、什么是IoC与DI

  • 控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计思想,可以用来减低计算机代码之间的耦合度。其中最常见的实现方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。

二、工厂模式与IoC模式

  • 可以把IoC模式看作工厂模式的升华,把IoC容器看作是一个大工厂,只不过这个大工厂里要生成的对象都是在XML文件中给出定义的。利用Java 的“反射”编程,根据XML中给出的类定义生成相应的对象。从实现来看,以前在工厂模式里写死了的对象,IoC模式改为配置XML文件,这就把工厂和要生成的对象两者隔离,极大提高了灵活性和可维护性。

三、 DI与传统的不同

  • Class A中用到了Class B的对象b,一般情况下,需要在A的代码中显式的new一个B的对象。
  • 采用依赖注入技术之后,Class A的代码只需要定义一个私有的B对象引用,不需要直接new来获得这个对象,而是通过相关的容器控制程序(IoC容器)来将B对象在外部new出来并注入到A类里的引用中。而具体获取的方法、对象被获取时的状态由配置文件(如XML)来指定。

四、反射实现对象注入

  • IoC中最基本的Java技术就是“反射”编程。通俗的说,就是使用反射技术,根据XML中给出的类定义生成b对象,并注入到A类引用里。
public class A {
    private B b;
}

public class B {
}
public class Test {
    //注入对象b
   public static void main(String[] args) {
       try {
           Class<?> aClass = Class.forName("A全路径名");
           Class<?> bClass = Class.forName("B全路径名");
           //反射生成对象
           Object A_object = aClass.getDeclaredConstructor().newInstance();
           Object B_object = bClass.getDeclaredConstructor().newInstance();
           //字段声明是private的,不能用getField方法,应该使用getDeclaredField方法来获取
           Field field = aClass.getDeclaredField("b");
           //字段声明为private,必须进行此操作
           field.setAccessible(true);
           //给字段注入对象
           field.set(A_object, B_object);
       } catch (ClassNotFoundException | NoSuchFieldException | InstantiationException | InvocationTargetException | NoSuchMethodException | IllegalAccessException e) {
           e.printStackTrace();
       }
   }
}

附、Spring依赖注入的4种方式

使用属性的setXXX方法注入

  • 属性注入即通过setXxx()方法注入Bean的属性值或依赖对象,由于属性注入方式具有可选择性和灵活性高的优点,因此属性注入是实际应用中最常采用的注入方式。
<!-- 属性注入 -->
<bean id="car" class="com.spring.model.Car">  
    <property name="maxSpeed" value="200"></property>
    <property name="brand" value="红旗CA72"></property>  
    <property name="price" value="200000.00"></property>
</bean>
  • 属性注入要求Bean提供一个默认的构造函数,并为需要注入的属性提供对应的Setter方法。Spring先调用Bean的默认构造函数实例化Bean对象,然后通过反射的方式调用Setter方法注入属性值。
package com.spring.model;

public class Car {
    private int maxSpeed;
    private String brand;
    private double price;
    
    public int getMaxSpeed() {
        return maxSpeed;
    }
    //一定要写被注入对象的set方法
    public void setMaxSpeed(int maxSpeed) {
        this.maxSpeed = maxSpeed;
    }
    public String getBrand() {
        return brand;
    }
    public void setBrand(String brand) {
        this.brand = brand;
    }
    public double getPrice() {
        return price;
    }
    public void setPrice(double price) {
        this.price = price;
    }
}

使用构造函数注入

  • 构造函数注入是除属性注入之外的另一种常用的注入方式,它保证一些必要的属性在Bean实例化时就得到设置(construct是bean生命周期的第一步,实例化bean),并且确保了Bean实例在实例化后就可以使用。
  • 使用方式
    • 在类中,不用为属性设置setter方法(但是可以有),但是需要生成该类带参的构造方法。
    • 在配置文件中配置该类的bean,并配置构造器,在配置构造器中用到了<constructor-arg>节点,该节点有四个属性:
      • index是索引,指定注入的属性位置,从0开始;
      • type是指该属性所对应的类型;
      • ref 是指引用的依赖对象;
      • value 当注入的不是依赖对象,而是基本数据类型时,就用value;
<!-- 构造函数注入(按类型匹配) -->
<bean id="car1" class="com.spring.model.Car">  
    <constructor-arg type="int" value="300"></constructor-arg>
    <constructor-arg type="java.lang.String" value="宝马"></constructor-arg>
    <constructor-arg type="double" value="300000.00"></constructor-arg>
</bean>
  • 使用构造函数注入的前提是Bean必须提供带参的构造函数。
package com.spring.model;
 
public class Car {
    
    private int maxSpeed;
    private String brand;
    private double price;
    
    //带参构造方法
    public Car(int maxSpeed,String brand, double price){
        this.maxSpeed=maxSpeed;
        this.brand=brand;
        this.price=price;
    }
}

使用接口注入

  • 接口注入需要实现接口,带有侵入性,不推荐
  • 最常见的,就是我们定义的servlet需要继承HTTPServlet,在我们的doPost方法中,HttpServletRequest参数就是使用接口注入的形式。

使用注解注入

  • SpringMVC支持自动扫描并注册为spring bean,需要配置<context:component-scan base-package="">base-package属性指定要自动检测扫描的包。该配置会自动扫描指定的包及其子包下面被构造型注解标注的类,并将这些类注册为spring bean,这样就不用在配置文件一个一个地配置成bean标签。构造型注解包括:@Controller@Component@Service@Repositoryt标注的自定义注解。
  • Spring 2.5 之后提供了注解方式的自动装配,常用的自动装配注解有以下几种:@Autowired@Qualifier@Named@Resource@Inject。但是要使用这些注解,需要在配置文件中配置<context:annotation-config />。只有加上这一配置,才可以使用注解进行spring bean的自动装配,默认情况下基于注解的装配是被禁用的。
    @Component
    public class TestService {
        @Autowired
        private TestDao testDao;

        public List<Files> selectAll() {
            List<Files> filesList = testDao.selectAll();
            return filesList;
        }

    }
Copyright © 2020 - 2024 saowu. All Right Reserved
Powered by Gridea