`

浅谈spring和依赖注入的价值

阅读更多

javaeye上看到有帖子,置疑spring和依赖注入的价值,回复内容整理如下:

依赖注入对设计有利,而spring则促进了依赖注入的使用。

如果业务处理类,它所使用的倚赖,都是依靠在这个类内部实现或者查找,那么必然使得正常的业务逻辑和获取依赖的方法混在一起。

我取个最简单的场景,某个注册的工作类,它需要获取当前"容许的用户名的最大长度",这个依赖非常简单吧?基本每个注册类都有这个限制,我们现在 把场景考虑的全面一点,对于复杂一点的系统,这个最大长度的限制可能来源很多,比如配制文件,数据库,可能类工作在前台比如web而配制在后台,可能需要 和第三放系统一起工作而需要到第三方系统中获取而对方只提供web service...

这么一个简单的依赖,“用户名的最大长度”,如果用依赖注入,只要一个简单的setUsernameMaxLength()方法就可以搞定。考虑 上面那么多种可能都出现,最恶劣的情况是要求一个系统可以同时支持然后通过配制方式进行,这对于将一个产品卖给n家客户的公司来说是最正常不过的要求。

那么我们来看一个很有"spring"风格的采用依赖注入的设计,通常都将会是这样:

注册工作类:
public void RegisterWork {
public void setUsernameMaxLengthProvider(UsernameMaxLengthProvider provider) {
int maxLength = provider.getUsernameMaxLength(); //简单获取结果,不管provider细节
}
....
}
“用户名的最大长度”的提供者接口
public interfacd UsernameMaxLengthProvider {
public int getUsernameMaxLength();
}
“用户名的最大长度”的提供者则可以有以下实现,都只负责读取数据,不关心数据被谁使用,怎么使用:
1.直接提供,可以用spring 构造函数方式注入usernameMaxLength值,也可以junit测试时直接new DirectdProvide对象
public class DirectdProvider implements UsernameMaxLengthProvider {
private int usernameMaxLength = 10;
public int getUsernameMaxLength() {
return UsernameMaxLength;
}
public DirectdProvider (int usernameMaxLength) {
this.usernameMaxLength = usernameMaxLength;
}
}
2.读本地配制文件
public class LocalConfigFileProvider implements UsernameMaxLengthProvider {
public void read(File configFile) {
usernameMaxLength = ....
}
}
3.类似的从数据库读取 DatabaseProvide
4.类似的web service从第三方读取 WebServiceProvider
5.其他的可能扩展的方式

开发时逻辑清晰,代码可读性超强,基本看类名和函数名搞定。测试时,轻松测试RegisterWork,testcase中只要 worker.setUsernameMaxLengthProvider(new DirectdProvider(20))就搞定。而针对UsernameMaxLengthProvider的几个实现类,更是轻松使用junit,每 个都覆盖一遍。

呵呵,现在我们可以砍刀,最简单的一个int型的“用户名的最大长度”,都可能遭遇如此复杂的场景。如果不用倚赖注入,而是选择在RegisterWork中自己搞定“用户名的最大长度”的获取,那么可能要遇到以下问题:
1. RegisterWork极其复杂,可以想像类似的依赖肯定还有其他
2. 获取“用户名的最大长度”的方式和注册的业务处理逻辑完全没有直接联系,对注册过程来说它只关注结果,“用户名的最大长度”是10还是20,而不是到底读本地文件还是读数据库。喧宾夺主了,次要逻辑干扰了主要逻辑
3. 难于测试。获取“用户名的最大长度”的方式的这些代码,被藏在RegisterWork类中,而且很有可能是private方法不对外暴露(如果不依赖注入还需要暴露吗?暴露给谁呢),怎么用mock测试?怎么能保证以上几种的实现都覆盖到?
4. 难于扩展。就算上面都搞定了,某一天突然来了一个变态需求,要求用ldap从另一个地方取配置呢?难道再把ldap请求的那一大片代码也写到RegisterWork里面?
5. 更难于被第三方扩展。运气好,上面这个ldap取配制的变态需求客户开始没有要求。顺利开发完成测试通过然后准备上线,最后一晚了客户才发现,"哦,给忘 了,你们想办法给加上,快,快,明天一早就要上线运行了...你们写死在代码里面了?那只能你们修改了原代码了"。吐血了吧,先骂一顿,可是活还的干啊, 咬牙切齿的把新的实现代码加上了,还得编译打包更新重启...如果是spring多好,单独写一个LdapProvider类,测试(这个测试比杂在 RegisterWork里面测试简单的多)通过后单独提供这个class仍classpath下,修改spring的配制将原来的 ***Provider替换掉,轻松搞定,甚至可以把这活仍给客户的开发人员,告诉他们怎么替换就可以了,管你ladp还是其他,谁让你们需求不明确,自 己扩展去。
6. 容易出错。刚吐血完成上面的变态需求,更新完毕,一会客户电话来了,“...怎么...不正常了?”。又吐血几升地检查,终于找出来了,原来是刚才写 ladp访问的代码时不小心改错了RegisterWork的一个地方,谁让RegisterWork类有几十上百个方法好几千行呢,一时急,又没有测试 到......可是客户不会理解的。

上述的场景,spring + 依赖注入的设计方式,优点很明显吧。

再考虑一下维护和二次开发的问题,上面的spring + 依赖注入的代码,好看易懂,方便扩展,维护起来轻松。如果是那么堆在RegisterWork里面,在那个大堆中代码要找出这些代码并读懂,估计不是件轻松的事情。

代码维护是需要成本的,写出易于维护的代码,是一个优秀程序员的基本素养,至少,不能让下一个接手的人骂娘吧?

分享到:
评论

相关推荐

    浅谈Spring IoC容器的依赖注入原理

    浅谈Spring IoC容器的依赖注入原理 Spring IoC容器的依赖注入原理是Spring框架的核心机制之一,负责将服务对象(Bean)实例化并将其提供给客户端使用。依赖注入原理可以分为两个阶段:IoC容器初始化和Bean实例化。 ...

    浅谈 Spring 原理 透析,ioc aop

    浅谈 Spring 原理 透析,IOC 和 AOP Spring 框架是一个从实际项目开发经验中抽取的,可高度重用的应用框架。它是一个轻量级容器,带有包装器,使许多不同的服务和框架更易于使用。轻量级容器接受任何 JavaBean,而...

    浅谈Spring中的Quartz配置

    为此,我们可以利用Spring的`SchedulerAwareJobFactory`和`QuartzJobBean`,这样Spring的依赖注入和AOP特性就能在Job中发挥作用。 最后,lib文件夹通常包含项目所需的库文件,对于Quartz来说,它可能包含了Quartz库...

    浅谈Spring+Hibernate整合(共16页).doc

    Spring 是一个轻量级的容器,提供依赖注入(Dependency Injection,DI)和面向切面编程(Aspect-Oriented Programming,AOP)等功能,使得应用程序的组件之间解耦,便于管理和测试。而 Hibernate 是一个对象关系映射...

    浅谈Spring解决循环依赖的三种方式

    浅谈Spring解决循环依赖的三种方式 在软件开发中,循环依赖是一个常见的问题,特别是在使用依赖注入框架时。循环依赖是指多个类之间存在循环引用的情况,例如A类依赖B类,B类依赖C类,而C类又依赖A类。这种情况下,...

    浅谈spring和spring MVC的区别与关系

    Spring是一个开源框架,功能主要是依赖注入和控制反转。依赖注入有三种形式:构造注入、setter注入和接口注入。控制反转则主要是起到操控作用,把对象的创建、初始化、销毁交给Spring容器来处理。面向切面(把功能...

    浅谈Spring如何解决循环依赖的问题

    "浅谈Spring如何解决循环依赖的问题" Spring框架是如何解决循环依赖的问题?这是一个经常被问到的高频面试题。要理解这个问题,需要从源码的角度对其实现原理进行讲解。 首先,需要了解 Spring bean 的创建过程。...

    浅谈SSH学习要点

    最后,Spring框架,以其依赖注入(IoC)和面向切面编程(AOP)闻名。IoC使得对象间的依赖关系可以通过配置文件或注解来管理,降低了代码的耦合度。AOP则是处理横切关注点,如日志、事务等,使代码更清晰。基础使用中...

    浅谈spring容器中bean的初始化

    3. **属性注入**:根据Bean定义中的依赖注入信息,将其他Bean的引用或值注入到当前Bean的属性中。 4. **初始化回调**:如果Bean定义中指定了初始化方法(通过`init-method`属性),Spring会在所有注入完成后调用这个...

    浅谈Spring装配Bean之组件扫描和自动装配

    Spring装配Bean之组件扫描和自动装配 Spring框架提供了两种方式来实现自动化装配:组件扫描和自动装配。组件扫描是指Spring自动发现应用上下文中所创建的bean,而自动装配是指Spring自动满足bean之间的依赖。 组件...

    浅谈Spring 的Controller 是单例or多例

    4. **依赖注入和`@Autowired`**:在Spring中,通常推荐通过依赖注入来获取服务层对象,而不是直接在Controller中创建这些对象。这是因为Spring会管理这些依赖的生命周期,保证在并发环境下的正确使用。如果服务层...

    浅谈Spring与SpringMVC父子容器的关系与初始化

    在Java Web开发中,Spring框架是核心的依赖注入(DI)和面向切面编程(AOP)框架,而Spring MVC则是Spring框架的一个模块,专门用于处理HTTP请求和响应。两者之间的关系体现在它们各自管理的Bean容器上,形成了所谓...

    浅谈Spring Context加载方式

    在Spring框架中,Context是Spring应用的核心容器,它负责管理所有Bean的生命周期和依赖注入。本文将深入探讨Spring Context的两种加载方式:基于XML配置和基于注解的配置。 首先,传统的Spring Context加载方式通常...

    浅谈Spring的两种配置容器

    BeanFactory提供了基本的依赖注入(DI)服务,允许开发者通过配置来控制对象的生命周期和装配。默认情况下,BeanFactory采用延迟初始化策略,这意味着在真正需要使用Bean时才会进行初始化和注入。这种设计降低了应用...

    浅谈Spring Boot 属性配置和自定义属性配置

    Spring Boot 属性配置和自定义属性配置详解 在 Spring Boot 中,属性配置是一项非常重要的功能,主要用于配置项目中的各种设置,如服务器端口、数据库连接信息、自定义属性等。在本文中,我们将详细介绍 Spring ...

    浅谈Spring学习之request,session与globalSession作用域

    浅谈Spring学习之request, session与globalSession作用域 在Spring框架中,request、session和globalSession是三个重要的作用域,分别对应着不同的生命周期和应用场景。在本文中,我们将深入探讨这三个作用域的定义...

    浅谈J2EE框架和分布式网络管理.pdf

    2. 业务层:在J2EE中,业务层是通过Spring Framework实现的,它提供了面向切面的编程(AOP)和依赖注入(DI)等特性,有助于实现业务组件的组装和关联。Spring框架通过轻量级容器管理业务逻辑对象的生命周期,使得...

    浅谈SpringMVC+Spring3+Hibernate4开发环境搭建

    Spring3是Spring框架的一个版本,它提供了依赖注入、AOP(面向切面编程)、事务管理等功能,使得开发者可以更加专注于业务逻辑而不是底层实现。在配置中,我们通常会创建一个`contextConfigLocation`参数,指向...

Global site tag (gtag.js) - Google Analytics