JSP(第九章:Spring)

1 控制反转(Inversion of Control)

2 依赖注入(Dependency Injection)

2.1 构造器注入

2.2 setter注入

3 总结


入门网站:http://how2j.cn/k/spring/spring-ioc-di/87.html

1 控制反转(Inversion of Control)

IoC,也就是Inversion of Controller,控制反转。

  • 传统编程:决定使用哪个具体的实现类的控制权在调用类本身,在编译阶段就确定了。
  • IoC模式:调用类只依赖接口,而不依赖具体的实现类,减少了耦合。控制权交给了容器,在运行的时候才由容器决定将具体的实现动态的“注入”到调用类的对象中。

简单Ioc例子

导入jar包,下载入门网站中的lib压缩至【WEB-INF/lib】下

【src/my.example/Car.java】:创建 车 接口

package my.example;
public interface Car {
     void gear() ;//挂挡
     void oil() ;//加油
     void run() ;
}

【src/my.example/BMWCarr.java】:创建 宝马bean

package my.example;
public class BMWCar implements Car{
    public void gear() {
        System.out.println("宝马挂挡");
    }
    public void oil() {
        System.out.println("宝马加油");
    }
    public void run() {
        System.out.println("宝马行驶");
    }
}

【src/my.example/ToyoCar.java】:创建 丰田bean

package my.example;
public class BMWCar implements Car{
    public void gear() {
        System.out.println("宝马挂挡");
    }
    public void oil() {
        System.out.println("宝马加油");
    }
    public void run() {
        System.out.println("宝马行驶");
    }
}

【src/my.example/Person.java】:创建 人类bean

package my.example;
public class Person {
    private Car c;
    public Person(Car c) {//Person 被动接受对象,参数是接口。
        this.c=c;
    }    
    public void drive() {
        c.gear();
        c.oil();
        c.run();
    }
}

【src/my.example/MainApp.java】:创建 实现类MainApp

package my.example;
public class MainApp {
    public static void main(String[] args) {
        Person p1=new Person(new BMWCar());
        Person p2=new Person(new ToyoCar());
        p1.drive();
        p2.drive();
    }
}

【结果:】

宝马挂挡
宝马加油
宝马行驶
丰田挂挡
丰田加油
丰田行驶

如果没有定义接口Car,传统变成中调用类Person决定调用哪一个Car类,宝马或者丰田由Person调用。

但Ioc编程中,把控制权移除,转交给了实现类MainApp,现了“控制”的“反转”,这也就是我们所说的IoC。

2 依赖注入(Dependency Injection)

DI,Dependency Injection,也就是依赖注入。

James Shore给出了依赖注入的定义:依赖注入就是将实例变量传入到一个对象中去(Dependency injection means giving an object its instance variables)。

Spring的核心就是依赖注入,Spring支持的注入方式主要有两种:setter注入(setter injection)构造器注入(constructor injection)

2.1 构造器注入

构造器注入:JavaBean必须要有构造函数

【src/my.example/Dog.java】:创建 狗bean

package my.example;
public class Dog {
    public String name;
    public Dog(String name) {
        super();
        this.name = name;
    }
}

【src/my.example/Customer.java】:创建 顾客bean

package my.example;
public class Customer {
    private String name;
    private String address;
    private int age;
    private Dog dog;
    public Customer(String name, String address, int age, Dog dog) {
        super();
        this.name = name;
        this.address = address;
        this.age = age;
        this.dog = dog;
    }
    @Override
    public String toString() {
        return "Customer [name=" + name + ", address=" + address + ", age=" + age + ", dog=" + dog.name + "]";
    }
}

【src/my.example/MainApp.java】: 创建 实现类

package my.example;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp {    
    public static void main(String[] args) {
        ApplicationContext context = 
                new ClassPathXmlApplicationContext("Beans.xml");
        Customer c=(Customer) context.getBean("customer");
        System.out.println(c.toString());
    }    
}

【src/Beans.xml】: 创建 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
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<!-- 每一个Bean代表一个对象 -->
<bean id="dog" class="my.example.Dog">
    <constructor-arg>
        <value>旺旺</value>
    </constructor-arg>
</bean>
<bean id="customer" class="my.example.Customer">
    <constructor-arg type="java.lang.String">
        <value>张三</value>
    </constructor-arg>
    <constructor-arg type="java.lang.String">
        <value>北京</value>
    </constructor-arg>
    <constructor-arg type="int">
        <value>18</value>
    </constructor-arg>
    <constructor-arg ref="dog"><!-- ref引用刚才创建的dog对象,可多个对象引用同一个dog -->
    </constructor-arg>
</bean>
</beans>

【结果:】

Customer [name=张三, address=北京, age=18, dog=旺旺]

2.2 setter注入

构造器注入:JavaBean必须要有set、get函数,并且删除构造函数

【src/my.example/Dog.java】:创建 狗bean

package my.example;
public class Dog {
    public String name;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

【src/my.example/Customer.java】:创建 顾客bean

package my.example;
public class Customer {
    private String name;
    private String address;
    private int age;
    private Dog dog;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public Dog getDog() {
        return dog;
    }
    public void setDog(Dog dog) {
        this.dog = dog;
    }
    @Override
    public String toString() {
        return "Customer [name=" + name + ", address=" + address + ", age=" + age + ", dog=" + dog.name + "]";
    }
}

【src/my.example/MainApp.java】: 创建 实现类,跟构造注入相同

package my.example;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp {
    public static void main(String[] args) {
        ApplicationContext context = 
                new ClassPathXmlApplicationContext("Beans.xml");
        Customer c=(Customer) context.getBean("customer");
        System.out.println(c.toString());
    }
}

【src/Beans.xml】: 创建 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
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<!-- 每一个Bean代表一个对象 -->
<bean id="mydog" class="my.example.Dog">
    <property name="name" value="喵喵"></property>
</bean>
<bean id="customer" class="my.example.Customer">
    <property name="name" value="李四"></property>
    <property name="address" value="上海"></property>
    <property name="age" value="20"></property>
    <property name="dog" ref="mydog"></property>
</bean>
</beans>

【结果:】

Customer [name=李四, address=上海, age=20, dog=喵喵]

3 总结

​ Ioc—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想。在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。如何理解好Ioc呢?理解好Ioc的关键是要明确“谁控制谁,控制什么,为何是反转(有反转就应该有正转了),哪些方面反转了”,那我们来深入分析一下:

  • 谁控制谁,控制什么:传统Java SE程序设计,我们直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象;而IoC是有专门一个容器来创建这些对象,即由Ioc容器来控制对 象的创建;谁控制谁?当然是IoC 容器控制了对象;控制什么?那就是主要控制了外部资源获取(不只是对象包括比如文件等)。
  • 为何是反转,哪些方面反转了:有反转就有正转,传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙创建及注入依赖对象;为何是反转?因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转;哪些方面反转了?依赖对象的获取被反转了。

​ IoC 不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合、更优良的程序。传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试;有了IoC容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是 松散耦合,这样也方便测试,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活。

​ 其实IoC对编程带来的最大改变不是从代码上,而是从思想上,发生了“主从换位”的变化。应用程序原本是老大,要获取什么资源都是主动出击,但是在IoC/DI思想中,应用程序就变成被动的了,被动的等待IoC容器来创建并注入它所需要的资源了。

理解DI的关键是:“谁依赖谁,为什么需要依赖,谁注入谁,注入了什么”,那我们来深入分析一下:

  • 谁依赖于谁:当然是应用程序依赖于IoC容器;
  • 为什么需要依赖:应用程序需要IoC容器来提供对象需要的外部资源;
  • 谁注入谁:很明显是IoC容器注入应用程序某个对象,应用程序依赖的对象;
  • 注入了什么:就是注入某个对象所需要的外部资源(包括对象、资源、常量数据)。

​ IoC和DI由什么关系呢?其实它们是同一个概念的不同角度描述,由于控制反转概念比较含糊(可能只是理解为容器控制对象这一个层面,很难让人想到谁来维护对象关系),所以2004年大师级人物Martin Fowler又给出了一个新的名字:“依赖注入”,相对IoC 而言,“依赖注入”明确描述了“被注入对象依赖IoC容器配置依赖对象”。


​ Spring所倡导的开发方式就是如此,所有的类都会在spring容器中登记,告诉spring你是个什么东西,你需要什么东西,然后spring会在系统运行到适当的时候,把你要的东西主动给你,同时也把你交给其他需要你的东西。所有的类的创建、销毁都由 spring来控制,也就是说控制对象生存周期的不再是引用它的对象,而是spring。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被spring控制,所以这叫控制反转。

​ IoC的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点是通过DI(Dependency Injection,依赖注入)来实现的。比如对象A需要操作数据库,以前我们总是要在A中自己编写代码来获得一个Connection对象,有了 spring我们就只需要告诉spring,A中需要一个Connection,至于这个Connection怎么构造,何时构造,A不需要知道。在系统运行时,spring会在适当的时候制造一个Connection,然后像打针一样,注射到A当中,这样就完成了对各个对象之间关系的控制。A需要依赖 Connection才能正常运行,而这个Connection是由spring注入到A中的,依赖注入的名字就这么来的。那么DI是如何实现的呢? Java 1.3之后一个重要特征是反射(reflection),它允许程序在运行的时候动态的生成对象、执行对象的方法、改变对象的属性,spring就是通过反射来实现注入的。

打赏
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!

客官,赏一瓶怡宝吧。

支付宝
微信