详解Spring的核心机制依赖注入

对于一般的Java项目,他们都或多或少有一种依赖型的关系,也就是由一些互相协作的对象构成的。Spring把这种互相协作的关系称为依赖关系。如A组件调用B组件的方法,可称A组件依赖于B组件,依赖注入让Spring的Bean以配置文件组织在一起,而不是以硬编码的方式耦合在一起

一、理解依赖注入

依赖注入(Dependency Injection) = 控制反转(Inversion ofControl,IoC):当某个Java实例(调用者)需另一个Java实例(被调用者)时,在依赖注入模式下,创建被调用者的工作不再由调用者来完成,因此称为 控制反转 ;创建被调用者实例的工作通常由Spring容器来完成,然后注入调用者,因此也称为 依赖注入

依赖注入:程序运行过程中,如需另一个对象协作(调用它的方法、访问他的属性)时,无须在代码中创建被调用者,而是依赖于外部容器的注入。Spring的依赖注入对调用者和被调用者几乎无任何要求,完全支持对POJO间依赖关系的管理

依赖注入

设值注入:IoC容器使用属性的setter方法来注入被依赖的实例

构造注入:IoC容器使用构造器来注入被依赖的实例

理解依赖注入:

一个人(Java实例,调用者)需要一把斧子(Java实例,被调用者)

在原始社会里,几乎没有社会分工;需要斧子的人(调用者)只能自己去磨一把斧子(被调用者);对应情形为:Java程序里的调用者自己创建被调用者,通常采用new关键字调用构造器创建一个被调用者

进入工业社会,工厂出现了,斧子不再由普通人完成,而在工厂里被生产出来,此时需要斧子的人(调用者)找到工厂,购买斧子,无须关心斧子的制造过程;对应简单工厂设计模式,调用者只需定位工厂,无须管理被调用者的具体实现

进入“共产主义”社会,需要斧子的人甚至无须定位工厂,“坐等”社会提供即可;调用者无须关心被调用者的实现,无须理会工厂,等待Spring依赖注入

二、设值注入

Person接口: 
public interface Person { 
  // 定义使用斧子的方法 
  public void useAxe(); 
} 
Spring推荐面向接口编程,这样可更好地让规范和实现分离,从而提供更好的解耦;对于一个Java EE应用,不管是DAO组件还是业务逻辑组件,都应该先定义一个接口,该接口定义了该组件应实现的功能,但功能的实现则由其实现类提供 
Axe接口: 
public interface Axe { 
  // Axe接口里有个砍的方法 
  public String chop(); 
} 
实现Axe: 
public class StoneAxe implements Axe { 
  public String chop() { 
    return "石斧砍柴好慢S"; 
  } 
} 

bean.xml: 

<?xml version="1.0" encoding="UTF-8"?> 
<!-- Spring配置文件的根元素,使用spring-beans-3.0.xsd语义约束 --> 
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
 xmlns="http://www.springframework.org/schema/beans" 
 xsi:schemaLocation="http://www.springframework.org/schema/beans 
 http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> 
  
 <!-- 配置chinese实例 --> 
 <bean id="chinese" class="com.Chinese"> 
   <!-- 将stoneAxe注入给axe属性 --> 
   <property name="axe" ref="stoneAxe" /> 
 </bean> 
  
 <!-- 配置stoneAxe实例 --> 
 <bean id="stoneAxe" class="com.StoneAxe" /> 
  
</beans> 

测试类: 

public class BeanTest { 
  public static void main(String[] args) { 
   // 创建Spring容器 
   ApplicationContext ctx = new ClassPathXmlApplicationContext("bean.xml"); 
   // 获取chinese实例 
   Person p = ctx.getBean("chinese", Person.class); 
   // 调用useAxe()方法 
   p.useAxe(); 
  } 
} 

Spring采用XML作为配置文件,从Spring2.0开始,Spring即可采用DTD来定义配置文件的语义约束,也可用XML Schema来定义(可利用Spring配置文件的扩展性,进一步简化Spring配置;还提供了一些新的标签;还允许程序员开发自定义的配置文件标签,让其他开发人员在Spring配置文件中使用这些标签:通常由第三方供应商完成);

可在Spring的projects目录的org.springframwork.beans、org.springframework.context等目录的\src\main\resources路径下找到各种*.xsd文件(Spring配置文件的XML Schema语义约束文件)

在配置文件中,Spring配置Bean实例通常会指定:

id :指定该Bean的唯一标识,程序通过id属性值来访问该Bean实例

class :指定该Bean的实现类, 此处不可再用接口 ,必须使用实现类Spring容器用XML解析器读取该属性,并利用反射来创建该实现类的实例

Spring会自动接管每个<bean.../>定义里的<property.../>元素定义,Spring会在调用无参构造器后、创建默认Bean实例后、调用对应的setter方法为程序注入属性值

每个Bean的id属性是该Bean的唯一标识,程序通过id属性访问Bean,Bean与Bean的依赖关系也通过id属性关联

Bean与Bean间的依赖关系由Spring管理,Spring采用setter方法为目标Bean注入所依赖的Bean,这种方式被称为 设值注入

使用Spring IoC容器的3个基本要点:

应用程序的各组件面向接口编程

应用程序的各组件不再由程序主动产生,而是由Spring容器来负责产生、并初始化

Spring采用配置文件、或Annotation来管理Bean的实现类、依赖关系,Spring容器则根据配置文件、利用反射来创建实例,并为之注入依赖关系

三、构造注入

在构造实例时,已经为其完成了依赖关系的初始化。这种利用构造器来设置依赖关系的方式,被称为构造注入

public class Chinese implements Person { 
 private Axe axe; 
  
 // 默认的构造器 
 public Chinese() { 
 } 
  
 // 构造注入所需的带参数的构造器 
 public Chinese(Axe axe) { 
   this.axe = axe; 
 } 
  
 // 实现Person接口的useAxe方法 
 @Override 
 public void useAxe() { 
   // 调用axe的chop()方法 
   // 表明Person对象依赖于axe对象 
   System.out.println(axe.chop()); 
 } 
} 

无须再提供设置axe属性的setter方法,仅仅提供了一个带Axe属性的构造器,Spring将通过该构造器为chinese注入所依赖的Bean实例 

<?xml version="1.0" encoding="UTF-8"?> 
<!-- Spring配置文件的根元素,使用spring-beans-3.0.xsd语义约束 --> 
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
 xmlns="http://www.springframework.org/schema/beans" 
 xsi:schemaLocation="http://www.springframework.org/schema/beans 
 http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> 
  
 <!-- 配置chinese实例 --> 
 <bean id="chinese" class="com.Chinese"> 
   <!-- 使用构造注入,为chinese实例注入stoneAxe实例 --> 
   <constructor-arg ref="stoneAxe" /> 
 </bean> 
  
 <!-- 配置stoneAxe实例 --> 
 <bean id="stoneAxe" class="com.StoneAxe" /> 
  
</beans> 

<constructor-arg.../>元素指定了一个构造器参数,该参数类型是Axe,这指定Spring调用Chinese类里带一个Axe参数的构造器来创建chinese实例,因为使用了有参数的构造器创建实例,所以当Bean实例被创建完成后,该Bean的依赖关系已经设置完成

配置<constructor-arg.../>元素时可指定一个index属性,用于指定该构造参数值将作为第几个构造参数值;如index=“0”表明该构造参数值将作为第一个构造参数

执行效果与使用设置注入时的执行效果完全一样。区别在于:创建Person实例中Axe属性的时机不同-----设置注入是先通过无参数的构造器创建一个Bean实例,然后调用对应的setter方法注入依赖关系;而构造注入则直接调用有参数的构造器,当Bean实例创建完成后,已经完成了依赖关系的注入

四、两种注入方式的对比

相比之下,设值注入有如下优点:

与传统的JavaBean的写法更相似,程序开发人员更容易理解、接受、通过setter方法设定依赖关系显得更加直观、自然

对于复杂的依赖关系,若采用构造注入,会导致构造器过于臃肿,难以阅读;Spring在创建Bean实例时,需同时实例化其依赖的全部实例,因而导致性能下降

尤其是在某些属性可选的情况下,多参数的构造器更加笨重

相比之下,构造注入有如下优点:

可在构造器中决定依赖关系的注入顺序,优先依赖的优先注入

对于依赖关系无需变化的Bean,构造注入更有用处;因为没有setter方法,所有的依赖关系全部在构造器内设定。因此,无须担心后续的代码对依赖关系产生破坏

依赖关系只能在构造器设定,则只有组件的创建者才能改变组件的依赖关系。对组件的调用者而言,组件内部的依赖关系完全透明,更符合高内聚的原则

一般采用以设值注入为主,构造注入为辅的注入策略。对于依赖关系无须变化的注入,尽量采用构造注入;而其他的依赖关系的注入,则考虑设值注入

 如有疑问请留言或者到本站社区交流讨论,感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

详解Spring的核心机制依赖注入的更多相关文章

  1. Spring JdbcTemplate执行数据库操作详解

    JdbcTemplate是Spring框架自带的对JDBC操作的封装,目的是提供统一的模板方法使对数据库的操作更加方便、友好,效率也不错,这篇文章主要介绍了Spring JdbcTemplate执行数据库操作,需要的朋友可以参考下

  2. Spring Batch批处理框架操作指南

    Spring Batch 是 Spring 提供的一个数据处理框架。企业域中的许多应用程序需要批量处理才能在关键任务环境中执行业务操作,这篇文章主要介绍了Spring Batch批处理框架操作指南,需要的朋友可以参考下

  3. Spring详细讲解@Autowired注解

    @Autowired注解可以用在类属性,构造函数,setter方法和函数参数上,该注解可以准确地控制bean在何处如何自动装配的过程。在默认情况下,该注解是类型驱动的注入

  4. 使用Spring AOP实现用户操作日志功能

    这篇文章主要介绍了使用Spring AOP实现了用户操作日志功能,功能实现需要一张记录日志的log表,结合示例代码给大家讲解的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  5. Angular 4依赖注入学习教程之ValueProvider的使用(七)

    这篇文章主要给大家介绍了关于Angular 4依赖注入之ValueProvider使用的相关资料,文中介绍的非常详细,对大家学习或者使用Angular4具有一定的参考学习价值,需要的朋友们下面来一起看看吧。

  6. Spring Security认证器实现过程详解

    一些权限框架一般都包含认证器和决策器,前者处理登陆验证,后者处理访问资源的控制,这篇文章主要介绍了Spring Security认证器实现过程,需要的朋友可以参考下

  7. spring学习JdbcTemplate数据库事务管理

    这篇文章主要为大家介绍了spring学习JdbcTemplate数据库事务管理,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  8. Spring Boot 集成Redisson实现分布式锁详细案例

    这篇文章主要介绍了Spring Boot 集成Redisson实现分布式锁详细案例,文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的朋友可以参考一下

  9. Spring Security实现接口放通的方法详解

    在用Spring Security项目开发中,有时候需要放通某一个接口时,我们需要在配置中把接口地址配置上,这样做有时候显得麻烦。本文将通过一个注解的方式快速实现接口放通,感兴趣的可以了解一下

  10. 如何利用Spring把元素解析成BeanDefinition对象

    这篇文章主要介绍了如何利用Spring把元素解析成BeanDefinition对象,文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的小伙伴可以参考一下

随机推荐

  1. 基于EJB技术的商务预订系统的开发

    用EJB结构开发的应用程序是可伸缩的、事务型的、多用户安全的。总的来说,EJB是一个组件事务监控的标准服务器端的组件模型。基于EJB技术的系统结构模型EJB结构是一个服务端组件结构,是一个层次性结构,其结构模型如图1所示。图2:商务预订系统的构架EntityBean是为了现实世界的对象建造的模型,这些对象通常是数据库的一些持久记录。

  2. Java利用POI实现导入导出Excel表格

    这篇文章主要为大家详细介绍了Java利用POI实现导入导出Excel表格,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  3. Mybatis分页插件PageHelper手写实现示例

    这篇文章主要为大家介绍了Mybatis分页插件PageHelper手写实现示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  4. (jsp/html)网页上嵌入播放器(常用播放器代码整理)

    网页上嵌入播放器,只要在HTML上添加以上代码就OK了,下面整理了一些常用的播放器代码,总有一款适合你,感兴趣的朋友可以参考下哈,希望对你有所帮助

  5. Java 阻塞队列BlockingQueue详解

    本文详细介绍了BlockingQueue家庭中的所有成员,包括他们各自的功能以及常见使用场景,通过实例代码介绍了Java 阻塞队列BlockingQueue的相关知识,需要的朋友可以参考下

  6. Java异常Exception详细讲解

    异常就是不正常,比如当我们身体出现了异常我们会根据身体情况选择喝开水、吃药、看病、等 异常处理方法。 java异常处理机制是我们java语言使用异常处理机制为程序提供了错误处理的能力,程序出现的错误,程序可以安全的退出,以保证程序正常的运行等

  7. Java Bean 作用域及它的几种类型介绍

    这篇文章主要介绍了Java Bean作用域及它的几种类型介绍,Spring框架作为一个管理Bean的IoC容器,那么Bean自然是Spring中的重要资源了,那Bean的作用域又是什么,接下来我们一起进入文章详细学习吧

  8. 面试突击之跨域问题的解决方案详解

    跨域问题本质是浏览器的一种保护机制,它的初衷是为了保证用户的安全,防止恶意网站窃取数据。那怎么解决这个问题呢?接下来我们一起来看

  9. Mybatis-Plus接口BaseMapper与Services使用详解

    这篇文章主要为大家介绍了Mybatis-Plus接口BaseMapper与Services使用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  10. mybatis-plus雪花算法增强idworker的实现

    今天聊聊在mybatis-plus中引入分布式ID生成框架idworker,进一步增强实现生成分布式唯一ID,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

返回
顶部