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依赖注入内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 200行Java代码如何实现依赖注入框架详解

    依赖注入介绍 先回顾下依赖注入的概念: 我们常提起的依赖注入(Dependency Injection)和控制反转(Inversion of Control)是同一个概念.具体含义是:当某个角色(可能是一个Java实例,调用者)需要另一个角色(另一个Java实例,被调用者)的协助时,在 传统的程序设计过程中,通常由调用者来创建被调用者的实例.但在Spring里,创建被调用者的工作不再由调用者来完成,因此称为控制反转;创建被调用者 实例的工作通常由Spring容器来完成,然后注入调用者,因此也称为

  • Java元注解meta-annotation和依赖注入详解

    这篇文章既介绍一个技术,又记录一个逐渐探索发现的过程,以供大家参考. 缘起 注意到Java的依赖注入DI规范(起初以为是CDI规范,然后发现是DI规范)有个叫@Qualifier的注解,用于当一个interface或base class有多个实现类时,能选择其中一个实现.如不用这一注解,一般的(按类型)注入就会报错说"不知道要在多个实现中选哪一个".这一注解可以放在一个自定义注解上(例如@MyPreferredImplementation),从而将自定义注解变成一个qualifier

  • 实例讲解Java的Spring框架中的控制反转和依赖注入

    近来总是接触到 IoC(Inversion of Control,控制反转).DI(Dependency Injection,依赖注入)等编程原则或者模式,而这些是著名 Java 框架 Spring.Struts 等的核心所在.针对此查了 Wikipedia 中各个条目,并从图书馆借来相关书籍,阅读后有些理解,现结合书中的讲解以及自己的加工整理如下: eg1 问题描述: 开发一个能够按照不同要求生成Excel或 PDF 格式的报表的系统,例如日报表.月报表等等.   解决方案: 根据"面向接口编

  • JavaWeb Spring依赖注入深入学习

    一.依赖注入(DI) 依赖注入听起来很高深的样子,其实白话就是:给属性赋值.一共有两种方法,第一是以构造器参数的形式,另外一种就是以setting方法的形式. 1 构造器注入 1 使用构造器注入 使用xml的注入方式 A. 通过参数的顺序 <constructor-arg index="0"><value>张三</value></constructor-arg> <constructor-arg index="1"

  • 深入解析Java的Spring框架中bean的依赖注入

    每一个基于java的应用程序都有一个共同工作来展示给用户看到的内容作为工作的应用几个对象.当编写一个复杂的Java应用程序,应用程序类应该尽可能独立其他Java类来增加重复使用这些类,并独立于其他类别的测试它们,而这样做单元测试的可能性.依赖注入(或有时称为布线)有助于粘合这些类在一起,同时保持他们的独立. 考虑有其中有一个文本编辑器组件的应用程序,要提供拼写检查.标准的代码将看起来像这样: public class TextEditor { private SpellChecker spell

  • 如何用Java注解和反射实现依赖注入

    概述 在Spring中,我们可以通过 @Autowired注解的方式为一个方法中注入参数,那么这种方法背后到底发生了什么呢,这篇文章将讲述如何用Java的注解和反射实现一个"低配版"的依赖注入. 下面是我们要做的一些事情: 通过 @interface的方式定义一个注解 为某个希望杯被注入的方法添加这个注解 编写测试代码,通过反射获取添加了注解的方法对应的Method对象,将该方法对象设置为可访问的,通过反射创建对象并调用这个方法,同时注入依赖数据 如上所述,我们分为三个步骤, 去加工出

  • JAVA使用quartz添加定时任务,并依赖注入对象操作

    最近在写定时任务,以前没接触过.查了些相关资料说使用quartz定时框架. 需要配置文件:config-quartz.xml 相关配置如下(红色部分是之后添加的,在后面步骤会说明): <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www

  • 详解Java Spring各种依赖注入注解的区别

    注解注入顾名思义就是通过注解来实现注入,Spring和注入相关的常见注解有Autowired.Resource.Qualifier.Service.Controller.Repository.Component. Autowired是自动注入,自动从spring的上下文找到合适的bean来注入 Resource用来指定名称注入 Qualifier和Autowired配合使用,指定bean的名称 Service,Controller,Repository分别标记类是Service层类,Contro

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

    一.C++的诟病 C++最遭人诟病的地方就是定义一个类需要写两个文件,一个.h文件和一个.cpp文件.例如定义一个CMainFrame类,mainframe.h内容如下: class CMainFrame : public CFrameWndEx { protected: CMainFrame(); public: virtual ~CMainFrame(); }; mainframe.cpp内容如下: CMainFrame::CMainFrame() { } CMainFrame::~CMai

  • Java基础之教你怎么用代码一键生成POJO

    一.前言 在写SpringBoot项目,有时候设计到的表有几十上百张,如果要一个一个手动创建JavaBean以及对应的mapper类的话,虽然支持CV的过程.但是也让人很头大. 好在Myabtis-Plus提供了一个代码生成器,可以帮助我们根据表生成对应的controller.service.mapper.entity. 接下来看看如何使用这个代码生成器. 二.使用 首先在项目中加入代码生成器的依赖 当前最新版本是3.4.2.具体可以到官网去查看. <dependency> <group

  • JavaWeb基础教程之Java基础加强版

    1.myeclipse的安装和使用 * eclipse:是一个免费的开发工具 * myeclipse:是一个收费的插件,破解myeclipse, ** 安装目录的要求: 不能有中文和空格 ** 安装完成之后,选择一个工作空间 ,这个工作空间不能有中文和空格 * 破解myeclipse ** 运行run.bat文件,但是运行之前,必须要安装jdk,通过配置环境变量 * myeclipse的使用 * 创建一个工程 - 类型 java project web project - 选择依赖的jdk,可以

  • java JDBC系列教程之JDBC类的简析与JDBC的基础操作

    什么是JDBC? 概念:JAVA Database Connectivity Javas数据库连接,Java语言操作数据库接口,然后由各个数据库厂商去实现这个接口,提供数据库驱动java包,我们可以使用这套接口,真正执行的是jar驱动包中的实习类 使用一张图让大家更为直观的理解: coder就是写这套接口的程序员 JDBC的使用步骤 1.导入驱动jar包 2.注册驱动 3.获取数据库连接对象 4.定义sql执行语句 5.获取sql语句执行对象 6.执行sql语句返回结果 7.处理结果 8.释放结

  • Java基础教程之final关键字浅析

    前言 前面在讲解String时提到了final关键字,本文将对final关键字进行解析. static和final是两个我们必须掌握的关键字.不同于其他关键字,他们都有多种用法,而且在一定环境下使用,可以提高程序的运行性能,优化程序的结构.下面我们来了解一下final关键字及其用法. final从总体上来说是"不可变的",可用于修改类.方法.变量. 一. final类 final修饰的类,该类不能被继承.当你确认一个类永远不会被继承或不想被继承,那么就可以用final修饰. 同样,对于

  • Java基础教程之对象的方法与数据成员

    在Java基础教程之从Hello World到面向对象一文中,我们初步了解了对象(object).对象中的数据成员表示对象的状态.对象可以执行方法,表示特定的动作. 此外,我们还了解了类(class).同一类的对象属于相同的类型(type).我们可以定义类,并使用该定义来产生对象. 我们进一步深入到对象.了解Java中方法与数据成员的一些细节. 调用同一对象的数据成员 方法可以调用该对象的数据成员.比如下面我们给Human类增加一个getHeight()的方法.该方法返回height数据成员的值

  • Java基础知识杂文

    1.基本概念 IO是主存和外部设备(硬盘.终端和网络等)拷贝数据的过程.IO是操作系统的底层功能实现,底层通过I/O指令进行完成. 所有语言运行时系统提供执行I/O较高级别的工具.(c的printfscanf,java的面向对象封装) 2.Java标准io回顾 Java标准IO类库是io面向对象的一种抽象.基于本地方法的底层实现,我们无须关注底层实现.InputStream\OutputStream(字节流):一次传送一个字节.Reader\Writer(字符流):一次一个字符. 3.nio简介

  • Java基础之代码死循环详解

    一.前言 代码死循环这个话题,个人觉得还是挺有趣的.因为只要是开发人员,必定会踩过这个坑.如果真的没踩过,只能说明你代码写少了,或者是真正的大神. 尽管很多时候,我们在极力避免这类问题的发生,但很多时候,死循环却悄咪咪的来了,坑你于无形之中.我敢保证,如果你读完这篇文章,一定会对代码死循环有一些新的认识,学到一些非常实用的经验,少走一些弯路. 二.死循环的危害 我们先来一起了解一下,代码死循环到底有哪些危害? 程序进入假死状态, 当某个请求导致的死循环,该请求将会在很大的一段时间内,都无法获取接

  • Java基础之颜色工具类(超详细注释)

    颜色工具类(超详细注释) 设置属性值自动格式化rgb值和十六进制颜色值. import java.util.HashMap; import java.util.Map; public class Color{ private final String[] hex_letters = {"0", "1", "2", "3", "4", "5", "6", "7

  • Java基础之数组详解

    前言 我们了解数组这个概念之前,我们先思考下面几个问题. 如果我们需要两个数据,那么直接创建两个变量即可 int a; int b; 如果需要五个数据,那么可以创建五个变量 int a; int b; int c; int d; int f; 但如果我们需要100个甚至是1万个数据,那么我们创一万个变量?显然这是不现实的.这个时候就需要我们的数组来起作用!帮我们"批量"创建变量. 由上可以得出:数组的本质就是让我们能"批量"创建相同类型的变量! 一.数组的概念 数组

随机推荐