`
frank-liu
  • 浏览: 1666415 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

spring学习:依赖注入的几种方式讨论

 
阅读更多

简介

    在前面的文章里,我已经对依赖注入的基本概念做了一个介绍。我们已经知道了依赖注入的意义和目的。但是在牵涉到具体实现的时候,我们有好几种不同的选择,其中就有自动关联(autowire),java代码关联(java config)以及传统的xml文件配置关联。本文针对这几种形式结合具体的示例做一个讨论和总结。

 

问题场景介绍

    假设我们有如下的一个问题,在下图中,我们有一个Service接口,定义特定的业务逻辑。在一个具体的实现中,BusinessService需要使用到第三方的数据,这里用接口DAO表示。为了保证松耦合和方便测试,有一个典型的实现BusinessDAO。

 

    当然,因为根据我们具体情况的需要,我们完全可以定义其他的实现。现在的问题是,如果需要采用依赖注入的方式让上述的BusinessService, BusinessDAO一起工作,同时在我们的业务代码里又没有对它们的耦合,具体该有几种详细的实现呢?具体实现的细节改如何呢?我们就一一看过来。

 

XML配置文件关联

    从最传统的这种方式开始。在上文中我们也已经举出过几个示例。一般我们将一个简单java对象定义为一个bean。而我们常用代码里对象之间的依赖关系通过构造函数或者属性方法的方式注入设置。在这种方式里,每个java对象就是一个单独定义的单元,从它们自身的定义里更多的情况下它们对于外界的依赖是一个抽象的接口或者抽象基类。而通过我们定义的关联关系,使得它们在最终被使用和运行的时候,各种具体的依赖关系已经构造好了。

    现在需要的就是按照前面图中定义的关系来构造示例。首先定义的Service接口如下:

package com.yunzero;

public interface Service {
	void doBusiness();
}

     

        对应的一个实现BusinessService如下:

 

package com.yunzero.impl;

import com.yunzero.DAO;
import com.yunzero.Service;

public class BusinessService implements Service {
	
	private DAO dao;

	public void setDao(DAO dao) {
		this.dao = dao;
	}

	@Override
	public void doBusiness() {
		System.out.println("Business impl in business service.");
		System.out.println(dao.getId());
	}

}

    这部分的代码其实就是一个通过属性注入的方式产生了一个对抽象接口DAO的依赖。而DAO的实现如下:

package com.yunzero;

public interface DAO {
	int getId();
}

 

    现在需要再定义的就是DAO的一个实现BusinessDAO:

 

package com.yunzero.impl;

import com.yunzero.DAO;

public class BusinessDAO implements DAO {

	@Override
	public int getId() {
		return 0;
	}
}

    有了这几步实现之后,剩下的就是将它们给拼在一起形成一个完整的应用。采用xml关联的方式该怎么来实现呢?我们先定义一个bean配置文件sampleContext.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.xsd">

	<bean id="service" class="com.yunzero.impl.BusinessService">
		<property name="dao" ref="dao"/>
	</bean>
	
	<bean id="dao" class="com.yunzero.impl.BusinessDAO"/>

</beans>

        我们来看它详细的定义。首先最外层的是<beans> 节点,然后有一个定义id为service的bean。它对应的具体实现是BusinessService。留意到前面代码里BusinessService对DAO接口的属性设置依赖。那里定义了一个setDAO的方法。在这里可以通过设置<property>属性将真正的实现关联进来。在这里是ref引用的dao。而id为dao的bean则是BusinessDAO的具体实例。这样,通过这么一个定义各个需要的bean以及它们之间的依赖,它们的依赖注入配置基础就弄好了。在实际代码中要构造出这些定义好的对象则比较简单,一个使用它们的代码实例如下:

 

package com.yunzero;

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

public class App 
{
    public static void main( String[] args )
    {
    	AbstractApplicationContext ctx = new ClassPathXmlApplicationContext("sampleContext.xml");
        Service service = ctx.getBean(Service.class);
        service.doBusiness();
        ctx.close();
    }
}

     上述代码里使用ApplicationContext来获取对象定义的容器。这里获取Service对象采用的是ctx.getBean(class)的方式。以前也有直接获取bean名字,然后再做一个强制类型转换的方式。和那种比起来,这种方式的代码更加简洁一些。

    如果这个时候运行代码,输出的结果如下:

 

Mar 30, 2015 10:02:21 PM org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@7106e68e: startup date [Mon Mar 30 22:02:21 CST 2015]; root of context hierarchy
Mar 30, 2015 10:02:21 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [sampleContext.xml]
Business impl in business service.
0
Mar 30, 2015 10:02:22 PM org.springframework.context.support.ClassPathXmlApplicationContext doClose
INFO: Closing org.springframework.context.support.ClassPathXmlApplicationContext@7106e68e: startup date [Mon Mar 30 22:02:21 CST 2015]; root of context hierarchy

     完整实现的详细代码会放在后面的附件里。 

    上述的这种方式可以说是最常用办法之一。它的特点在于非常的简单直观。任何需要定义以及依赖的bean对象都会通过这种方式定义出来。当然,它也有一些不足的地方。如果我们后续的代码作一些变动,这里依赖定义的都是字符串,比较容易出错,而且有时候代码改动之后还要改配置,整个过程显得比较繁琐。

  

自动关联

    正因为有上述的问题,spring引入了另外一种注入的方式。这种方式相对来说更加简单一些。可以说实现了零配置。以前面的示例为基础,我们定义的接口还是没有任何变化,但是具体针对接口的实现。它们稍微有一点变化。比如BusinessService和BusinessDAO的实现分别如下:

package com.yunzero.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.yunzero.DAO;
import com.yunzero.Service;

@Component
public class BusinessService implements Service {
	
	private DAO dao;

	@Autowired
	public void setDao(DAO dao) {
		this.dao = dao;
	}

	@Override
	public void doBusiness() {
		System.out.println("Business impl in business service.");
		System.out.println(dao.getId());
	}

}

 

package com.yunzero.impl;

import org.springframework.stereotype.Component;

import com.yunzero.DAO;

@Component
public class BusinessDAO implements DAO {

	@Override
	public int getId() {
		return 0;
	}

}

    这些代码和前面的实现基本上一样,除了一个地方,就是这两个实现的类定义的地方多了一个@Component的标注。这就相当于告诉spring这是一个已经定义好的bean。我们也可以在@Component里面增加对bean的自定义信息。这样,对于一个定义的抽象接口,@Component相当于标注这个类就是对应该接口的一个注入实现。那么,对于那些还依赖其他bean的情况呢?

    在前面的代码里有一个DAO的依赖,而且在对dao元素的setDAO()方法里有一个标注@Autowired,这就相当于将一个DAO的实现和BusinessDAO关联起来了。而怎么找这个DAO的具体实现呢?和前面的一样,有一个BusinessDAO的实现并且它也被标注为@Component。

    有了这些bean和它们之间关联的定义了,spring里该怎么使用它们呢?虽然我们定义了这些component以及它们之间的关联关系,但是spring并不是那么聪明的就知道。它需要去扫描所有这些类才知道哪些类是哪些接口的实现以及关联了哪些类。所以和前面的xml文件配置类似,它需要一个指定配置信息的地方。为了省略硬性的xml文件配置,可以定义一个专门用来扫描的类,比如这里我们可以定义一个类ServiceConfig:

package com.yunzero.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan(basePackages="com.yunzero")
public class ServiceConfig {

}

     它的作用相当于替换了前面的配置文件。这个类其实本身并不实现任何特殊的业务逻辑。仅仅相当于起到一个类似于配置文件的作用。在实际项目中应当尽量将这类文件放在一个单独的包里。另外,在ServiceConfig的实现里,它有一个@ComponentScan的标注。这个标注用来指示spring扫描包的目录。一般来说spring默认扫描该类所在的包以及下面所包含的子包。这里采用自定义指定包路径的方式。

     经过这些修改,我们使用自动配置的运行代码如下:

 

package com.yunzero;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;

import com.yunzero.config.ServiceConfig;


public class App 
{
    public static void main( String[] args )
    {
    	AbstractApplicationContext ctx = new AnnotationConfigApplicationContext(ServiceConfig.class);
        Service service = ctx.getBean(Service.class);
        service.doBusiness();
        ctx.close();
    }
}

     采用这种方法的要点在于将配置类ServiceConfig.class作为参数提供给AnnotationConfigApplicationContext。其他的使用方式则基本上一样。

    这样,采用自动化配置的方式就完成了。总的来说,这种方式就是将替换抽象接口的bean采用@Component标注好,然后将抽象依赖之间的关联用@Autowired标注好。最后用一个配置类告诉spring去哪些包从上往下的去扫描。

 

Java配置代码关联

    除了上述的两种方法以外,还要一种办法就是配置代码关联。采用自动化配置的方法固然好,它可以做到不用写任何配置文件,但是也有一个问题。就是如果我们需要引用某些第三方的类库时,由于那些库的源代码并不在我们的掌握之中,它们并没有作那些@Component标注,这个时候再采用原来的办法就不可行了。如果我们希望能够解决这个问题的同时还能够尽可能少的使用配置文件,可以借鉴一部分前面ServiceConfig类的方法。我们这里定义一个如下的SampleConfig类:

 

package com.yunzero.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.yunzero.DAO;
import com.yunzero.Service;
import com.yunzero.impl.BusinessDAO;
import com.yunzero.impl.BusinessService;

@Configuration
public class SampleConfig {
	
	@Bean
	public Service businessService() {
		BusinessService service = new BusinessService();
		service.setDao(businessDAO());
		return new BusinessService();
	}
	
	@Bean
	public DAO businessDAO() {
		return new BusinessDAO();
	}
}

    注意到,这里这个类的定义没有添加@ComponentScan标注。这里只是针对每个Bean的创建采用专门@Bean修饰的方法单独拎了出来。这里的方法相当于定义了一系列的工厂方法。对于每个bean的创建就通过返回对应的对象。@Bean在这里起到一个标注和定义bean的作用,而且创建的这个bean的id和定义的方法名相同。

    具体对这种情况的使用和前面的一样,这里就不再赘述。

 

总结

    采用xml配置文件、自动配置和java 配置类是最主要的几种注入方法。通常来说,采用自动配置的方式最简单省事,一方面因为它只需要在代码里添加一点标注,而且不需要去定义任何配置文件,另外,因为这些标注是和代码关联的,它具有强类型相关性,能够更快的检测的错误,这样可以避免在配置文件中产生的错误。 当然,从个人的角度来说,采用xml文件的方式可以实现一个更加理想的配置和代码分离,保证代码编译之后基本上不需要做任何改动,可以做到代码和配置的完全隔离。

 

参考材料

 http://www.amazon.com/Spring-Action-Craig-Walls/dp/161729120X/ref=sr_1_1?s=books&ie=UTF8&qid=1427984830&sr=1-1&keywords=spring+in+action+5th+edition

 

  • 大小: 11.7 KB
分享到:
评论

相关推荐

    spring依赖注入的几种方式

    spring依赖注入的几种方式

    Spring 依赖注入的几种方式详解

    本篇文章主要介绍了Spring 依赖注入的几种方式详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

    Spring中依赖注入与容器

     依赖注入的几种形式 - 接口注入 - 构造器注入 - 属性(setter)注入  容器 容器是管理 组件的生命周期,注入组件(声明)所需的资源 例如: 容器 : 国家 义务教育、纳税、社保、医疗、养老 组件 : 公民 - ...

    详析Spring中依赖注入的三种方式

    在开发的过程中突然对Spring的依赖注入几种方式出现混交,打算做个简单的小结,方便大家和自己以后参考借鉴,如有总结不对的地方,请大家不吝指教!下面来一起看看吧。

    Spring依赖注入的三种方式实例详解

    Spring依赖注入(DI)的三种方式,分别为: 1. 接口注入 2. Setter方法注入 3. 构造方法注入 下面介绍一下这三种依赖注入在Spring中是怎么样实现的。 首先我们需要以下几个类: 接口 Logic.java 接口实现类 ...

    Spring开发指南

    依赖注入的几种实现类型 Type1 接口注入 Type2 设值注入 Type3 构造子注入 几种依赖注入模式的对比总结 Spring Bean封装机制 Bean Wrapper Bean Factory ApplicationContext Web Context Spring 高级...

    java面试Spring.pdf

    1. Spring 介绍 1.1 Spring 的优点 ...说一下Spring基于xml注入bean的几种方式? Spring如何解决循环依赖问题? Spring的自动装配 Spring框架中都用到了哪些设计模式? Spring框架中有哪些不同类型的事件?

    SpringFramework常见知识点.md

    - Spring依赖注入的方式有几种? - 一个bean的定义包含了什么?(BeanDefinition) - bean的作用域有哪些? - Spring 的扩展点主要有哪些? - Spring如何解决循环依赖? - 事务的传播行为是什么?有哪些? - 什么是AOP...

    25个经典的Spring面试问答

    Spring有几种配置方式 如何用基于XML配置的方式配置Spring 如何用基于Java配置的方式配置Spring 怎样用注解的方式配置Spring 请解释Spring Bean的生命周期 Spring Bean的作用域之间有什么区别 什么是Spring inner ...

    java视频教程:spring框架精讲附加实战项目练习

    详细介绍了对象创建的细节和依赖注入的几种方式;介绍了如何使用注解完成Spring的相关功能;介绍了如何借助于Spring搭建JUnit测试环境;详细介绍了什么是AOP,以及AOP的基本示例;介绍了如何使用Spring中的JDBC模块...

    Spring面试题含答案.pdf

    20. 哪种依赖注入方式你建议使用,构造器注入,还是 Setter 方法注入? 21.什么是 Spring beans? 22. 一个 Spring Bean 定义 包含什么? 23. 如何给 Spring 容器提供配置元数据? 24. 你怎样定义类的作用域? 25. ...

    千锋JavaEE精讲之Spring框架实战教程

    课程简介:  这门课程主要讲Spring的核心概念和基本使用。...详细介绍了对象创建的细节和依赖注入的几种方式;介绍了如何使用注解完成Spring的相关 资源太大,传百度网盘了,链接在附件中,有需要的同学自取。

    高级开发spring面试题和答案.pdf

    传播特性有几种?7种; 某一个事务嵌套另一个事务的时候怎么办? REQUIRED_NEW和REQUIRED区别 Spring的事务是如何回滚的,实现原理; 抽象类和接口的区别,什么时候用抽象类什么时候用接口; StringBuilder和...

    Spring面试专题.pdf

    6、Spring 有几种配置方式? 7、如何用基于 XML 配置的方式配置 Spring? 8、如何用基于 Java 配置的方式配置 Spring? 9、怎样用注解的方式配置 Spring? 10、请解释 Spring Bean 的生命周期? 11、Spring Bean 的...

    Spring面试题.zip

    6、Spring 有几种配置方式? 7、如何用基于 XML 配置的方式配置 Spring? 8、如何用基于 Java 配置的方式配置 Spring? 9、怎样用注解的方式配置 Spring? 10、请解释 Spring Bean 的生命周期? 11、Spring Bean 的...

    Spring框架分为哪七大模块

    Core封装包是框架的最基础部分,提供IOC和依赖注入特性。这里的基础概念是BeanFactory,它提供对Factory模式的经典实现来消除对程序性单例模式的需要,并真正地允许你从程序逻辑中分离出依赖关系和配置。 2.Spring...

    Spring基础与快速入门

    4 依赖注入:建立对象与对象之间依赖关系的实现,包括接口注入、构造注入、set注入,在Spring中只支持后两种 5 AOP:面向方面编程,我们可以把日志、安全、事务管理等服务(或功能)理解成一个“方面”,那么以前...

    开源框架 Spring Gossip

    从代理机制初探 AOP 动态代理 &lt;br&gt;AOP 观念与术语 Spring AOP Advices Advices 包括了Aspect 的真正逻辑,由于缝合至Targets的时机不同,Spring 提供了几种不同的 Advices。 Before ...

Global site tag (gtag.js) - Google Analytics