依赖注入的基本原则是:应用组件不应该负责查找资源或者其他依赖的协作对象。配置对象的工作应该由IoC容器负责,“查找资源”的逻辑应该从应用组件的代码中抽取出来,交给IoC容器负责。
下面分别演示3中注入机制。
代码 待注入的业务对象 Content.java
package  com.zj.ioc.di;
 
public   class  Content {
 
     public   void  BusniessContent(){
       System. out .println( "do business" );
    }
   
     public   void  AnotherBusniessContent(){
       System. out .println( "do another business" );
    }
}
MyBusniess类展示了一个业务组件,它的实现需要对象Content的注入。代码3,代码4,代码5,6分别演示构造子注入(Constructor Injection),设值注入(Setter Injection)和接口注入(Interface Injection)三种方式。
 
代码 3构造子注入(Constructor Injection) MyBusiness.java
package  com.zj.ioc.di.ctor;
import  com.zj.ioc.di.Content;
 
public   class  MyBusiness {
     private  Content  myContent ;
 
     public  MyBusiness(Content content) {
        myContent  = content;
    }
   
     public   void  doBusiness(){
        myContent .BusniessContent();
    }
   
     public   void  doAnotherBusiness(){
        myContent .AnotherBusniessContent();
    }
}
 
代码 4设值注入(Setter Injection)  MyBusiness.java
package  com.zj.ioc.di.set;
import  com.zj.ioc.di.Content;
 
public   class  MyBusiness {
     private  Content  myContent ;
 
     public   void  setContent(Content content) {
        myContent  = content;
    }
   
     public   void  doBusiness(){
        myContent .BusniessContent();
    }
   
     public   void  doAnotherBusiness(){
        myContent .AnotherBusniessContent();
    }
}
 
代码 设置注入接口 InContent.java
package  com.zj.ioc.di.iface;
import  com.zj.ioc.di.Content;
 
public   interface  InContent {
     void  createContent(Content content);
}
 
代码 6接口注入(Interface Injection) MyBusiness.java
package  com.zj.ioc.di.iface;
import  com.zj.ioc.di.Content;
 
public   class  MyBusiness  implements  InContent{
     private  Content  myContent ;
 
     public   void  createContent(Content content) {
        myContent  = content;
    }
   
     public   void  doBusniess(){
        myContent .BusniessContent();
    }
   
     public   void  doAnotherBusniess(){
        myContent .AnotherBusniessContent();
    }
}
 
5.依赖拖拽(Dependency Pull)
最后需要介绍的是依赖拖拽,注入的对象如何与组件发生联系,这个过程就是通过依赖拖拽实现。
代码7 依赖拖拽示例
public static void main(String[] args) throws Exception{
//get the bean factory
BeanFactory factory = getBeanFactory();
MessageRender mr = (MessageRender) factory.getBean(“renderer”);
mr.render();
}
而通常对注入对象的配置可以通过一个xml文件完成。
使用这种方式对对象进行集中管理,使用依赖拖拽与依赖查找本质的区别是,依赖查找是在业务组件代码中进行的,而不是从一个集中的注册处,特定的地点执行。



================================================================================================================
 在java开发中,程序员在某个类中需要依赖其它类的方法,则通常是new一个依赖类再调用类实例的方法,这种开发存在的问题是new的类实例不好统一管理,spring提出了依赖注入的思想,即依赖类不由程序员实例化,而是通过spring容器帮我们new指定实例并且将实例注入到需要该对象的类中。依赖注入的另一种说法是“控制反转”,通俗的理解是:平常我们new一个实例,这个实例的控制权是我们程序员,而控制反转是指new实例工作不由我们程序员来做而是交给spring容器来做。

Spring依赖注入(DI)的三种方式,分别为:

1.  Setter方法注入

2.  构造方法注入

3.  接口注入


下面介绍一下这三种依赖注入在Spring中是怎么样实现的。

 Setter方法注入

首先我们需要以下几个类:

接口 Logic.java

接口实现类 LogicImpl.java

一个处理类 LoginAction.java

还有一个测试类 TestMain.java

Logic.java如下:


[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. <span style="font-size:18px;"><span style="font-size:18px;">package DI;  
  2. //定义接口  
  3. public interface Logic {  
  4.     public String getName();  
  5. }  
  6. </span></span>  

LogicImpl.java如下:
[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. <span style="font-size:18px;"><span style="font-size:18px;">package DI;  
  2.   
  3. public class LogicImpl implements Logic {  
  4. //实现类  
  5.   
  6.     public String getName() {  
  7.           
  8.         return "lishehe";  
  9.     }  
  10.   
  11. }  
  12. </span></span>  

LoginAction.java 会根据使用不同的注入方法而稍有不同

Setter方法注入:

[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. <span style="font-size:18px;"><span style="font-size:18px;">package DI;  
  2.   
  3. public class LoginAction {  
  4.      private Logic logic;  
  5.   
  6.        
  7.   
  8.         public void execute() {  
  9.   
  10.            String name = logic.getName();  
  11.   
  12.            System.out.print("My Name Is " + name);    
  13.   
  14.         }  
  15.   
  16.        
  17.   
  18.         /** 
  19.  
  20.          * @return the logic 
  21.  
  22.          */  
  23.   
  24.         public Logic getLogic() {  
  25.   
  26.            return logic;  
  27.   
  28.         }  
  29.   
  30.        
  31.   
  32.         /** 
  33.  
  34.          * @param logic 
  35.  
  36.          *            the logic to set 
  37.  
  38.          */  
  39.   
  40.         public void setLogic(Logic logic) {  
  41.   
  42.            this.logic = logic;  
  43.   
  44.         }  
  45. }  
  46. </span></span>  


客户端测试类

TestMain.java

[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. <span style="font-size:18px;">package DI;  
  2.   
  3. import org.springframework.context.ApplicationContext;  
  4. import org.springframework.context.support.FileSystemXmlApplicationContext;  
  5.   
  6. public class TestMain {  
  7.   
  8.      /** 
  9.  
  10.      * @param args 
  11.  
  12.      */  
  13.   
  14.     public static void main(String[] args) {  
  15.   
  16.    
  17.   
  18.        // 得到ApplicationContext对象  
  19.   
  20.        ApplicationContext ctx = new FileSystemXmlApplicationContext(  
  21.   
  22.               "applicationContext.xml");  
  23.   
  24.        // 得到Bean  
  25.   
  26.        LoginAction loginAction = (LoginAction) ctx.getBean("loginAction");  
  27.   
  28.         
  29.   
  30.        loginAction.execute();  
  31.   
  32.     }  
  33. }  
  34. </span>  

定义了一个Logic 类型的变量 logic, LoginAction并没有对logic 进行实例化,而只有他对应的setter/getter方法,因为我们这里使用的是Spring的依赖注入的方式

 

applicationContext.xml配置文件如下:

[html]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. <span style="font-size:18px;"><?xml version="1.0" encoding="UTF-8"?>  
  2.   
  3. <!--  
  4.   - Application context definition for JPetStore's business layer.  
  5.   - Contains bean references to the transaction manager and to the DAOs in  
  6.   - dataAccessContext-local/jta.xml (see web.xml's "contextConfigLocation").  
  7.   -->  
  8. <beans xmlns="http://www.springframework.org/schema/beans"  
  9.         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  10.         xmlns:aop="http://www.springframework.org/schema/aop"  
  11.         xmlns:tx="http://www.springframework.org/schema/tx"  
  12.         xsi:schemaLocation="  
  13.             http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd  
  14.             http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd  
  15.             http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">  
  16.       
  17.   
  18. <bean id="logic" class="DI.LogicImpl"/>  
  19.   
  20. <bean id="loginAction" class="DI.LoginAction">  
  21.   
  22.  <property name="logic" ref="logic"></property>  
  23.   
  24. </bean>  
  25.   
  26. </beans>  
  27. </span>  

运行效果:



构造器注入

顾名思义,构造方法注入,就是我们依靠LoginAction的构造方法来达到DI的目的,如下所示:

[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. <span style="font-size:18px;">package DI;  
  2.   
  3. public class LoginAction {  
  4.       
  5.       
  6.      private Logic logic;  
  7.   
  8.        
  9.   
  10.         public LoginAction(Logic logic) {  
  11.   
  12.            this.logic = logic;  
  13.   
  14.         }  
  15.   
  16.        
  17.   
  18.         public void execute() {  
  19.   
  20.            String name = logic.getName();  
  21.   
  22.            System.out.print("My Name Is " + name);  
  23.   
  24.         }  
  25.       
  26. }  
  27. </span>  

里我们添加了一个LoginAction的构造方法

applicationContext.xml配置文件如下:

[html]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. <span style="font-size:18px;">    <bean id="logic" class="DI.LogicImpl"/>  
  2.   
  3. <bean id="loginAction" class="DI.LoginAction">  
  4.   
  5.  <constructor-arg index="0" ref="logic"></constructor-arg>  
  6.   
  7. </bean></span>  

我们使用constructor-arg来进行配置, index属性是用来表示构造方法中参数的顺序的,如果有多个参数,则按照顺序,从 0,1...来配置

 

我们现在可以运行testMain.java,结果跟使用Setter方法注入完全一样.

 效果图


 

其中需要注意一点有:构造函数有多个参数的话,如:参数1,参数2,而参数2依赖于参数1,这中情况则要注意构造函数的顺序,必须将参数1放在参数2之前。

接口注入

下面继续说说我们不常用到的接口注入,还是以LogicAction为例,我们对他进行了修改,如下所示:

LogicAction.java

[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. <span style="font-size:18px;">package DI;  
  2.   
  3. public class LoginAction {  
  4.       
  5.     private Logic logic;  
  6.   
  7.        
  8.   
  9.     public void execute() {  
  10.   
  11.        try {  
  12.   
  13.            Object obj = Class.forName("DI.LogicImpl")  
  14.   
  15.                   .newInstance();  
  16.   
  17.            logic = (Logic) obj;  
  18.   
  19.            String name = logic.getName();  
  20.   
  21.            System.out.print("My Name Is " + name);  
  22.   
  23.        } catch (Exception e) {  
  24.   
  25.            e.printStackTrace();  
  26.   
  27.        }  
  28.   
  29.     }  
  30.       
  31.        
  32.   
  33. }  
  34. </span>  

配置文件:

[html]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. <span style="font-size:18px;"><bean id="logic" class="DI.LogicImpl"/>  
  2.   
  3. <bean id="loginAction" class="DI.LoginAction"></span>  
效果图




总结

    对于Spring的依赖注入,最重要的就是理解他的,一旦理解了,将会觉得非常的简单。无非就是让容器来给我们实例化那些类,我们要做的就是给容器提供这个接口,这个接口就我们的set方法或者构造函数了。



Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐