一、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的更多相关文章

  1. Spring IoC 和 DI 简介(二)

    Spring IoC 和 DI 简介 IoC:Inverse of Control(控制反转) 读作“反转控制”,更好理解,不是什么技术,而是一种设计思想,就是将原本在程序中手动创建对象的控制权,交由 ...

  2. Spring 学习教程(一):浅谈对Spring IOC以及DI的理解

    一.个人对IoC(控制反转)和DI(依赖注入)的理解我们平时在开发java web程序的时候,每个对象在需要使用它的合作对象时,自己都要将它要合作对象创建出来(比如 new 对象),这个合作对象是由自 ...

  3. Spring IOC(DI)

    软件152 余建强 1 什么是IOC IOC—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想.在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不 ...

  4. (转)spring IOC、DI理解

    转自: http://www.cnblogs.com/xdp-gacl/p/4249939.html 个人理解: IOC控制反转,反转的是获取依赖对象的方式.传统的应用在存在依赖关系时,比如A依赖于B ...

  5. 对Spring Ioc 以及DI的精彩理解

    转:http://blog.csdn.net/cyjs1988/article/details/50352916 学习过spring框架的人一定都会听过Spring的IoC(控制反转) .DI(依赖注 ...

  6. Spring IoC与DI(依赖注入)

    Spring Ioc 所谓控制反转,即将传统的需要代码进行的操作,交由Spring容器来做.下面以添加book为例进行比较一下: BookService.java public interface B ...

  7. 利用递归,反射,注解等,手写Spring Ioc和Di 底层(分分钟喷倒面试官)了解一下

    再我们现在项目中Spring框架是目前各大公司必不可少的技术,而大家都知道去怎么使用Spring ,但是有很多人都不知道SpringIoc底层是如何工作的,而一个开发人员知道他的源码,底层工作原理,对 ...

  8. Spring基础03——Spring IOC和DI概述

    1.什么是IOC与DI IOC(Inversion of Control):其思想是反转资源获取方向,传统的资源查找方式要求组件想容器发起请求查找资源,作为回应,容器适时的返回资源,而应用了IOC之后 ...

  9. Spring IoC、DI入门小程序

    Alt+/智能提示xml配置文件节点及属性:在接口上使用Ctrl+T可以提示其实现类 一.IoC控制反转(将创建对象的权利交给spring)入门小程序 1.引入jar包 2.工程基本结构 3.新建Us ...

随机推荐

  1. Java 之 字符输出流[writer]

    一.字符输出流 java.io.Writer 抽象类是表示用于写出字符流的所有类的超类,将指定的字符信息写出到目的地.   它定义了字节输出流的基本共性功能方法. void write(int c) ...

  2. window.addEventListener('error')监听页面是否更新版本

    因本司更新迭代的速度很快,有时候更改一个BUG就要马上更新版本,就会引起用户在应用当中,页面点击无反应,其实是打包的js和css的包名称更改,找不到以前的包的缘故.我现在用一个小方法,判断js或css ...

  3. /etc/apt/sources.list 和 /etc/apt/sources.list.d

    转自:大数据云技术基础之Linux源:/etc/apt/sources.list文件 导读 1./etc/apt/sources.list的作用是什么?2.为什么会产生 /etc/apt/source ...

  4. 配置多网卡多IP的方式

    [root@web01 conf.d]# cat ip.conf server {     listen 10.0.0.7:80;     server_name _;       location ...

  5. 多个ip地址匹配正则表达式

    匹配规则:多个ip地址使用,号进行分割 例如:1.1.1.1,2.2.2.2var iplist =/^((25[0-5]|2[0-4]\d|((1\d{2})|([1-9]?\d)))\.){3}( ...

  6. webpack多页面打包配置

    单页面应用:整个应用里面只有一个html文件.现在主流的框架,vue,react都是单页面应用. 所以webpack绝大部分都是对单页面打包.那么webpack如何对多页面进行打包 index.htm ...

  7. 用Xcode配置完美ACMer环境

    用Xcode配置完美ACMer环境 前言 ​ 作为\(ACMer\),需求大致为强大的文本编辑功能\((VIM)\),便捷的文件模版功能以及多文件编译功能.\(vscode\)虽然强大,但是与集成\( ...

  8. JVM堆空间用途分析与划分依据

    在上一次[https://www.cnblogs.com/webor2006/p/9876493.html]已经对JVM的内存空间的划分进行了理论化的学习,这次还是对上一次提到的理论进行进一步的补充, ...

  9. k8s的pod

    一.Pod的分类 自主式Pod : 控制器管理的Pod:Kubernetes使用更高级的称为Controller的抽象层,来管理Pod实例.每个Pod都有一个特殊的被称为“根容器”的Pause容器. ...

  10. 学习php doctrine

    了解symfony3.3.PHP FIG.Doctrine: 了解angular2.material2:. 熟悉git:了解开源项目:openstack docker ceph等: NoSQL(HBa ...