Java基础之教你如何正确运用依赖注入

 更新时间:2021年05月14日 16:11:40   作者:林 文  
最近发现很多使用Spring框架的Java代码存在依赖注入方式的误用,甚至是滥用.因此整理了这篇文章,欢迎大家一起探讨,需要的朋友可以参考下

一、C++的诟病

C++最遭人诟病的地方就是定义一个类需要写两个文件,一个.h文件和一个.cpp文件。例如定义一个CMainFrame类,mainframe.h内容如下:

class CMainFrame : public CFrameWndEx
{
protected:
	CMainFrame();
public:
	virtual ~CMainFrame();
};

mainframe.cpp内容如下:

CMainFrame::CMainFrame()
{
}
 
CMainFrame::~CMainFrame()
{
}

当需要给这个类添加一个方法时,需要同时修改.h文件和.cpp文件。例如新增一个DefWindowProc函数。需要在.h文件中增加该函数的声明。

class CMainFrame : public CFrameWndEx
{
protected:
	CMainFrame();
public:
	virtual ~CMainFrame();
 
protected:
	virtual LRESULT DefWindowProc(UINT message, WPARAM wParam, LPARAM lParam);
};

mainframe.cpp中增加DefWindowProc的定义:

LRESULT CMainFrame::DefWindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
	if(message==WM_NCPAINT ) 
	{
		if(bShow){
			ShowWindow(SW_SHOW);
		}
		else {
			ShowWindow(SW_HIDE);
		}
	}
 
	return CFrameWndEx::DefWindowProc(message, wParam, lParam);
}

可以看出C++的类定义代码中,一次变化需要修改两个文件,其维护的繁琐令人诟病。

二、Java的改进

然而Java的出现彻底解决了这个问题,一个类就对应一个.java文件(包括后来其他面向对象语言也秉承了这个思路,比如C#)。

比如一个LogService类用于对日志进行维护,起初只包含日志的增删功能,LogService.java代码如下。

public class LogService{
	public ServiceResult<Boolean> addLog (SysLogInfo logInfo) {
		......
	}
 
	public ServiceResult<Boolean> delLog (String id) {
		......
	}
}

当需要增加一个updateLog方法时,仅需对LogService.java进行修改。

public class LogService{
	public ServiceResult<Boolean> addLog (SysLogInfo logInfo) {
		......
	}
 
	public ServiceResult<Boolean> delLog(String id) {
		......
	}
 
	public ServiceResult<Boolean> updateLog (SysLogInfo logInfo) {
		......
	}
 
}

一切变得方便了很多。

三、误用导致的退步

但是最近在看一些基于Spring(SpringBoot、SpringMVC)框架写的代码时,发现很多类的代码又回到了C++的形式。例如在使用一个LogService时,开发人员首先定义了一个interface,在LogService.java中:

public interface LogService {
	ServiceResult<Boolean> addLog(SysLogInfo logInfo);
	ServiceResult<Boolean> delLog(String id);
}

然后定义了一个该接口的实现类,在LogServiceImpl.java中:

public class LogServiceImpl implements LogService{
	
	@Override
	public ServiceResult<Boolean> addLog(SysLogInfo logInfo) {
		......		
	}
 
	@Override
	public ServiceResult<Boolean> delLog(String id) {
		......
	}
}

在需要实例化这个类的地方用了一个@Autowired注解注入。

public class LogController {
	@Autowired
	private LogService logservice;
}

在问及开发人员为什么要象这样做时,其给了一个自信的回答:这是面向接口编程!

注意:这个设计中LogService.java类似于C++中的.h文件,LogServiceImpl.java类似于C++中的.cpp文件,这两个文件共同定义了一个LogService类。当需要给这个类添加一个updateLog方法时,LogService.java和LogServiceImpl.java都需要被修改,又走回了C++的老路。这显然是对面向接口编程的曲解。如果这样都能算面向接口编程的话,那么C++就成了一门天然的面向接口编程的语言,还何必去学那些复杂的设计模式。

不过这样写代码有什么问题吗?其实也没有太大问题,只是代码繁琐一点而已(C++就是这样的)。只不过既然你选择了Java语言,却又写成了C++的样子,就好像在开一辆自动挡的汽车,却一直拨到手动模式驾驶一样。

四、正确理解面向接口编程

那么什么才是面向接口编程呢,其要点在于:接口是基于变化的抽象。在有可能变化的地方才需要接口。假设上面的例子中,写日志的动作同时存在3种不同的实现:

1.写到日志文件。

2.写到数据库。

3.写到本地的一个日志服务的UDP端口。

那么可以基于这个接口写3个不同的实现类:

public class LogServiceFile implements LogService{
}
public class LogServiceDB implements LogService{
}
public class LogServiceUdp implements LogService{
}

当然此时如果还是使用下面的代码会报错,因为Autowired只能装配对应接口的唯一一个派生类的Bean,而此时存在3个派生类。

public class LogController {
	@Autowired
	private LogService logservice;
}

需要改进成类似下面这个样子,根据实际情况使用对应的派生类对象:

public class LogController {
	private LogService logservice;
	void writeLog(SysLogInfo logInfo){
		logservice = GetLogServiceInst();
		logservice.addLog(logInfo);
	}
}

如果你的接口只有一个实现类,而且在可以遇见的将来也不会有其他实现类,那么还是建议你能简化一点,采用最基本的类定义方式,减少代码的复杂性。

到此这篇关于教你如何正确运用Java依赖注入的文章就介绍到这了,更多相关Java依赖注入内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java版C语言版简单使用静态语言实现动态数组的方法

    Java版C语言版简单使用静态语言实现动态数组的方法

    本文给大家分享java版和C语言版简单使用静态语言实现动态数组的方法,非常不错,具有参考借鉴价值,需要的朋友参考下吧
    2017-10-10
  • SpringBoot 项目使用hutool 工具进行 http 接口调用的处理方法

    SpringBoot 项目使用hutool 工具进行 http 接口调用的处理方

    在实际的开发过程中一个互联网的项目来说 ,有可能会涉及到调用外部接口的实际业务场景,下面通过本文给大家介绍SpringBoot 项目 使用hutool 工具进行 http 接口调用的处理方法,需要的朋友可以参考下
    2022-06-06
  • 一文秒懂IDEA中每天都在用的Project Structure知识

    一文秒懂IDEA中每天都在用的Project Structure知识

    这篇文章主要介绍了一文秒懂IDEA中每天都在用的Project Structure知识,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-10-10
  • java支付宝即时到帐提交订单功能

    java支付宝即时到帐提交订单功能

    这篇文章主要为大家详细介绍了java支付宝即时到帐提交订单功能,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-08-08
  • springboot+swagger2.10.5+mybatis-plus 入门详解

    springboot+swagger2.10.5+mybatis-plus 入门详解

    这篇文章主要介绍了springboot+swagger2.10.5+mybatis-plus 入门,本文通过实例图文相结合给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-12-12
  • Spring的Ioc模拟实现详细介绍

    Spring的Ioc模拟实现详细介绍

    这篇文章主要介绍了Spring的Ioc模拟实现详细介绍,具有一定参考价值,需要的朋友可以了解下。
    2017-11-11
  • mybatis @InsertProvider报错问题及解决

    mybatis @InsertProvider报错问题及解决

    这篇文章主要介绍了mybatis @InsertProvider报错的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-07-07
  • Spring Cache手动清理Redis缓存

    Spring Cache手动清理Redis缓存

    这篇文章主要介绍了Spring Cache手动清理Redis缓存,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-10-10
  • MyBatis动态<if>标签的使用

    MyBatis动态<if>标签的使用

    本文主要介绍了MyBatis动态<if>标签的使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-05-05
  • Kotlin基础教程之操作符与操作符重载

    Kotlin基础教程之操作符与操作符重载

    这篇文章主要介绍了Kotlin基础教程之操作符与操作符重载的相关资料,需要的朋友可以参考下
    2017-05-05

最新评论