Spring 的依赖注入

       依赖注入(Dependency Injection)和控制反转(Inversion of Control)是同一个概念。具体含义是:当某个角色(可能是一个 Java 实例,调用者)需要另一个角色(另一个 Java 实例,被调用者)的协助时,在传统的程序设计过程中,通常由调用者来创建被调用者的实例。但在 Spring 里,创建被调用者的工作不再由调用者来完成,因此称为控制反转;创建被调用者实例的工作通常由 Spring 容器来完成,然后注入调用者,因此也称为依赖注入。
   1 、 设值注入 (setter 注入 )
设值注入是指通过 setter 方法传入被调用者的实例。这种注入方式简单、直观,因而在 Spring 的依赖注入里大量使用。
示例一:
(1 )定义 IPerson 接口:
     public interface IPerson {
          public void useAxe();//使用斧子的方法
     }
(2 )定义 IAxe 接口:
    public interface IAxe {
           public String chop();//”砍”方法
     }
(3 )定义 IPerson 接口的实现类:
         public class Chinese implements IPerson {
               private IAxe axe;//面向接口编程,而不是具体的实现类
               public Chinese() { }
               public void setAxe(IAxe axe) { //设值注入所需的 setter 方法
                        this.axe = axe;
               }
             @Override
             public void useAxe() { //实现 Person 接口的 useAxe 方法
                  System.out.println(axe.chop());
              }
        }
(4 )定义 IAxe 类 接口的第一个实现类 StoneAxe:
        public class StoneAxe implements IAxe {
           public StoneAxe() { }
           @Override
          public String chop() {
            return "石斧砍柴好慢!";
          }
      }
(5 )定义 Spring 的配置文件将 IPerson 实例和 IAxe 实例组织在一起:
         <?xml version="1.0" encoding="UTF-8"?>
         <!DOCTYPE beans PUBLIC "-//SPRING/DTD BEAN//EN"
           "http://www.springframework.org/dtd/spring-beans.dtd">
          <beans>
             <!-—定义第一 bean,该 bean 的 id 是 chinese, class 指定该 bean 实例的实现类 -->
              <bean id="chinese" class="com.bean.Chinese">
                    <!-- property 元素用来指定需要容器注入的属性,axe 属性需要容器注入此处是设值注入,因此 Chinese 类必须拥有 setAxe 方法 -->
                    <property name="axe">
                        <!-- 此处将另一个 bean 的引用注入给 chinese bean -->
                       <ref local="steelAxe"/>
                   </property>
             </bean>
            <!-- 定义 stoneAxe bean -->
                <bean id="steelAxe" class="com.bean.SteelAxe"></bean>
        </beans>

    从配置文件中,可以看到 Spring 管理 bean 的灵巧性。bean 与 bean 之间的依赖关系放在配置文件里组织,而不是写在代码里。通过配置文件的指定,Spring
能精确地为每个 bean 注入属性。因此,配置文件里的 bean 的 class 元素,不能仅仅是接口,而必须是真正的实现类。Spring 会自动接管每个 bean 定义里的 property 元素定义。Spring 会在执行无参数的构造器后、创建默认的 bean 实例后,调用对应的 setter 方法为程序注入属性值。property 定义的属性值将不再由该 bean 来主动创建、管理,而改
为被动接收 Spring 的注入。每个 bean 的 id 属性是该 bean 的惟一标识,程序通过 id 属性访问 bean,bean 与 bean 的依赖关系也通过 id 属性完成。
  (6 )定义 Servlet:
    public class IOCTest extends HttpServlet {
       public IOCTest() { }
       protected void doPost(......) {
                ApplicationContext ac =WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
                IPerson person = (IPerson)ac.getBean("chinese");
                person.useAxe();
          }
       }
(7 )配置 web.xml 文件:
     <?xml version="1.0" encoding="UTF-8"?>
     <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns="http://java.sun.com/xml/ns/javaee"
       xsi:schemaLocation="http://java.sun.com/xml/ns/javaeehttp://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
      <display-name>FirstSpring</display-name>
      <listener>
          <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class>
      </listener>
      <servlet>
          <servlet-name>IOCTest</servlet-name>
          <servlet-class>com.servlet.IOCTest</servlet-class>
      </servlet>
      <servlet-mapping>
            <servlet-name>IOCTest</servlet-name>
            <url-pattern>/ioc</url-pattern>
      </servlet-mapping>
    </web-app>


程序的执行结果如下:
    石斧砍柴好慢


       Servlet 调用 IPerson 的 useAxe()方法时,该方法的方法体内需要使用 IAxe的实例,但程序里没有任何地方将特定的 IPerson 实例和 IAxe 实例耦合在一起。或者说,程序里没有为 IPerson 实例传入 IAxe 的实例,IAxe 实例由 Spring 在运行期间动态注入。IPerson 实例不仅不需要了解 IAxe 实例的具体实现,甚至无须了解 IAxe 的创建过程。程序在运行到需要 IAxe 实例的时候,Spring 创建了 IAxe 实例,然后注入给需要 IAxe 实例的调用者。IPerson 实例运行到需要 IAxe 实例的地方,自然就产生了 IAxe 实例,用来供 IPerson 实例使用。调用者不仅无须关心被调用者的实现过程,连工厂类都可以省略(按需分配)。如果需要改写 IAxe 的实现类。或者说,提供另一个实现类给 IPerson 实例使用。IPerson 接口、Chinese 类都无须改变。只需提供另一个 IAxe 的实现,然后对配置文件进行简单的修改即可。
        IAxe接口的另一个实现类 SteelAxe
           public class SteelAxe implements IAxe {
                 @Override
                 public String chop() {
                        return "钢斧砍柴很快!";
                  }
              }
然后,修改原来的 Spring 配置文件,在其中增加如下一行:
 XML 代码
     <!-- 定义一个 steelAxe bean-->
        <bean id="steelAxe" class="com.bean.SteelAxe"/>
该行重新定义了一个 IAxe 的实现:SteelAxe。然后修改 chinese bean 的配置,将原来传入 stoneAxe 的地方改为传入 steelAxe。也就是将
        <ref local="stoneAxe"/>
改成
       <ref local="steelAxe"/>


此时再次执行程序,将得到如下结果:
钢斧砍柴真快
     

          IPerson 与 IAxe 之间没有任何代码耦合关系,bean 与 bean 之间的依赖关系由 Spring 管理。采用 setter 方法为目标 bean 注入属性的方式,称为设值注入。业务对象的更换变得相当简单,对象与对象之间的依赖关系从代码里分离出来,通过配置文件动态管理。

2 、构造器注入:
示例二:
(1 )定义 bean ( User.java ): :
   package com.bean;
   public class User {
             private Integer id;
             private String name;
             private Integer age;
             public User() { }

            public User(Integer id, String name, Integer age) {
                     super();
                     this.id = id;
                     this.name = name;
                     this.age = age;
            }
           public Integer getId() {
                   return id;
             }
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "User [id=" + id + ", name=" + name + ", age=" + age
+ "]";
}
}
(2 )定义配置文件( applicationContext.xml ): :
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING/DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>

<bean id="user" class="com.bean.User">
<constructor-arg index="0" type="java.lang.Integer" value="1"/>
<constructor-arg index="1" type="java.lang.String" value=" 张飞 "/>
<constructor-arg index="2" type="java.lang.Integer" value="23"/>
</bean>
</beans>
id :任意命名,要易于区分
class :要被 g spring 管理的类,【 class=" 包名. . 类名" " 】
<constructor-arg> 标签:顾名思义就是" " 构造方法的参数" "
index :构造方法参数所在位置的索引值
type :参数的类型
value :为参数赋值
(3 )在 t Servlet 中添加对 r User 的访问:
      User user = (User)ac.getBean("user");
      System.out.printlnuser);


总 结:
依赖注入和控制反转,目的是为了使类与类之间解耦合,提高系统的可扩展性和可维护性。我们可以从以下几个方面理解:
     a、参与者都有谁?
     b、依赖:谁依赖谁?为什么需要依赖?
     c、注入:谁注入谁?又注入了什么呢?
    d、控制反转:谁控制谁?控制什么?为什么叫反转呢?存在正转吗?
    e、控制反转和依赖注入是同一个概念吗?


第一个问题,参与者都有谁?
    (1)对象
    (2)IOC/DI 容器
    (3)某个对象的外部资源


第二问题:依赖,谁依赖谁?为什么需要依赖?
    对象依赖于 IOC/DI 容器,至于为什么要依赖呢?对象需要 IOC/DI 容器来提供需要的外部资源。


第三个问题:注入,谁注入谁?又注入了什么呢?
      是 IOC/DI 容器注入对象,注入了什么呢?注入的是对象所需要的资源


第四个问题 : 控制反转 , 谁控制谁?控制什么?为什么叫反转呢?存在正转吗?
     是 IOC/DI 容器控制对象,主要是控制对象实例的创建,反转是相对于正向而言的,那么什么算是正向的呢?考虑一下常规情况下的应用程序,如果要在 A里面使用 C,你会怎么做呢?当然是直接去创建 C 的对象,也就是说,是在 A 类中主动去获取所需要的外部资源 C,这种情况被称为正向的。那么什么是反向呢?就是 A 类不再主动去获取 C,而是被动等待,等待 IoC/DI 的容器获取一个 C的实例,然后反向的注入到 A 类中。


第五个问题:控制反转和依赖注入式同一个概念吗?
       依赖注入和控制反转是对同一件事情的不同描述,从某个方面讲,就是它们描述的角度不同。依赖注入是从应用程序的角度在描述,即:应用程序依赖容器
创建并注入它所需要的外部资源;而控制反转是从容器的角度在描述,即:容器控制应用程序,由容器反向的向应用程序注入应用程序所需要的外部资源。

原文地址:https://www.cnblogs.com/lone5wolf/p/10969345.html