前言
上一篇我们简单了解了早期的开发历史进程,也看到了框架等技术出现的必然性,这篇文章就开始讲讲为什么在众多框架中Spring脱颖而出、备受宠爱。这里主要通过对使用Spring的两大特性即依赖注入和AOP的好处讲解来体现Spring的价值。
为什么使用依赖注入
1、面向接口编程
参考原文:https://blog.csdn.net/xlgen157387/article/details/78884005
以持久化层接口UserDao为例,有一个接口的实现类UserDaoImpl,由于是面向接口编程,因此我们在每次使用UserDao的时候,都要进行实例化一次,实例化代码如下:
UserDao userDao = new UserDaoImpl();
传统时我们在每次使用UserDao的时候都需要进行实例化,当然不仅仅有UserDao需要进行实例化,还有很多需要进行实例化的,也就是每一个方法中都需要进行实例化我们需要用到的接口的实现类,这就会存在大量的实例化对象,并且他们的生命周期可能就是从方法的调用开始到方法的调用结束为止,加大了GC回收的压力!
了解设计模式的可能会想到使用单例模式的方式来解决这个问题,以此来避免大量重复的创建对象,但是我们还要考虑到众多的这种对象的创建都需要改成单例模式的话,是一个耗时耗力的操作。对于这个系统来说,如果都把这种面向接口的对象实现类转换为单例模式的方式的话,大概也要写十几个或者上百个这种单例模式代码,而对于一个单例模式的写法来说,往往是模板式的代码。
可以看出,这种方式有两个问题:
(1)业务代码与单例模式的模板代码放在一个类里,耦合性较高;
(2)大量重复的单例模式的模板代码;
从上述可以看出,使用的单例模式虽然从性能上有所提高,但是却加重了我们的开发成本。因此只会小规模的使用,例如我们操作JDBC的Utils对象等。从上述代码的演进过程我们可以看得出来,我们即需要一个单例的对象来避免系统中大量重复对象的创建和销毁,又不想因为使用单例模式造成大量重复无用的模板代码和代码的耦合!
我们可能会联想到“数据库连接池”,我们在获取数据库连接的时候会从这个池子中拿到一个连接的,假设这个数据库连接池很特殊,有且只能有N个数据库连接,并且每一个连接对象都不同(假设),那么这个不就相当于每一个连接都是单例的了吗?既可以避免大量对象的创建,也可以实现不会出现大量重复性的模板代码,因此,这里应该有一个大胆的想法,我们是否可以建立一个池子,将我们的接口实现类对象放入到这个池子中,我们在使用的时候直接从这个池子里边取就行了!如果我们要创建这个池子,首先要确定需要把哪些对象放进这个池子,通过怎样的方式放进去,放进去之后如何进行管理,如何进行获取,池子中的每一个对象的生命周期是怎么样的等等这些东西都是我们需要考虑到的!
说到这里,上述我们想要创建的池子其实就是Spring容器的雏形,将接口实现类的对象放进池子进行管理的过程其实也是Spring IOC依赖注入、控制反转的雏形!Spring的依赖注入/控制反转就是从我们的配置文件或注解中的得到我们需要进行注入到Spring容器的实现类的信息,Spring IOC通过这些配置信息创建一个个单例的对象并放入Spring容器中,Spring容器可以看做是一个集合保存着我们的这些对象。
2、实例
参考原文:https://zhuanlan.zhihu.com/p/29426019
我们现在要使用ILogger来对我们系统中的日志进行输出,ILogger同样是一个接口,他下面的实现类有FileLogger(将日志打印到文件)、ServerLogger(将日志打印到一台日志服务器)等。首先我们使用传统的方式创建对象,我们的代码如下:
public class PaymentAction {
private ILogger logger = new FileLogger();
public void pay(BigDecimal payValue) {
logger.log("pay begin, payValue is " + payValue);
// do otherthing
// ...
logger.log("pay end");
}
}
如果在我们的系统的每个类中都添加日志打印,那我们就需要在所有类中添加创建对象的代码。。。
当我们把所有类都添加好后,我们现在突然需要改需求,原来我们都实现FileLogger将日志打印到文件,现在要实现ServerLogger,我们要将日志打印到一台日志服务器上,一个简单的方法当然是全局将所有对象创建的方法替换,但是如果下次要改成将日志打印到控制台呢,还要在全局替换一次吗?明显这样做是有很大问题的。这就是对象间的耦合性。
这时我们可能想到设计模式中的工厂模式,我们将代码改成如下形式:
public class LoggerFactory {
public static ILogger createLogger() {
return new ServerLogger();
}
}
public class PaymentAction {
private ILogger logger = LoggerFactory.createLogger();
public void pay(BigDecimal payValue) {
logger.log("pay begin, payValue is " + payValue);
// do otherthing
// ...
logger.log("pay end");
}
}
这样我们就把logger对象的创建交给外部去做,有了这个LoggerFactory,以后要是要换日志打印的方式,只需要修改这个工厂类就好了。
虽然这样做到了一定的解耦,解决了不用到处改代码的问题,但是还存在几个问题:
- 工厂类每次都new一个新对象,是不是很浪费,能不能做成单例的,甚至是做成单例和多例是可以配置;
- 如果有这种需求:日志部分需要打印到远程服务器,其他要打印到本地,怎么实现;
这时我们再来看看Spring,Spring的依赖注入就是将对象交给外部去创建的机制,并且Spring支持单例、多例转化,想在哪里实现什么只需要在xml中配置(当然不仅仅是在xml中配置)就可以了。
3、小结
这里我们参考了两个例子来说明了一下依赖注入的好处,这里简单总结一下为什么要使用依赖注入:
- 传统的代码,每个对象负责管理与自己需要依赖的对象,导致如果需要切换依赖对象的实现类时,需要修改多处地方。同时,过度耦合也使得对象难以进行单元测试。
- 依赖注入把对象的创造交给外部去管理,很好的解决了代码紧耦合(tight couple)的问题,是一种让代码实现松耦合(loose couple)的机制。
- 松耦合让代码更具灵活性,能更好地应对需求变动,以及方便单元测试。
为什么使用AOP
参考原文:https://blog.csdn.net/xlgen157387/article/details/78892509
在面向对象的大环境下,我们可以很好地组织代码,通过继承、封装和多态的思想去设计一个个比较让人满意的类,但是我们慢慢的发现,我们的代码中逐渐多了很多重复性的代码,下面举一个例子:
public class PaymentAction {
private ILogger logger = new FileLogger();
public void pay(BigDecimal payValue) {
logger.log("传入的参数是: " + payValue);
// do otherthing
logger.log("结果是");
}
public void pay1(BigDecimal payValue) {
logger.log("传入的参数是: " + payValue);
// do otherthing
logger.log("结果是");
}
public void pay2(BigDecimal payValue) {
logger.log("传入的参数是: " + payValue);
// do otherthing
logger.log("结果是");
}
}
可以看到,上述代码中logger.log("传入的参数是: " + payValue);输出日志,虽然功能上确实可以实现,但是我们的业务代码已经被这些非核心的代码所混淆,并且占据了大量的空间!显然这种显示的调用过程成为了我们开发过程中的一个痛点,如何将类似这种的非核心的代码剥离出去成为一个迫切需要解决的问题!
不仅如此,假设我们要控制每一个方法的访问权限,只允许一部分用户进行访问,在不考虑过滤器的情况下,我们是不是需要在每一个方法开始的时候判断用户是否具有该权限,如果有的话就可以进行访问,如果没有的话,就不允许进行访问!
诸如此类,还有数据库事务的控制,数据库连接的创建和关闭等等,这些都充斥这大量重复性的模板代码!一个很现实的问题,假如有一天,业务需求不需要进行日志记录了,那岂不是我们需要把以前写的代码,全部删掉!想想都是一件很可怕的事情!
这时一个非常重要的思想就被提出来了,那就是AOP。AOP是一种思想,不同的厂商或企业可能有不同的实现方式,例如:AspectJ、AspectWerkz、JBoss AOP、Spring AOP等。这里在给出AOP的概念来理解一下:
AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容(Spring核心之一),是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
这里可以理解为,我们正常的一些业务流程完整的走下来,时连续不断的,这是我们使用一个切面插入到这些完整的业务流程中,使得我们的一些常用的功能被重业务代码中抽取出来,从另一个方向来实现,如图:
我们把一些日志记录、安全验证的操作通过AOP 的形式插入到业务流程中的代码中。使得我们的代码更加注重业务流程,更简洁。
小结
这里我们举例简单的了解一下为什么要使用AOP,以及AOP与Spring的关系等。而并没有对SpringAOP做过多介绍,包括AOP的分类及使用等,我们留待之后在总结。
总结
本章主要从为什么使用依赖注入、aop两大Spring特性,进而说明Spring使用的意义及优点。后面再继续通过对Spring的版本、特性详解、使用实例、常用整合等方面对Spring做全面的总结。