(4)Spring框架----依赖注入(DI)_spring 依赖注入 聚合还是组合-程序员宅基地

技术标签: spring  java  控制反转  依赖注入  # Spring  Bean  

Spring发展到现在遵循“约定大于配置”原则,在基本的应用配置上(如数据库配置),建议采用XML配置方式;在业务逻辑处理上,建议采用注解方式。回顾过往,XML配置方式真的让人又爱又恨。所以,本节通篇采用XML配置方式。

1.DI的配置使用

1.1  依赖和依赖注入

传统应用程序设计中所说的依赖一般指“类之间的关系”,那先让我们复习一下类之间的关系:

  • 泛化表示类与类之间的继承关系、接口与接口之间的继承关系;
  • 实现:表示类对接口的实现;
  • 依赖:当类与类之间有使用关系时就属于依赖关系,不同于关联关系,依赖不具有“拥有关系”,而是一种“相识关系”,只在某个特定地方(比如某个方法体内)才有关系。
  • 关联:表示类与类或类与接口之间的依赖关系,表现为“拥有关系”;具体到代码可以用实例变量来表示; 
  • 聚合:属于是关联的特殊情况,体现部分-整体关系,是一种弱拥有关系;整体和部分可以有不一样的生命周期;是一种弱关联;
  • 组合:属于是关联的特殊情况,也体现了体现部分-整体关系,是一种强“拥有关系”;整体与部分有相同的生命周期,是一种强关联;

1.2 IoC容器依赖的含义

Spring IoC容器的依赖有两层含义:Bean依赖容器容器注入Bean的依赖资源

  • Bean依赖容器:也就是说Bean要依赖于容器,这里的依赖是指容器负责创建Bean并管理Bean的生命周期,正是由于由容器来控制创建Bean并注入依赖,也就是控制权被反转了,这也正是IoC名字的由来,此处的有依赖是指Bean和容器之间的依赖关系
  • 容器注入Bean的依赖资源:容器负责注入Bean的依赖资源,依赖资源可以是Bean、外部文件、常量数据等,在Java中都反映为对象,并且由容器负责组装Bean之间的依赖关系,此处的依赖是指Bean之间的依赖关系可以认为是传统类与类之间的“关联”、“聚合”、“组合”关系

1.3 依赖注入的好处

为什么要应用依赖注入,应用依赖注入能给我们带来哪些好处呢?

  • 动态替换Bean依赖对象,程序更灵活:替换Bean依赖对象,无需修改源文件:应用依赖注入后,由于可以采用配置文件方式实现,从而能随时动态的替换Bean的依赖对象,无需修改java源文件;
  • 更好实践面向接口编程,代码更清晰:在Bean中只需指定依赖对象的接口,接口定义依赖对象完成的功能,通过容器注入依赖实现;
  • 更好实践优先使用对象组合,而不是类继承:因为IoC容器采用注入依赖,也就是组合对象,从而更好的实践对象组合。

       采用对象组合,Bean的功能可能由几个依赖Bean的功能组合而成,其Bean本身可能只提供少许功能或根本无任何功能,全部委托给依赖Bean,对象组合具有动态性,能更方便的替换掉依赖Bean,从而改变Bean功能;

       而如果采用类继承,Bean没有依赖Bean,而是采用继承方式添加新功能,,而且功能是在编译时就确定了,不具有动态性,而且采用类继承导致Bean与子Bean之间高度耦合,难以复用。

  • 增加Bean可复用性:依赖于对象组合,Bean更可复用且复用更简单;
  • 降低Bean之间耦合:由于我们完全采用面向接口编程,在代码中没有直接引用Bean依赖实现,全部引用接口,而且不会出现显示的创建依赖对象代码,而且这些依赖是由容器来注入,很容易替换依赖实现类,从而降低Bean与依赖之间耦合;
  • 代码结构更清晰:要应用依赖注入,代码结构要按照规约方式进行书写,从而更好的应用一些最佳实践,因此代码结构更清晰。

由此可以看出,其实依赖注入只是一种装配对象的手段,设计的类结构才是基础,如果设计的类结构不支持依赖注入,Spring IoC容器也注入不了任何东西,从而从根本上说“如何设计好类结构才是关键,依赖注入只是一种装配对象手段”。


2.依赖注入实现方式

前边IoC一章我们已经了解了Bean依赖容器,那容器如何注入Bean的依赖资源,Spring IoC容器注入依赖资源主要有以下两种基本实现方式:

  • 构造器注入:就是容器实例化Bean时注入那些依赖,通过在在Bean定义中指定构造器参数进行注入依赖,包括实例工厂方法参数注入依赖,但静态工厂方法参数不允许注入依赖;
  • setter注入:通过setter方法进行注入依赖;
  • 接口注入:

我们已经知道注入实现方式了,接下来让我们来看看具体配置吧。

2.1 构造方法注入

先定义一个汽车类

/**
 * 依赖注入
 * 【1】构造方法的注入方式
 */
public class Car {
	
	private String name;
	private Double price;
	
	public Car(String name, Double price) {
		this.name = name;
		this.price = price;
	}
	
	@Override
	public String toString() {
		return "Car [name=" + name + ", price=" + price + "]";
	}
	
}

XML配置:

<!-- ========================依赖注入注入的几种方式========================== -->
<!-- ========================1.使用构造方法的方式注入======================== -->
<!-- 常规构造方法注入 -->
<bean id="car" class="com.hl.spring.ioc.charcater4.Car" >
<!-- 
	<constructor-arg name="name" value="大奔"/>
	<constructor-arg name="price" value="100000"/>
-->
	<constructor-arg index="0" value="奇瑞QQ"/>
	<constructor-arg index="1" value="20000"/>
</bean>

汽车出来,总得有人去开车,造个人出来吧,Person类:

public class Person {
	
	private String name;
	private Car car;
	
	public Person(String name, Car car) {
		super();
		this.name = name;
		this.car = car;
	}

	@Override
	public String toString() {
		return "Person [name=" + name + ", car=" + car + "]";
	}
}

XML配置文件:

<!-- 构造方法注入,当Java类的属性是另一个Java类时 -->
<bean id="person" class="com.hl.spring.ioc.charcater4.Person">
	<constructor-arg name="name" value="xiaoming"/>
	<constructor-arg name="car" ref="car"/>
</bean>

2.2 set方法注入

/**
 * 依赖注入
 * 【2】set方法的注入方式
 */
public class Car2 {
	
	private String name;
	private Double price;
	
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Double getPrice() {
		return price;
	}

	public void setPrice(Double price) {
		this.price = price;
	}


	@Override
	public String toString() {
		return "Car [name=" + name + ", price=" + price + "]";
	}
}

XML配置:

<!-- ==========================2.使用Set方法的方式注入========================== -->
<bean id="car2" class="com.hl.spring.ioc.charcater4.Car2">
	<property name="name" value="赛特"/>
	<property name="price" value="998" />
</bean>

3. 测试

测试类:

public class Test01 {
	@Test
	public void run(){
		ApplicationContext context = 
				new ClassPathXmlApplicationContext("/character4.xml");
		
		/*【1】构造方法注入*/
		Car c = context.getBean("car",Car.class);
		System.out.println("构造方法注入:" + c);
		
		/*【1】构造方法注入,Java类的属性是另一个Java类时*/
		Person person = context.getBean("person",Person.class);
		System.out.println("构造方法注入:" + person);
		
		/*【2】set方法注入*/
		Car2 car2 = context.getBean("car2",Car2.class);
		System.out.println("set方法注入:" + car2);
	}
}

测试结果:

构造方法注入:Car [name=奇瑞QQ, price=2000.0]
构造方法注入:Person [name=xiaoming, car=Car [name=奇瑞QQ, price=2000.0]]
set方法注入:Car [name=赛特, price=998.0]

4. 注入复杂类型

Spring不仅能注入简单类型数据,还能注入集合(Collection、无序集合Set、有序集合List)类型、数组(Array)类型、字典(Map)类型数据、Properties类型数据,接下来就让我们一个个看看如何注入这些数据类型的数据。

/**
 * 复杂类型的注入
 */
public class CollectionBean {
	
	private String[] arrs;
	private List<String> list;
	private Set<String> set;
	private Map<String, Integer> map;
	private Properties properties;
	
	public void setArrs(String[] arrs) {
		this.arrs = arrs;
	}

	public void setSet(Set<String> set) {
		this.set = set;
	}

	public void setMap(Map<String, Integer> map) {
		this.map = map;
	}

	public void setProperties(Properties properties) {
		this.properties = properties;
	}

	public void setList(List<String> list) {
		this.list = list;
	}

	@Override
	public String toString() {
		return "CollectionBean [arrs=" + Arrays.toString(arrs) + ", list=" + list + ", set=" + set + ", map=" + map
				+ ", properties=" + properties + "]";
	}
}

 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:context="http://www.springframework.org/schema/context" 
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context.xsd"> 
	
    <!-- ===================依赖注入注入,注入复杂类型=========================== -->
    <!-- properties标签中的name名称需要与CollectionBean类中对应变量的名称一致 -->
    <bean id="collectionBean" class="com.hl.spring.ioc.charcater5.CollectionBean">
    <!-- ========================注入数组======================== -->
        <property name="arrs">
	    <list>
		<value>哈哈</value>
		<value>呵呵</value>
		<value>嘿嘿</value>
	    </list>
        </property>
     
    <!-- ========================注入list======================== -->
	<property name="list">
	    <list>
		<value>小雷</value>
		<value>小风</value>
		<value>小雨</value>
	    </list>
	</property>
	
    <!-- =======================注入set集合======================= -->
	<property name="set">
	    <set>
		<value>set</value>
		<value>集合</value>
	    </set>
	</property>
		
    <!-- =======================注入map集合======================= -->
	<property name="map">
	    <map>
		<entry key="map01" value="1"></entry>
		<entry key="map02" value="2"></entry>
		<entry key="map03" value="3"></entry>
	    </map>
	</property>
		
    <!-- ==================注入properties属性文件================== -->
	<property name="properties">
	    <props>
	        <prop key="uname">root</prop>
	        <prop key="pass">123</prop>
	    </props>
	</property>
    </bean>
    
    <!-- ===============依赖注入注入,引入其他的xml配置文件=============== -->
    <!-- <import resource="character2.xml"/> -->
    
</beans>

测试类:

public class Test05 {
	@Test
	public void run(){
		ApplicationContext context = 
				new ClassPathXmlApplicationContext("/character5.xml");
		CollectionBean bean = context.getBean("collectionBean",CollectionBean.class);
		System.out.println("复杂类型注入:" + bean);
	}
}

 测试结果:

复杂类型注入:
CollectionBean [
    arrs=[哈哈, 呵呵, 嘿嘿],
    list=[小雷, 小风, 小雨],
    set=[set, 集合], map={map01=1, map02=2, map03=3}, properties={uname=root, pass=123}
]

5. 配置简写

让我们来总结一下依赖注入配置及简写形式,其实我们已经在以上部分穿插着进行简化配置了:

5.1 构造器注入

1)常量值

简写:<constructor-arg index="0" value="常量"/>

全写:<constructor-arg index="0"><value>常量</value></constructor-arg>

2)引用

简写:<constructor-arg index="0" ref="引用"/>

全写:<constructor-arg index="0"><ref bean="引用"/></constructor-arg>


5.2 setter注入

1)常量值

 简写:<property name="message" value="常量"/>

 全写:<property name="message"><value>常量</value></ property>

2)引用

写:<property name="message" ref="引用"/>

全写:<property name="message"><ref bean="引用"/></ property>

3)数组:<array>没有简写形式

4)列表:<list>没有简写形式

5)集合:<set>没有简写形式

6)字典

简写:

<map>

             <entry key="键常量" value="值常量"/>

             <entry key-ref="键引用" value-ref="值引用"/>

</map>

全写:

<map>

             <entry><key><value>键常量</value></key><value>值常量</value></entry>

             <entry><key><ref bean="键引用"/></key><ref bean="值引用"/></entry>

</map>

7)Properties:没有简写形式


5.3 使用p命名空间简化setter注入

使用p命名空间来简化setter注入,具体使用如下:

<?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 id="bean1" class="java.lang.String">
        <constructor-arg index="0" value="test"/>
    </bean>
<bean id="idrefBean1" class="cn.javass.spring.chapter3.bean.IdRefTestBean" 
p:id="value"/>
<bean id="idrefBean2" class="cn.javass.spring.chapter3.bean.IdRefTestBean" 
p:id-ref="bean1"/>
</beans>
  • xmlns:p="http://www.springframework.org/schema/p" :首先指定p命名空间
  • <bean id="……" class="……" p:id="value"/> :常量setter注入方式,其等价于<property name="id" value="value"/>
  • <bean id="……" class="……" p:id-ref="bean1"/> :引用setter注入方式,其等价于<property name="id" ref="bean1"/>
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_27706119/article/details/88981726

智能推荐

Sandboxie v5.45.2正式版 系统安全工具_sandboxie系统安全工具-程序员宅基地

文章浏览阅读141次。简介:菜鸟高手裸奔工具沙盘Sandboxie是一款国外著名的系统安全工具,它可以让选定程序在安全的隔离环境下运行,只要在此环境中运行的软件,浏览器或注册表信息等都可以完整的进行清空,不留一点痕迹。同时可以防御些带有木马或者病毒的恶意网站,对于经常测试软件或者不放心的软件,可放心在沙盘里面运行!下载地址:http://www.bytepan.com/J7BwpqQdKzR..._sandboxie系统安全工具

Mac技巧|如何在 MacBook上设置一位数登录密码-程序员宅基地

文章浏览阅读230次,点赞4次,收藏5次。Mac老用户都知道之前的老版本系统是可以设置一位数登陆密码的,但是更新到10.14以后就不可以了,今天就教大家怎么在新版本下设置Mac一位数登陆密码。

chatgpt中的强化学习 PPO_chatgpt使用的强化学习-程序员宅基地

文章浏览阅读3.4k次。本该到此结束,但是上述实现的时候其实是把生成的每一步的奖励都使用统一的句子级reward,但该代码其实也额外按照每个token来计算奖励值的,为了获取每个token的奖励,我们在生成模型的隐层表示上,多加一个线性层,映射到一维,作为每个状态的预测奖励值。类似的,在文本生成中我们也可以用蒙特卡洛方法来估计一个模型的状态价值。假如我们只采样到了s1和s2,没有采样到s3,由于7和3都是正向奖励,s1和s2的训练后生成的概率都会变大,且s1的概率变的更大,这看似合理,但是s3是未参与训练的,它的概率反而减小了。_chatgpt使用的强化学习

获取不规则多边形中心点_truf计算重心-程序员宅基地

文章浏览阅读433次,点赞10次,收藏8次。尝试了3种方法,都失败了!_truf计算重心

HDU 1950最长上升子序列 学习nlogn_poj 1631 hdu 1950为啥是最长上升子序列-程序员宅基地

文章浏览阅读406次。学习LIS_poj 1631 hdu 1950为啥是最长上升子序列

kubernetes===》二进制安装_sed -ie 's#image.*#image: ${ epic_image_fullname }-程序员宅基地

文章浏览阅读550次。一、节点规划主机名称IP域名解析k8s-m-01192.168.12.51m1k8s-m-02192.168.12.52m2k8s-m-03192.168.12.53m3k8s-n-01192.168.12.54n1k8s-n-02192.168.12.55n2k8s-m-vip192.168.12.56vip二、插件规划#1.master节点规划kube-apiserverkube-controller-manage_sed -ie 's#image.*#image: ${ epic_image_fullname }#g

随便推点

UAC绕过提权_uac白名单 提权-程序员宅基地

文章浏览阅读106次。UAC绕过提权_uac白名单 提权

Linux一键部署OpenVPN脚本-程序员宅基地

文章浏览阅读664次,点赞7次,收藏12次。每次架设OpenVPN Server就很痛苦,步骤太多,会出错的地方也多,基本很少一次性成功的。

头文件的相互包含问题_多个头文件相互包含-程序员宅基地

文章浏览阅读397次。 今天看了继承以及派生类,并且运行了教程中的一个实例,但是仍然有好多坑。主要如下:建立了一个基类bClass以及由基类bClass派生的一个dClass,并且建立两个头文件.h分别申明这两个类,在cpp程序中进行运行来检验。具体程序如下:#ifndef ITEM_BASE//为避免类重复定义,需要在头文件的开头和结尾加上如这个所示 #define ITEM_BASEclass bClass..._多个头文件相互包含

python -- PyQt5(designer)安装详细教程-程序员宅基地

文章浏览阅读1.3w次,点赞19次,收藏88次。PyQt5安装详细教程,安装步骤很详细

微信小程序scroll-view去除滚动条-程序员宅基地

文章浏览阅读154次。官方文档:https://developers.weixin.qq.com/miniprogram/dev/component/scroll-view.html。_scroll-view去除滚动条

POJ-3233 Matrix Power Series 矩阵A^1+A^2+A^3...求和转化-程序员宅基地

文章浏览阅读146次。S(k)=A^1+A^2...+A^k.保利求解就超时了,我们考虑一下当k为偶数的情况,A^1+A^2+A^3+A^4...+A^k,取其中前一半A^1+A^2...A^k/2,后一半提取公共矩阵A^k/2后可以发现也是前一半A^1+A^2...A^k/2。因此我们可以考虑只算其中一半,然后A^k/2用矩阵快速幂处理。对于k为奇数,只要转化为k-1+A^k即可。n为矩阵数量,m为矩阵..._a^1 a^2 ... a^k