Spring|IOC与DI
一、IOC
IOC(Inversion of Control),控制反转,是Spring的核心内容之一。
什么是“控制反转”?
【示例】
package com.my; /**
* @Author jyy
* @Description {}
* @Date 2018/7/16 11:29
*/
public class Person { private String id;
private String name;
private Address address; public Person() {
this.id = "1001";
this.name = "张三";
this.address = new Address();
}
}
如上定义了类Person,并在构造函数中对其属性进行赋值。这种方式虽然简单,但是代码的重用性不强,而且耦合度很高,所以我们可以做如下更改:
package com.my; /**
* @Author jyy
* @Description {}
* @Date 2018/7/16 11:29
*/
public class Person { private String id;
private String name;
private Address address; public Person(String id, String name, Address address) {
this.id = id;
this.name = name;
this.address = address;
}
}
这种方式,将类中属性赋值的权利,交由第三方。提高了代码的重用性,并降低了耦合度。基于这个思想,Spring为我们提供了另一种更加灵活的方式,代码如下:
package com.my; /**
* @Author jyy
* @Description {}
* @Date 2018/7/16 11:29
*/
public class Person { private String id;
private String name;
private Address address; public Person(String id, String name, Address address) {
this.id = id;
this.name = name;
this.address = address;
} public Person() {
} public String getId() {
return id;
} public void setId(String id) {
this.id = id;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public Address getAddress() {
return address;
} public void setAddress(Address address) {
this.address = address;
}
}
package com.my; /**
* @Author jyy
* @Description {}
* @Date 2018/7/16 11:30
*/
public class Address { private String country;
private String province;
private String city; public String getCountry() {
return country;
} public void setCountry(String country) {
this.country = country;
} public String getProvince() {
return province;
} public void setProvince(String province) {
this.province = province;
} public String getCity() {
return city;
} public void setCity(String city) {
this.city = city;
}
}
xml配置
<bean id="person" class="com.my.Person">
<constructor-arg type="java.lang.String" value="1001"/>
<constructor-arg type="java.lang.String" value="张三"/>
<constructor-arg type="com.my.Address" ref="address"/>
</bean> <bean id="address" class="com.my.Address">
<property name="country" value="中国"/>
<property name="province" value="江苏省"/>
<property name="city" value="南京市"/>
</bean>
package com.my; import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext; /**
* @Author jyy
* @Description {}
* @Date 2018/7/13 10:06
*/
public class MainApp {
public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
Person person = (Person) context.getBean("person");
System.out.println(person.getName());
Address address = person.getAddress();
System.out.println(address.getCountry() + "-" + address.getProvince() + "-" + address.getCity()); }
}
执行结果:
张三
中国-江苏省-南京市
Spring提供的这种方式将传统上由程序代码直接操控的对象的调用权交给外部容器,通过容器来实现对象组件的装配和管理。
所谓的“控制反转”概念就是组件对象的控制权转移了,从程序代码本身转移到了外部容器。
IOC最常见的一种应用场景,就是配置数据库连接。我们将操作数据库的对象交由容器进行统一管理。
二、IOC容器
Spring容器是Spring框架的核心,容器创建对象,把它们连接在一起,配置它们,并管理他们的整个生命周期从创建到销毁。Spring 容器使用依赖注入(DI)来管理组成一个应用程序的组件。这些被创建的对象被称为Spring Beans。
通过阅读配置元数据提供的指令,容器知道对哪些对象进行实例化,配置和组装。配置元数据可以通过 XML或 Java 代码(参考3.4)来表示。
Spring IOC容器利用Java的POJO类和配置元数据来生成Spring Beans。
2.1、IOC容器-BeanFactory
这是一个最简单的容器,它主要的功能是为依赖注入 (DI) 提供支持,这个容器接口在 org.springframework.beans.factory.BeanFactory 中被定义。 BeanFactory 和相关的接口,比如BeanFactoryAware、 DisposableBean、InitializingBean,仍旧保留在 Spring 中,主要目的是向后兼容已经存在的和那些 Spring 整合在一起的第三方框架。
在 Spring 中,有大量对 BeanFactory 接口的实现。其中,最常被使用的是 XmlBeanFactory 类。这个容器从一个 XML 文件中读取配置元数据,由这些元数据来生成一个被配置化的系统或者应用。
在资源宝贵的移动设备或者基于 applet 的应用当中, BeanFactory 会被优先选择。否则,一般使用的是 ApplicationContext,除非你有更好的理由选择 BeanFactory。
【示例】
XmlBeanFactory factory = new XmlBeanFactory(new ClassPathResource("Beans.xml"));
Person person = (Person) factory.getBean("person");
System.out.println(person.getName());
2.2、IOC容器-ApplicationContext
Application Context 是 spring 中较高级的容器。和 BeanFactory 类似,它可以加载配置文件中定义的 bean,将所有的 bean 集中在一起,当有请求的时候分配 bean。 另外,它增加了企业所需要的功能,比如,从属性文件中解析文本信息和将事件传递给所指定的监听器。这个容器在 org.springframework.context.ApplicationContext interface接口中定义。
ApplicationContext 包含 BeanFactory 所有的功能,一般情况下,相对于 BeanFactory,ApplicationContext 会更加优秀。当然,BeanFactory 仍可以在轻量级应用中使用,比如移动设备或者基于 applet 的应用程序。
最常被使用的 ApplicationContext 接口实现:
FileSystemXmlApplicationContext:该容器从 XML 文件中加载已被定义的 bean。在这里,你需要提供给构造器 XML 文件的完整路径。
ClassPathXmlApplicationContext:该容器从 XML 文件中加载已被定义的 bean。在这里,你不需要提供 XML 文件的完整路径,只需正确配置 CLASSPATH 环境变量即可,因为,容器会从 CLASSPATH 中搜索 bean 配置文件。
WebXmlApplicationContext:该容器会在一个 web 应用程序的范围内加载在 XML 文件中已被定义的 bean。
//负责生成和初始化所有的对象,即所有在 XML bean 配置文件中的 bean。
ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
//利用 getBean() 方法得到所需要的 bean。 这个方法通过配置文件中的 bean ID 来返回一个真正的对象。一旦得到这个对象,就可以利用这个对象来调用任何方法。
Person person = (Person) context.getBean("person");
System.out.println(person.getName());
Address address = person.getAddress();
System.out.println(address.getCountry() + "-" + address.getProvince() + "-" + address.getCity());
三、DI
DI(Dependency Injection),依赖注入,控制反转(IOC)是一个通用的概念,它可以用许多不同的方式去表达,依赖注入仅仅是控制反转的一个具体的例子。
3.1、什么是依赖注入?
让我们将这两个词分开来看一看。这里将依赖关系部分转化为两个类之间的关联。例如,类 A 依赖于类 B。现在,让我们看一看第二部分,注入。这意味着类 B 将通过 IOC 被注入到类 A 中。
依赖注入可以向构造函数传递参数的方式发生,或者通过使用 setter 方法 post-construction。
【举例】
在第一个示例中,我们已经使用构造函数进行依赖注入,下面我们说第二种方法:
package com.my; /**
* @Author jyy
* @Description {}
* @Date 2018/7/16 11:29
*/
public class Person { private String id;
private String name;
private Address address; public Person() {
} public String getId() {
return id;
} public void setId(String id) {
this.id = id;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public Address getAddress() {
return address;
} public void setAddress(Address address) {
this.address = address;
}
}
package com.my; /**
* @Author jyy
* @Description {}
* @Date 2018/7/16 11:30
*/
public class Address { private String country;
private String province;
private String city; public String getCountry() {
return country;
} public void setCountry(String country) {
this.country = country;
} public String getProvince() {
return province;
} public void setProvince(String province) {
this.province = province;
} public String getCity() {
return city;
} public void setCity(String city) {
this.city = city;
}
}
<?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-3.0.xsd"> <bean id="person" class="com.my.Person">
<property name="id" value="1001"/>
<property name="name" value="张三"/>
<property name="address" ref="address"/>
</bean> <bean id="address" class="com.my.Address">
<property name="country" value="中国"/>
<property name="province" value="江苏省"/>
<property name="city" value="南京市"/>
</bean>
</beans>
package com.my; import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext; /**
* @Author jyy
* @Description {}
* @Date 2018/7/13 10:06
*/
public class MainApp {
public static void main(String[] args) { //负责生成和初始化所有的对象,即所有在 XML bean 配置文件中的 bean。
ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
//利用 getBean() 方法得到所需要的 bean。 这个方法通过配置文件中的 bean ID 来返回一个真正的对象。一旦得到这个对象,就可以利用这个对象来调用任何方法。
Person person = (Person) context.getBean("person");
System.out.println(person.getName());
Address address = person.getAddress();
System.out.println(address.getCountry() + "-" + address.getProvince() + "-" + address.getCity()); }
}
输出结果:
张三
中国-江苏省-南京市
两种方式的返回结果一致
3.2、自动装配-依赖注入
Spring 容器可以在不使用<constructor-arg>和<property>元素的情况下自动装配相互协作的 bean 之间的关系,这有助于减少编写一个大的基于 Spring 的应用程序的 XML 配置的数量。
其中较常用的两种方式,“byName”和“byType”
【举例】
“byName”,配置文件中的属性autowire=“byName”,并且Person类中包含属性address,及其setAddress(..)方法,那么spring就会查找配置文件中id=“address”的bean进行自动装配。
<?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-3.0.xsd"> <bean id="person" class="com.my.Person" autowire="byName">
<property name="id" value="1001"/>
<property name="name" value="张三"/>
</bean> <bean id="address" class="com.my.Address">
<property name="country" value="中国"/>
<property name="province" value="江苏省"/>
<property name="city" value="南京市"/>
</bean>
</beans>
“byType”,配置文件中的属性autowire=“byType”,并且Person类中包含属性address,及其setAddress(..)方法。address由Address类声明,那么spring就会查找配置文件中类型为Address类的bean进行自动装配。
<?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-3.0.xsd"> <bean id="person" class="com.my.Person" autowire="byType">
<property name="id" value="1001"/>
<property name="name" value="张三"/>
</bean> <bean id="address" class="com.my.Address">
<property name="country" value="中国"/>
<property name="province" value="江苏省"/>
<property name="city" value="南京市"/>
</bean>
</beans>
3.3、注解装配-依赖注入
从 Spring 2.5 开始就可以使用注解来配置依赖注入。注解默认情况下在 Spring 容器中不打开。因此,在可以使用基于注解之前,我们将需要在我们的 Spring 配置文件中启用它。
<!--开启注解-->
<context:annotation-config/>
@Required:作用于属性的setter方法,标明属性必须在配置文件中声明,否则会抛出异常。
@Required
public void setAddress(Address address) {
this.address = address;
}
<?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-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!--开启注解-->
<context:annotation-config/> <bean id="person" class="com.my.Person">
<property name="id" value="1001"/>
<property name="name" value="张三"/>
<!--<property name="address" ref="address"/>-->
</bean> <bean id="address" class="com.my.Address">
<property name="country" value="中国"/>
<property name="province" value="江苏省"/>
<property name="city" value="南京市"/>
</bean>
</beans>
输出结果:抛出“BeanInitializationException”异常
@Autowired:可以作用于类的构造方法,属性的setter方法及属性本身。利用“byType”模式,实现属性值的自动装配。
package com.my; import org.springframework.beans.factory.annotation.Autowired; /**
* @Author jyy
* @Description {}
* @Date 2018/7/16 11:29
*/
public class Person { private String id;
private String name;
private Address address; public Person() {
} public String getId() {
return id;
} public void setId(String id) {
this.id = id;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public Address getAddress() {
return address;
} @Autowired
public void setAddress(Address address) {
this.address = address;
}
}
<?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-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!--开启注解-->
<context:annotation-config/> <bean id="person" class="com.my.Person">
</bean> <bean id="address" class="com.my.Address">
<property name="country" value="中国"/>
<property name="province" value="江苏省"/>
<property name="city" value="南京市"/>
</bean>
</beans>
执行结果:
null
null
中国-江苏省-南京市
虽然配置文件中,没有配置address属性与com.my.Address之间的关联关系,但是通过注解@Autowired,实现属性的自动装配。
我们也可以在属性本身及类构造函数上声明@Autowired,例如:
@Autowired
private Address address;
@Autowired
public Person(Address address) {
this.address = address;
}
@Qualifier:当创建多个类型相同的bean时,@Autowired就不能唯一确定该调用哪个bean进行自动装配,这个时候就需要@Qualifier来消除混乱。
<?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-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!--开启注解-->
<context:annotation-config/> <bean id="person" class="com.my.Person">
</bean> <bean id="address1" class="com.my.Address">
<property name="country" value="中国"/>
<property name="province" value="江苏省"/>
<property name="city" value="南京市"/>
</bean> <bean id="address2" class="com.my.Address">
<property name="country" value="中国"/>
<property name="province" value="江苏省"/>
<property name="city" value="苏州市"/>
</bean>
</beans>
@Autowired
@Qualifier("address2")
private Address address;
执行结果:
中国-江苏省-苏州市
@Resource:区别于上面的注解,此注解是jdk提供的,不是spring的特性。可以作用于属性及其setter方法,利用“byName”模式,实现属性值的自动装配。
@Resource(name="address1")
private Address address;
执行结果:
中国-江苏省-南京市
也可以直接使用 @Resource,而不声明name,这时就通过属性名进行查找。
3.4、基于java的配置元数据
以上的配置均是基于XML配置元数据,下面我们将演示如何通过代码及注解完成元数据配置。
package com.my; /**
* @Author jyy
* @Description {}
* @Date 2018/7/16 11:29
*/
public class Person { private String id;
private String name;
private Address address; public Person() {
} public Person(Address address) {
this.address = address;
} public String getId() {
return id;
} public void setId(String id) {
this.id = id;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public Address getAddress() {
return address;
} public void setAddress(Address address) {
this.address = address;
}
}
package com.my; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; /**
* @Author jyy
* @Description {}
* @Date 2018/8/2 17:49
*/
@Configuration
public class AnnotationConfig { @Bean
public Person person() {
return new Person(address());
} @Bean
public Address address() {
return new Address("中国", "江苏", "无锡");
}
}
package com.my; import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext; /**
* @Author jyy
* @Description {}
* @Date 2018/7/13 10:06
*/
public class MainApp { public static void main(String[] args) { ApplicationContext ctx = new AnnotationConfigApplicationContext(AnnotationConfig.class);
Person person = ctx.getBean(Person.class);
Address address = person.getAddress();
System.out.println(address.getCountry() + "-" + address.getProvince() + "-" + address.getCity()); }
}
输出结果:
中国-江苏-无锡
带有 @Configuration 的注解类表示这个类可以使用 Spring IoC 容器作为 bean 定义的来源。@Bean 注解告诉 Spring,一个带有 @Bean 的注解方法将返回一个对象,该对象应该被注册为在 Spring 应用程序上下文中的 bean。
在这里,带有 @Bean 注解的方法名称作为 bean 的 ID,它创建并返回实际的 bean,比如address()中的“address”为bean的ID。
Spring|IOC与DI的更多相关文章
- Spring IoC 和 DI 简介(二)
Spring IoC 和 DI 简介 IoC:Inverse of Control(控制反转) 读作“反转控制”,更好理解,不是什么技术,而是一种设计思想,就是将原本在程序中手动创建对象的控制权,交由 ...
- Spring 学习教程(一):浅谈对Spring IOC以及DI的理解
一.个人对IoC(控制反转)和DI(依赖注入)的理解我们平时在开发java web程序的时候,每个对象在需要使用它的合作对象时,自己都要将它要合作对象创建出来(比如 new 对象),这个合作对象是由自 ...
- Spring IOC(DI)
软件152 余建强 1 什么是IOC IOC—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想.在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不 ...
- (转)spring IOC、DI理解
转自: http://www.cnblogs.com/xdp-gacl/p/4249939.html 个人理解: IOC控制反转,反转的是获取依赖对象的方式.传统的应用在存在依赖关系时,比如A依赖于B ...
- 对Spring Ioc 以及DI的精彩理解
转:http://blog.csdn.net/cyjs1988/article/details/50352916 学习过spring框架的人一定都会听过Spring的IoC(控制反转) .DI(依赖注 ...
- Spring IoC与DI(依赖注入)
Spring Ioc 所谓控制反转,即将传统的需要代码进行的操作,交由Spring容器来做.下面以添加book为例进行比较一下: BookService.java public interface B ...
- 利用递归,反射,注解等,手写Spring Ioc和Di 底层(分分钟喷倒面试官)了解一下
再我们现在项目中Spring框架是目前各大公司必不可少的技术,而大家都知道去怎么使用Spring ,但是有很多人都不知道SpringIoc底层是如何工作的,而一个开发人员知道他的源码,底层工作原理,对 ...
- Spring基础03——Spring IOC和DI概述
1.什么是IOC与DI IOC(Inversion of Control):其思想是反转资源获取方向,传统的资源查找方式要求组件想容器发起请求查找资源,作为回应,容器适时的返回资源,而应用了IOC之后 ...
- Spring IoC、DI入门小程序
Alt+/智能提示xml配置文件节点及属性:在接口上使用Ctrl+T可以提示其实现类 一.IoC控制反转(将创建对象的权利交给spring)入门小程序 1.引入jar包 2.工程基本结构 3.新建Us ...
随机推荐
- git一些简单运用
1.删除本地文件后,继续从远处仓库拉取回来,提示up-to-date,执行如下 git reset --hard origin/master 待补充
- element-ui 省市区联动组件 el-cascader
<el-form-item label="省市 :" prop="description"> <el-cascader size=" ...
- 原生js实现ajax封装
一.什么是ajax? 定义:Ajax(Asynchronous Java and XML的缩写)是一种异步请求数据的web开发技术,在不需要重新刷新页面的情况下,Ajax 通过异步请求加载后台数据,并 ...
- sed 追加文件内容
追加用法总结 1.a 在匹配行后面追加 2.i 在匹配行前面追加 3.r 将文件内容追加到匹配行后面 4.w 将匹配行写入指定文件 在匹配行后面追加 a passwd文件第10行后面追加"A ...
- java 扫描微信公众号二维码,关注并登录逻辑
场景:户扫描微信公众号的二维码,关注后自动登录网站,若已关注则直接登录. 逻辑: 系统生成带参数的临时二维码:参数 scene_str 自定义为唯一值(可以是uuid),临时二维码的生成方式参照官方接 ...
- mamcached+(magent+keepalived高可用)搭建及理论概述
目录 一.理论概述 工作流程 二.部署 环境 环境概述 部署 三.测试 四.总结 一.理论概述 Memcached服务器端与PHP-Memcache客户端安装配置_服务器应用_Linux公社-Linu ...
- charles 手机证书下载安装
本文参考:charles 手机证书下载安装 本文的Charles,适应windows/MAC/IOS/Android,避免抓包HTTPS失败和乱码: 用的版本是V4.1.2,其它版本原理类似: cha ...
- SpringBoot之多数据源动态切换数据源
原文:https://www.jianshu.com/p/cac4759b2684 实现 1.建库建表 首先,我们在本地新建三个数据库名分别为master,slave1,slave2,我们的目前就是写 ...
- 第七届蓝桥杯C/C++程序设计本科B组决赛 ——棋子换位(代码补全题)
棋子换位 有n个棋子A,n个棋子B,在棋盘上排成一行.它们中间隔着一个空位,用“.”表示,比如: AAA.BBB 现在需要所有的A棋子和B棋子交换位置.移动棋子的规则是:1. A棋子只能往右边移动,B ...
- export default 和 export 的使用方式
node中导入模块:var 名称 = require('模块标识符') node中向外暴露成员的形式:module.exports = {} 在ES6中,也通过规范的形式,规定了ES6中如何导入和导出 ...