Spring依赖注入(控制反转)DI(IOC)的常用方式(构造器注入和setter注入)


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

通常使用setter方法和构造器方式来进行注入,在过去的开发过程中,这两种注入方式都是非常常用的。spring也同时支持这两种依赖注入的方式:设值注入和构造注入。这两种依赖注入的方式并没有绝对的好坏,知识适应的场景不一样。

相比而言setter注入具有以下优点:

1)与传统的JavaBean的写法更相似,程序开发人员更容易理解、接受。通过setter方法设定依赖关系显得更加直观、自然。
2)对于复杂的依赖关系,如果采用构造注入,会导致构造器过于臃肿,难以阅读。Spring在创建Bean实例时,需要同时实例化其依赖的全部实例,因而导致性能下降。而是用设置注入可以避免这些问题。
3)尤其在某些属性可选的情况下,多参数的构造器更加笨重。

某些情况下,构造注入的优势:


1)构造注入可以再构造器中决定依赖关系的注入顺序,有限依赖的优先注入。例如,组件中其它依赖关系的注入,常常需要依赖于Datasource的注入。采用构造注入,可以在代码中清晰地决定注入顺序。
2)对于依赖关系无需变化的Bean,构造注入更加有用。因为没有setter方法,所有的依赖关系全部在构造器内设定。因此,无需担心后续代码对依赖关系的破坏。
3)依赖关系只能在构造器中设定,则只有组建的创建者才能改变组建的依赖关系。队组建的调用者而言,组件内部的依赖关系完全透明,更符合高内聚的原则。

建议:采用设置注入为主,构造注入为辅的注入策略。对于依赖关系无需变化的注入,尽量采用构造注入;而其它的依赖关系的注入,则考虑设值注入。


一、构造器注入

这种方式的注入是指带有参数的构造函数注入,再定义属性类时,需要定义带有参数的构造器,属性值通过这一构造器注入到对象中

相对于使用setter方式进行注入,使用构造器注入时,明确了哪些属性是必须的,通过构造强制依赖关系,不可能实例化不完全的或无法使用的bean。

构造器注入主要有两种方式:

  • 使用<constructor-arg>元素

  • 使用Spring3.0引入的c命名空间

使用<construtor-arg>元素进行构造器注入时会使得xml配置文件相对繁琐,但有时能比使用c命名空间进行注入具有更多功能

1、使用<constructor-arg>元素进行注入

属性类:

public class SpringAction {
    //注入对象springDao
    private String name;
    private int salary;
    private User user;
    
    //此处必须提供含参数的构造函数用于注入相关属性
    public SpringAction(String name,int salary,User user){
        this.name = name;
        this.salary = salary;
        this.user = user;
        System.out.println("构造方法调用属性");
    }
        
        public void save(){
        ...
    }
}

配置文件beans.xml

<!--配置bean,配置后该类由spring管理-->
<bean name="user" class="com.bless.springdemo.vo.User"></bean>

--很久参数索引赋值--
<bean name="springAction" class="com.bless.springdemo.action.SpringAction">
     <constructor-arg index="0" value="刘晓刚" />
     <constructor-arg index="1" value="3500" />
     <constructor-arg index="2" ref="user"/>
</bean>

--根据参数类型赋值--
<bean name="springAction" class="com.bless.springdemo.action.SpringAction">
     <constructor-arg type="Java.lang.String" value="刘晓刚" />
     <constructor-arg type="java.lang.Intager" value="3500" />
     <constructor-arg type="com.bless.springdemo.vo.User" ref="user"/>
</bean>

--根据参数名称赋值--
<bean name="springAction" class="com.bless.springdemo.action.SpringAction">
     <constructor-arg name="name" value="刘晓刚" />
     <constructor-arg name="salary" value="3500" />
     <constructor-arg name="user" ref="user"/>
</bean>

--按照参数顺序直接赋值(value)--
<bean name="springAction" class="com.bless.springdemo.action.SpringAction">
     <constructor-arg value="刘晓刚" />
     <constructor-arg value="3500" />
     <constructor-arg ref="user"/>
</bean>

2、使用c命名空间注入

spring3.0版本后添加的注入方式,简化了xml配置文件内容

配置文件beans.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"
    xmlns:p="http://www.springframework.org/schema/c"       ////使用命名空间时,注意头文件这里要多出这一行
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

<!--配置bean,配置后该类由spring管理-->
<bean name="user" class="com.bless.springdemo.vo.User"></bean>

<bean name="springAction" class="com.bless.springdemo.action.SpringAction" 
      c:name-value="小明" 
      c:salary-value="2300"
      c:user-ref="user"/>

--也可以这样使用ref属性--
<bean name="springAction" class="com.bless.springdemo.action.SpringAction" 
      c:_-ref="user"/>    

--当只使用value属性时可以这样--
<bean name="springAction" class="com.bless.springdemo.action.SpringAction" 
      c:_value="小明" 
      c:_salary="2300"/>
      
<bean name="springAction" class="com.bless.springdemo.action.SpringAction" 
      c:_0="小明" 
      c:_1="2300"/>
      
</beans>

二、setter注入

1、常量和bean注入(使用<property>标签)

属性类:

public class SpringAction {
    //注入对象springDao
    private String name;
    private int salary;
    private User user;
    
    //此处一定要有属性的setter方法
    public void setName(String name) { 
        this.name = name; 
    } 
    
    public void setSalary(Salary salary) { 
        this.salary = salary; 
    } 
    
    public void setUser(User user) { 
        this.user = user; 
    } 
        
        public void save(){
        ...
    }
}

配置文件beans.xml

<!--配置bean,配置后该类由spring管理-->
<bean name="user" class="com.bless.springdemo.vo.User"></bean>

--很久参数索引赋值--
<bean name="springAction" class="com.bless.springdemo.action.SpringAction">
     <property index="0" value="jane"/>
     <property index="1" value="3500" />
     <property index="2" ref="user"/>
</bean>

--根据参数类型赋值--
<bean name="springAction" class="com.bless.springdemo.action.SpringAction">
     <property type="Java.lang.String" value="刘晓刚" />
     <property type="java.lang.Intager" value="3500" />
     <property type="com.bless.springdemo.vo.User" ref="user"/>
</bean>

--根据参数名称赋值--
<bean name="springAction" class="com.bless.springdemo.action.SpringAction">
     <property name="name" value="刘晓刚" />
     <property name="salary" value="3500" />
     <property name="user" ref="user"/>
</bean>

--按照参数顺序直接赋值(value)--
<bean name="springAction" class="com.bless.springdemo.action.SpringAction">
     <property value="刘晓刚" />
     <property value="3500" />
     <property ref="user"/>
</bean>

2、集合对象注入

配置文件beans.xml

<!--配置bean,配置后该类由spring管理-->
<bean name="user" class="com.bless.springdemo.vo.User"></bean>

--注入list参数--
<bean name="springAction" class="com.bless.springdemo.action.SpringAction">
     <property index="0">
        <list>
            <value>小红</value>
            <value>小明</value>
            <value>小刚</value>
        </list>
     </property>
</bean>

--在list中引用bean--
<bean name="springAction" class="com.bless.springdemo.action.SpringAction">
     <property index="0">
        <list>
            <ref bean="user"/>
            <ref bean="student"/>
        </list>
     </property>
</bean>

--注入map参数--
<bean name="springAction" class="com.bless.springdemo.action.SpringAction">
     <property name="map">
        <map>
            <entry key="name" value="小明"/>
            <entry key="name" value="小红"/>
            <entry key="name" value="小刚"/>
        </map>
     </property>
</bean>

--null注入--
<property name="wife"><null/></property>

3、p命名空间注入

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"       ////使用命名空间时,注意头文件这里要多出这一行
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

<!--配置bean,配置后该类由spring管理-->
<bean name="user" class="com.bless.springdemo.vo.User"></bean>

<bean name="springAction" class="com.bless.springdemo.action.SpringAction" 
      p:name="小明" 
      c:salary="2300"
      c:user-ref="user"/>
      
</beans>

4、使用util命名空间进行集合注入(了解,p命名空间不能注入集合)

三、工厂方法注入

1、静态工厂的方法注入

静态工厂顾名思义,就是通过调用静态工厂的方法来获取自己需要的对象,为了让spring管理所有对象,我们不能直接通过"工程类.静态方法()"来获取对象,而是依然通过spring注入的形式获取:

package com.bless.springdemo.factory; 

import com.bless.springdemo.dao.FactoryDao; 
import com.bless.springdemo.dao.impl.FactoryDaoImpl; 
import com.bless.springdemo.dao.impl.StaticFacotryDaoImpl; 

public class DaoFactory { 
    //静态工厂 
    public static final FactoryDao getStaticFactoryDaoImpl(){ 
        return new StaticFacotryDaoImpl(); 
    } 
}

同样看关键类,这里我需要注入一个FactoryDao对象,这里看起来跟第一种注入一模一样,但是看随后的xml会发现有很大差别

public class SpringAction { 
    //注入对象 
    private FactoryDao staticFactoryDao; 

    public void staticFactoryOk(){ 
        staticFactoryDao.saveFactory(); 
    } 
    //注入对象的set方法 
    public void setStaticFactoryDao(FactoryDao staticFactoryDao) { 
        this.staticFactoryDao = staticFactoryDao; 
    } 
} 

Spring的IOC配置文件,注意看<bean name="staticFactoryDao">指向的class并不是FactoryDao的实现类,而是指向静态工厂DaoFactory,并且配置 factory-method="getStaticFactoryDaoImpl"指定调用哪个工厂方法:

<!--配置bean,配置后该类由spring管理--> 
<bean name="springAction" class="com.bless.springdemo.action.SpringAction" > 
    <!--(3)使用静态工厂的方法注入对象,对应下面的配置文件(3)--> 
    <property name="staticFactoryDao" ref="staticFactoryDao"></property> 
    </property> 
</bean> 
<!--(3)此处获取对象的方式是从工厂类中获取静态方法--> 
<bean name="staticFactoryDao" class="com.bless.springdemo.factory.DaoFactory" factory-method="getStaticFactoryDaoImpl"></bean> 

4.实例工厂的方法注入

实例工厂的意思是获取对象实例的方法不是静态的,所以你需要首先new工厂类,再调用普通的实例方法:

public class DaoFactory { 
    //实例工厂 
    public FactoryDao getFactoryDaoImpl(){ 
        return new FactoryDaoImpl(); 
    } 
}

那么下面这个类没什么说的,跟前面也很相似,但是我们需要通过实例工厂类创建FactoryDao对象:

public class SpringAction { 
    //注入对象 
    private FactoryDao factoryDao; 
    
    public void factoryOk(){ 
        factoryDao.saveFactory(); 
    } 
    
    public void setFactoryDao(FactoryDao factoryDao) { 
        this.factoryDao = factoryDao; 
    } 
} 

最后看spring配置文件:

<!--配置bean,配置后该类由spring管理--> 
<bean name="springAction" class="com.bless.springdemo.action.SpringAction"> 
    <!--(4)使用实例工厂的方法注入对象,对应下面的配置文件(4)--> 
    <property name="factoryDao" ref="factoryDao"></property> 
</bean> 

<!--(4)此处获取对象的方式是从工厂类中获取实例方法--> 
<bean name="daoFactory" class="com.bless.springdemo.factory.DaoFactory"></bean> 
<bean name="factoryDao" factory-bean="daoFactory" factory-method="getFactoryDaoImpl"></bean> 

四、另外注意:

通过Spring创建的对象默认是单例的,如果需要创建多实例对象可以在<bean>标签后面添加一个属性:

<bean name="..." class="..." scope="prototype">  
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容