文章目录
一、Spring整合junit
1.1、导入spring整合junit的jar
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.2.1.RELEASE</version> <scope>test</scope> </dependency>
1.2、在测试类上添加注解
@ContextConfiguration(classes = {SpringMainConfig.class}) @RunWith(SpringJUnit4ClassRunner.class) public class ConditionalTest { @Autowired private Pig pig; @Test public void testPig() throws Exception{ System.out.println(pig); } }
1.3、说明
@ContextConfiguration注解需要注意的细节是:
- classes:指定的是主配置类的字节码
- locations:指定xml文件的位置
二、面向切面编程(AOP)
2.1、问题引出
首先来看一个问题,假如我现在有一个UserServiceImpl用户业务类,其中呢,有一个保存用户的方法,即:
public class UserServiceImpl { // 保存用户 public void save(User user){ // 调用Dao新增用户 } }
现在的需求是:我要在保存用户之前新增事务的功能,你可能会这么做:
public void save(User user){ // 1、新增事务功能代码 // 调用Dao新增用户 }
现在的需求又变了,我要在保存用户之前不仅新增事务功能了,还要添加日志功能了,你可能又会这么做:
public void save(User user){ // 2、新增记录日志功能代码 // 1、新增事务功能代码 // 调用Dao新增用户 }
现在的需求又变了,我要在保存用户之前不仅新增事务功能了,日志功能了,还要增加开始执行保存用户方法的开始时间的功能了,你可能又会这么做:
public void save(User user){ // 3、新增统计开始执行保存用户方法的开始时间功能 // 2、新增记录日志功能代码 // 1、新增事务功能代码 // 调用Dao新增用户 }
现在的需求又变了,我要在保存用户之前不仅新增事务、日志功能、统计方法开始执行时间,还要新增用户是否有权限访问这个方法功能,你可能又会这么做:
public void save(User user){ // 4、新增判断用户是否有权限功能 // 3、新增统计开始执行保存用户方法的开始时间功能 // 2、新增记录日志功能代码 // 1、新增事务功能代码 // 调用Dao新增用户 }
需求又在一直改变改变着,可能忽然领导突然哪一天大腿一拍,说这些新增的功能都不要了,好,我们把我们辛苦写的代码全部注释了,也可能一气之下全删了;又过了几天,领导又抽风了,说我们需要新增事务和权限,好,我们又吭哧吭哧的去改代码了;…领导反复抽风,我们反复改反复改…最后我们顶不住压力,自杀了。
出现这个问题的原因在于:反复改需求,而这些需求与我们真正要保存用户的业务逻辑关系不大,同时,我们还应该需要考虑到的是:我们的系统中可能不仅仅只有用户模块,还有商品模块,订单模块,分类模块等等。而这些模块在保存相应的数据的时候,都有可能需要新增事务、日志、时间统计、权限等功能。那么如果我们对保存的数据方法都全部写一遍新增事务功能、新增日志功能、新增时间统计功能、新增权限功能,这样的代码无疑是很臃肿的,而且是很难维护的,想到这里,你可能会这么做,把公共的代码抽取出来,可能这么干:
public class BaseService { // 新增事务 public void transaction(){ System.out.println("事务功能"); } // 新增日志 public void log(){ System.out.println("日志功能"); } }
public class UserServiceImpl extends BaseService{ public void save(User user){ super.log(); super.transaction(); // 调用Dao新增用户 } }
解决方案:把公共代码抽取到一个父类的BaseService,让具体的模块Service继承BaseService,然后调用父类的功能,这样做即:通过继承的方式实现对业务方法的功能的增强。
但是这样做还有一个问题,虽然我们其他模块也需要此功能时,可以采用继承的方式来做,但是一个很严重的问题是:我们的业务功能在去调用的时候,对业务功能的增强实际上还是硬编码了。还是没有解决方便维护的问题,那我们期望能够解决的问题是:能否“神不知鬼不觉”的在不改变源代码的情况下去扩展功能呢? 答案肯定是可以的,那这就是AOP,同时,这个也是我们学习AOP的原因所在,也是AOP的作用所在。
从上图可以看出,如果从系统的横向角度来看,我们的权限功能,日志功能,事务功能是把系统的各个模块中的各个业务方法在执行之前从前面拦截了,好像拿了一把刀把一个完整的苹果切成一半,形成了一个切面。这个也就是=="面向切面编程==中切面的含义。
2.2、AOP
2.2.1、概念
AOP:全称是 Aspect Oriented Programming 即:面向切面编程/面向方面编程。AOP不是一项新的技术,是OOP(面向对象编程)技术的补充,是OOP的延续,同时也是Spring最重要的内容之一。
简单来说,AOP就是把我们程序重复的代码抽取出来,在需要执行的时候,使用代理技术,在不修改源码的基础上,对我们的已有方法进行增强。
Spring框架的AOP机制可以让开发者把业务流程中的通用功能抽取出来,单独编写功能代码。在业务流程执行过程中,Spring框架会根据业务流程要求,自动把独立编写的功能代码切入到流程的合适位置。
2.2.2、作用
在程序运行期间,不修改源码对已有方法进行增强。
2.2.3、优势
- 减少重复代码
- 提高开发效率
- 维护方便
2.2.4、实现方式
动态代理
2.2.5、专业术语
2.2.5.1、连接点
全英叫:Joinpoint。所谓连接点是指那些被拦截到的点。在 spring 中,这些点指的是方法,因为 spring 只支持方法类型的连接点。
2.2.5.2、切入点
全英叫:Pointcut。所谓切入点是指我们要对哪些 Joinpoint 进行拦截。
2.2.5.3、通知/增强
全英叫:Advice。所谓通知/增强是指拦截到 Joinpoint 之后所要做的事情就是通知。说白了,就是一段功能性的代码。
通知的类型
前置通知
后置通知
异常通知
最终通知
环绕通知
try{ // 前置通知【开启事务】 // 业务代码.... // 后置通知【提交事务】 }catch (Exception e){ // 异常通知 【回滚事务】 }finally { // 最终通知 【释放资源】 }
2.2.5.4、织入
全英叫:Weaving。是指把增强应用到目标对象来创建新的代理对象的过程,是一个动作。
2.2.5.5、切面
全英叫:Aspect。是切入点和通知/增强的结合。
2.2.5.6、引介【了解】
全英叫:Introduction。引介是一种特殊的通知在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field。
好书推荐
《快捷学习Spring》
对于Java开发人员来说,Spring是必须学习的框架。这个不可思议的开发工具支持从小型企业电子商务应用程序到企业级微服务的一切。掌握Spring是一个漫长的过程。迈出第一步很容易!从这里开始。
本书向Java开发人员介绍如何使用Spring框架构建应用程序。书中包含信息丰富的图表以及相关的例子,作者清晰生动的写作,能帮助读者轻松掌握所需的技能。本书阐述了如何规划、编写和测试应用程序。通过关注最重要的特性,本书为探索Spring丰富的生态系统提供了坚实的基础。
作者简介
Laurenţiu Spilcă是Endava的专职开发主管和培训师,他负责欧洲、美国和亚洲客户的金融市场项目开发。他有超过10年的开发经验。Laurenţiu相信,重要的不仅是交付高质量的软件,还要分享知识和帮助他人提升技能。这些信念驱使他去设计和讲授与Java技术相关的课程,并进行演示和参与研讨会。
购书链接:点此进入