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 ...
随机推荐
- MySQL存储引擎的介绍
数据库存储引擎是数据库底层软件组件,数据库管理系统使用数据引擎进行创建.查询.更新和删除数据操作.不同的存储引擎提供不同的存储机制.索引技巧.锁定水平等功能,使用不同的存储引擎还可以获得特定的功能. ...
- javascript--清除表单缓存
表单缓存是指当用户在表单输入之后再次回到该表单或者刷新页面的时候,浏览器会直接显示用户之前的输入,即表单缓存下来了.多数情况下这正是我们想要的,但也有些情况我们希望表单能够刷新,特别是根据后台的数据刷 ...
- Java8常用示例
java.util.Map中的putIfAbsent.computeIfAbsent.computeIfPresent.compute的区别 探索Java8:(三)Predicate接口的使用 Has ...
- SQLSEVER刚建表时主键自增
alter table 表名 drop column ID alter table 表名 add ID int identity(1,1)
- php中long2ip和2iplong
<?php $string=ip2long("127.0.0.1"); $ip=long2ip($string); echo $string; echo "< ...
- jade属性怎么写
关于元素和标签,可能傻傻分不清楚,什么是元素,什么是标签,举个例子 比如div是一个块状元素,那么尖括号包起来的是标签,他用来标记这个元素,尖括号里面是元素名,元素是由开始和结束标签组成,用来包含内容 ...
- SHELL编程基础01
首先shell是在linux下运行的一种环境,它是以shell脚本来运行的,学会了它基本可以解决任何问题,也可以用shell脚本开发. 和java,python的相比,其弱类型的语言没有那么复杂的结构 ...
- 微服务,开源 RPC 框架 - Spring Cloud
Spring Cloud:国外 Pivotal 公司 2014 年对外开源的 RPC 框架,仅支持 Java 语言 Spring Cloud 利用 Spring Boot 特性整合了开源行业中优秀的组 ...
- 7月新的开始 - Axure学习05 - 元件库的创建
元件库的创建 元件库的创建.载入.编辑和删除等操作 元件库的后缀名名:.rplib 学习结果: 实现iPhone6的原型图 分辨率是 1080*1920,现在我们按比例缩小去实现(360*640)
- 杭电2019多校第八场 Acesrc and Good Numbers——思维打表&&oeis
题意 给定 $d,x$,$f(d,k)$ 表示 $1 \sim k$ 中 $d$ 出现的次数, $k$ 满足 $f(d,k) = k$,求小于 $x$ 的最大的 $k$. 分析 正解不会...,学习了 ...