第二十七天 春之细雨润物于无形 —Spring的依赖注入

6月11日,晴。“夏条绿已密,朱萼缀明鲜。炎炎日正午,灼灼火俱燃。”

IT人习惯把具体的事物加工成的形状一致的类,正是这样的一致,加上合适的规范,才能彰显对象筋道的牙感和bean清香的味道。Spring比谁都清楚OO的奥妙,让组件之间的依赖关系由容器在运行时期决定,称作依赖注入(Dependency Injection)。

下面用一通俗的例子,一探依赖注入奥妙。

设计模式中的一个原则:针对接口编程,不要针对实现编程。

一、设计两个接口:

(1)奶制品接口—MilkProductInterface.java

package edu.eurasia.IOC;

public interface MilkProductInterface {
   public void drinkMilk();
   public void eatCheese();
}

(2)奶制品接口的实现类—Milk.java

package edu.eurasia.IOC;

public class Milk implements MilkProductInterface {

	public void drinkMilk() {
		System.out.println("新鲜的牛奶真好喝!");
	}

	public void eatCheese() {
		System.out.println("只有鲜奶,无奶酪o(╯□╰)o");
	}
}

(3)奶制品接口的实现类—Cheese.java

package edu.eurasia.IOC;

public class Cheese implements MilkProductInterface {

	public void drinkMilk() {
		System.out.println("只有奶酪,无鲜奶o(╯□╰)o");

	}

	public void eatCheese() {
		System.out.println("发酵后的美味奶酪真好吃!");

	}
}

(4)人的接口—PersonInterface.java

package edu.eurasia.IOC;

public interface PersonInterface {
	public void drink();
	public void eat();
}

(5)人接口的实现类—Person.java

正常情况下,人要喝牛奶,吃奶酪,这样写就ok了。

package edu.eurasia.IOC;

public class Person implements PersonInterface {
	public void drink() {
		MilkProductInterface milk = new Milk();
		milk.drinkMilk();
	}

	public void eat() {
		MilkProductInterface cheese = new Cheese();
		cheese.eatCheese();
	}
}

(6)测试类—TestIOC.java

package edu.eurasia.IOC;

import org.junit.Test;

public class TestIOC {
	@Test
	public void eatdrink(){
	   PersonInterface p = new Person();
    	   p.drink();
    	   p.eat();
	}
}

结果输出:新鲜的牛奶真好喝!

发酵后的美味奶酪真好吃!

上述代码,很多初学者看不出来有什么问题。但是,类是一样的类,一“new”见高下。使用“new”的时候,就已经在实例化一个具体类了,这就是一种实现,只要有具体类的出现,就会导致代码更缺乏弹性。重温设计模式中的一个原则:针对接口编程,不要针对实现编程。

二、依赖注入(Set):

(1)修改人接口的实现类—Person.java

package edu.eurasia.IOC;

public class Person implements PersonInterface {
	private MilkProductInterface milkX;

	public void setMilkX(MilkProductInterface milkX) {
		this.milkX = milkX;
	}

	@Override
	public void drink() {
		milkX.drinkMilk();
	}

	@Override
	public void eat() {
		milkX.eatCheese();

	}
}

声明一个MilkProductInterface接口变量milkX,这个milkX代表任意一种具体的奶制品,可能是牛奶milk,也可能是奶酪cheese,还有可能是奶茶milky
tea...,总之,我们可以动态的在运行时改变它的具体属性。怎么改?别着急,你发现了milkX的set方法,哈哈,这就是大名鼎鼎的Set注入(setter
injection)

看到了注入,那么依赖(Dependency)在哪里?

        我们对着雾霾喊: 依赖,我们的好DI, 你在哪里呵,你在哪里? 你可知道,我们想念你...
         雾霾翻滚: 它就在配置文件里...

(2)编写配置文件—applicationContext.xml,放在src下面。

<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-2.5.xsd">

	<bean id="milk" class="edu.eurasia.IOC.Milk"></bean>
	<bean id="cheese" class="edu.eurasia.IOC.Cheese"></bean>

	<bean id="person" class="edu.eurasia.IOC.Person">
		<property name="milkX" ref="milk"></property>
	</bean>
</beans> 

配置文件是Spring依赖注入的关键:

首先,定义两个bean,第一个bean的id是milk, class指定该bean实例的实现类;第二个bean的id是cheese,class同样指定该bean实例的实现类。这两个bean是为下面的Bean提供引用注入的。

其次,再定义一个bean,bean的id是person,
class指定该bean实例的实现类。property元素用来指定需要容器注入的属性,属性名milkX要与Person.java中的属性一致,ref引用要注入的值即上面定义的任一个id。不同的id,就是注入的不同值,因此Person类必须拥有setMilkX方法。说了半天,灵活性就在这里体现了。

从配置文件中,可以看到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属性完成。

注:

1、spring配置文件中Bean中的id和name的区别

id:鼓励使用ID属性来标识一个Bean, 不能重名,可以被DTD验证,不能以数字,符号打头,不能有空格;

name: 可以重名, 后面的覆盖前面。

      2、JavaBean关于属性命名的特殊规范

Spring配置文件中<property>元素所指定的属性名和Bean实现类的Setter方法满足Sun JavaBean的属性命名规范:xxx的属性对应setXxx()方法。

一般情况下,Java的属性变量名都以小写字母开头,如maxSpeed、brand等。但也存在特殊的情况,考虑到一些特殊意义的大写英文缩略词(如USA、XML等),JavaBean也允许大写字母开头的属性变量名,但必须满足“变量的前两个字母要么全部大写,要么全部小写”的要求,如brand、ICCard是合法的,而iCiCCard是非法的。

(3)改写测试类—TestIOC.java

package edu.eurasia.IOC;

import org.junit.Test;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestIOC {
	@Test
	public void eatdrink(){
	    AbstractApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
	    Person p =  (Person) context.getBean("person");
	    p.drink();
	    p.eat();
	}
}

程序实例化Spring的上下文,通过Person bean的id来获取bean实例,从而实现面向接口编程。

结果输出:新鲜的牛奶真好喝!

只有鲜奶,无奶酪o(╯□╰)o

更改applicationContext.xml里面的ref="cheese"。

此时再次执行程序,将得到如下结果:

只有奶酪,无鲜奶o(╯□╰)o

发酵后的美味奶酪真好吃!

采用setter方法为目标bean注入属性的方式,称为设值注入。此外,还有一种叫构造注入,所谓构造注入,指通过构造函数来完成依赖关系的设定,而不是通过setter方法。

三、依赖注入(构造):

(1)修改人接口的实现类—Person.java

package edu.eurasia.IOC;

public class Person implements PersonInterface {

	private MilkProductInterface milkX;

	public Person(MilkProductInterface milkX) {
		super();
		this.milkX = milkX;
	}

	@Override
	public void drink() {

		milkX.drinkMilk();
	}

	@Override
	public void eat() {
		milkX.eatCheese();

	}
}

(2)修改配置文件—applicationContext.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-2.5.xsd">

	<bean id="milk" class="edu.eurasia.IOC.Milk"></bean>
	<bean id="cheese" class="edu.eurasia.IOC.Cheese"></bean>

	<bean id="person" class="edu.eurasia.IOC.Person">
		<!-- <property name="milkX" ref="milk"></property> -->
	     <constructor-arg ref="milk"></constructor-arg>
	</bean>
</beans> 

执行效果与使用设值注入时的执行效果完全一样。区别在于:创建Person实例中属性的时机不同—设值注入是现创建一个默认的bean实例,然后调用对应的构造方法注入依赖关系。而构造注入则在创建bean实例时,已经完成了依赖关系。

第二十七天 春之细雨润物于无形 —Spring的依赖注入,布布扣,bubuko.com

时间: 2024-10-26 20:19:20

第二十七天 春之细雨润物于无形 —Spring的依赖注入的相关文章

centos samba/squid 配置 第二十七节课

centos  samba/squid 配置  第二十七节课 上半节课 下半节课 一. samba配置1. 什么是sambaSamba服务类似于windows上的共享功能,可以实现在Linux上共享文件,windows上访问,当然在Linux上也可以访问到.是一种在局域网上共享文件和打印机的一种通信协议,它为局域网内的不同计算机之间提供文件及打印机等资源的共享服务. 注意:smb侦听端口:139  和  445  端口 2. 安装配置samba yum install -y samba samb

Gradle 1.12用户指南翻译——第二十七章. Ear 插件

其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Github上的地址: https://github.com/msdx/gradledoc/tree/1.12. 直接浏览双语版的文档请访问: http://gradledoc.qiniudn.com/1.12/userguide/userguide.html. 另外,Android 手机用户可通过我写的一个程序浏览文档,带缓存功能的,目前

javaSE第二十七天

第二十七天????447 1:反射(理解)????447 (1)类的加载及类加载器????447 (2)反射:????448 A:定义????448 B:获取字节码对象的三种方式????449 (3)反射的使用????450 A:通过反射获取构造方法并使用????450 1).反射获取构造方法的介绍????450 2)通过反射去获取该构造方法并使用????451 3)通过反射获取私有构造方法并使用????452 B:通过反射获取成员变量并使用????452 C:通过反射获取成员方法并使用????

“全栈2019”Java多线程第二十七章:Lock获取lock/释放unlock锁

难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多线程第二十七章:Lock获取lock/释放unlock锁 下一章 "全栈2019"Java多线程第二十八章:公平锁与非公平锁详解 学习小组 加入同步学习小组,共同交流与进步. 方式一:关注头条号Gorhaf,私信"Java学习小组". 方式二:关注公众号Gorhaf,回

第二十七个知识点:什么是对称密码加密的AEAD安全定义?

第二十七个知识点:什么是对称密码加密的AEAD安全定义? AEAD 在之前的博客里,Luke描述了一种被广泛使用的操作模式(ECB,CBC和CTR)对块密码.我们也可能会想我们加密方案的完整性,完整性意味着接收到的消息就是发送的消息,在信道中没有意外修改或者恶意修改,真实性意味着接收者确实是收到了发送者的消息.为了使用这些特性,我们通常使用消息认证码(MAC),那些基于hash的消息认证码叫做HMAC.把这两个密码原语放在一起不是平凡的:为了获得IND-CCA安全方案,我们需要遵循加密然后MAC

第二十七天反射和面向对像中的内置函数

1.isinstance判断类和对象之间的关系如果存在类对象关系返回True否则返回False class Person: def func(self): pass a=Person() b=1 print(isinstance(a,Person)) #左边对象右边类 print(isinstance(b,Person)) 结果为 True False 2.issubclass判断是否是子类和父类之间的关系: class Person: def func(self): pass class So

【无私分享:ASP.NET CORE 项目实战(第二章)】添加EF上下文对象,添加接口、实现类以及无处不在的依赖注入(DI)

目录索引 [无私分享:ASP.NET CORE 项目实战]目录索引 简介 上一章,我们介绍了安装和新建控制器.视图,这一章我们来创建个数据模型,并且添加接口和实现类. 添加EF上下文对象 按照我们以前的习惯,我们还是新建几个文件夹 Commons:存放帮助类 Domians:数据模型 Services:接口和实现类 我们在Domains文件夹下添加一个类库 Domain 我们新建一个类 ApplicationDbContext 继承 DbContext 1 using Microsoft.Ent

【SSH三大框架】Spring基础第二篇:Spring依赖注入的三种方式

控制反转(Inversion of Control)和依赖注入(Dependency Injection): 应用控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它.也可以说,依赖被注入到对象中.所以,控制反转是,关于一个对象如何获取他所依赖的对象的引用,这个责任的反转. 对于依赖注入,有三种方式: 1.使用属性的setter方法注入 2.使用构造器注入 3.使用注解注入 下面我们介绍下这三种方式: 一.使用属性的setter方法注入 首先,我们写一个

第二十七课

第一单元语法部分        Vて行く Vて来る「行く.来る」有方向性.以某物为参照物.接在「Vて」形式后面,表示完成V动作后进行方向性移动.     Vていく    ①表示移动时的状态.“…着去,…去”        例:学校まで走っていこう.    ②表示远离说话人而去.         例:船はどんどん遠くに離れていった.    ③表示做完某动作行为再去.“…了再去”        例:母の誕生日だから.途中で花を買って行きました.    ④以某一时间为基准,变化或动作继续下去.“…下去