Spring AOP失效之谜
每天学习一点点 编程PDF电子书免费下载: http://www.shitanlife.com/code
什么是AOP
1
AOP(Aspect Oriented Programming),即面向切面编程,其是OOP(Object Oriented Programming,面向对象编程)的补充和完善。在面向对象编程的世界中,我们很容易理解OOP的思想,简单来说,OOP引入封装、继承、多态等概念来建立一种对象层次结构,这种层次结构是纵向的。虽然OOP允许开发者定义纵向的关系,但并不适合定义横向的关系,例如日志功能。日志代码往往横向地散布在所有对象层次中,而与它对应的对象的核心功能关系不大,对于其他类型的代码,如安全性检查、异常处理、事务处理等也都是如此,这种散布在各处的重复的代码被称为横切逻辑,在OOP设计中,它导致了大量代码的重复,不利于各个功能模块的重用。
AOP技术则恰恰相反,它利用一种称为"横切"的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块之中,并将其命名为"Aspect",即切面。所谓"切面",简单说就是将那些被多个业务模块所共同调用的逻辑封装起来,以达到减少重复代码,降低模块之间的耦合度,并提高系统的可维护性的目的。
使用"横切"技术,AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与业务逻辑关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处基本相似,比如权限认证、日志、事务管理等。AOP的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。
AOP示例2
前面说了这么多,接下来我们就用Spring AOP来实现简单的日志记录功能吧。假如我们已经有了一个功能完善的用户登陆接口,现在我们需要在用户调用登陆接口的前后记录下用户的登录行为日志。要实现该功能,最简单的方法就是在原有的登录逻辑里加入日志记录代码,但是这样一来势必需要对原有的登录逻辑进行修改,容易引入新的bug,因此我们决定使用AOP来实现日志记录的功能。在本实例中,我们搭建一个Spring Boot工程,并引入Spring AOP依赖,pom文件依赖关系如下:
接着我们实现最原始的登录逻辑:
登录逻辑十分简单,首先判断用户是否为合法用户,如果是则可以正常登录,如果不是则禁止登录。
由于我们要在登录逻辑前后加入日志功能,所以我们需要编写一个环绕通知:
可以看到,我们的环绕增强针对login方法进行横切逻辑的织入,在调用目标对象的前后,分别对用户登录日志进行记录。
接下来写一个测试类看一下效果:
结果输出如下:
可以看到,我们通过AOP很方便地实现了日志记录功能。
AOP失效的情形3
接下来假如我们又有了一个新需求,就是要对不合法用户做些特殊的处理,比如说统计下不合法用户调用登陆接口的次数。由于直接修改原有的登录逻辑有很多弊端,所以我们还是选择通过AOP来实现该功能。这可以通过编写一个返回增强来实现:
我们对isLegal方法进行增强,先拿到isLegal方法的返回值,再根据该返回值决定是否需要累加登录次数。
接下来我们还是用上一节的测试类来测试一下,我们直接看结果:
这个时候诡异的事情发生了。明明user_1为非法用户,但是为何没有对其登录次数进行累加呢?AOP为何会失效呢?下文将为你解开谜团。
AOP为何失效
4
之所以会出现上述AOP失效的现象,归根到底是由于AOP的实现机制导致的。Spring AOP采用代理的方式实现AOP,我们编写的横切逻辑被添加到动态生成的代理对象中,只要我们调用的是代理对象,则可以保证调用的是被增强的代理方法。而在代理对象中,不管你的横切逻辑是怎样的,也不管你增加了多少层的横切逻辑,有一点可以确定的是,你终归会调用目标对象的同一方法来调用原始的业务逻辑。
如果目标对象中的原始方法依赖于其他对象,那么Spring会注入所依赖对象的代理对象,从而保证依赖的对象的横切逻辑能够被正常织入。而一旦目标对象调用的是自身的其他方法时,问题就来了,这种情况下,目标对象调用的并不是代理对象的方法,故被调用的方法无法织入横切逻辑。
如上图所示,method1和method2方法是同个类中的方法,当外部通过代理对象调用method1时,最终会调用目标对象的method1方法,而在目标对象的method1方法中调用method2方法时,最终调用的是目标对象的method2方法,而不是代理对象的method2方法,故而针对method2的AOP增强失效了。
如何避免AOP失效5
要解决上述Spring AOP失效的问题,有两个方法,一个是将isLegal方法跟login方法写在不同的类里,这样一来,当login方法调用isLegal方法时,Spring会注入相应的代理对象,从而可以调用到isLegal方法的代理逻辑。另一个方法是在调用isLegal方法时先获取当前上下文的代理对象,再通过该代理对象调用被增强了的isLegal方法,这样一来也能解决AOP失效的问题。实际上Spring AOP为我们提供了获取当前上下文代理对象的方法,使用起来非常方便,首先需要在AOP配置里暴露代理对象,在Spring Boot中可以通过注解@EnableAspectJAutoProxy(exposeProxy = true)进行配置:
然后修改login方法,通过AopContext获取当前上下文代理对象,再通过该代理对象调用isLegal方法:
最后我们运行测试类看下效果:
可以看到,现在已经可以实现对非法用户的登录次数进行累加了,这样就解决了上述AOP失效的问题。
每天学习一点点 编程PDF电子书免费下载: http://www.shitanlife.com/code
Spring AOP失效之谜的更多相关文章
- Spring AOP学习笔记05:AOP失效的罪因
		
前面的文章中我们介绍了Spring AOP的简单使用,并从源码的角度学习了其底层的实现原理,有了这些基础之后,本文来讨论一下Spring AOP失效的问题,这个问题可能我们在平时工作中或多或少也会碰到 ...
 - Spring aop注解失效
		
问题 在spring 中使用 @Transactional . @Cacheable 或 自定义 AOP 注解时,对象内部方法中调用该对象的其他使用aop机制的方法会失效. @Transactiona ...
 - Spring Aop 动态代理失效分析
		
1. Spring Aop 原理 Spring Aop 通过动态代理创建代理对象,在调用代理对象方法前后做增强. 2. Transactional, Async 注解失效? 当在动态代理方法中调用当前 ...
 - Spring同一个类中的注解方法调用AOP失效问题总结
		
public interface XxxService { // a -> b void a(); void b(); } @Slf4j public class XxxServiceImpl ...
 - Spring AOP注解为什么失效?90%Java程序员不知道
		
使用Spring Aop注解的时候,如@Transactional, @Cacheable等注解一般需要在类方法第一个入口的地方加,不然不会生效. 如下面几种场景 1.Controller直接调用Se ...
 - Java缓存框架使用EhCache结合Spring AOP
		
一.Ehcache简介 EhCache是一个纯Java的进程内缓存框架,具有如下特点: 1. 快速简单,非常容易和应用集成. 2.支持多种缓存策略 . 3. 缓存数据有 ...
 - Spring AOP不起作用原因
		
一.直接在切面类定义切点: AOP切面类里面的方法全部不支持触发切面,否则一个切面函数把自己当做切点就会导致递归层层调用. AOP切面类发出函数调用一律不触发切面,避免两个切面类相互调用迭代请求的情况 ...
 - Spring事务失效的原因
		
http://blog.csdn.net/paincupid/article/details/51822599 Spring事务失效的原因 5种大的原因 如使用mysql且引擎是MyISAM,则事务会 ...
 - Spring AOP声明式事务异常回滚(转)
		
转:http://hi.baidu.com/iduany/item/20f8f8ed24e1dec5bbf37df7 Spring AOP声明式事务异常回滚 近日测试用例,发现这样一个现象:在业务代码 ...
 
随机推荐
- 搭建前端监控系统(四)Js截图上报篇
			
===================================================================== 前端监控系统: DEMO地址 GIT代码仓库地址 ==== ...
 - 《深入理解Java虚拟机》(五)JVM调优 - 工具
			
JVM调优 - 工具 JConsole:Java监视与管理控制台 JConsole是一个机遇JMX(Java Management Extensions,即Java管理扩展)的JVM监控与管理工具,监 ...
 - c#unicode,中文互转
			
/// <summary> /// 中文转unicode /// </summary> /// <returns></returns> public s ...
 - oracle的start with connect by prior
			
oracle的start with connect by prior是根据条件递归查询"树",分为四种使用情况: 第一种:start with 子节点ID='...' connec ...
 - 【github&&git】7、gitignore 修改不起作用
			
在git使用过程中有时会遇到修改了.gitignore文件,修改了之后发现,不能起作用,这是因为git存在缓存问题,所以做一下步骤即可: git rm -r --cached . git add . ...
 - 最新版本elasticsearch本地搭建入门篇
			
最新版本elasticsearch本地搭建入门篇 项目介绍 最近工作用到elasticsearch,主要是用于网站搜索,和应用搜索. 工欲善其事,必先利其器. 自己开始关注elasticsearch, ...
 - kafka安装与简单使用
			
一.kafka安装 安装是非常简单的,现在推荐安装0.8的版本,这个版本是非常稳定的,而且公司里面也多用此版本. 简单的安装: 这个是我使用的版本,kafka_2.11-0.8.2.2.tgz 直接t ...
 - cookie、localStorage、sessionStorage的区别
			
localStorage - 没有时间限制的数据存储 sessionStorage - 针对一个 session 的数据存储 共同点:sessionStorage.localStorage和cooki ...
 - javascript: checked 不可全选
			
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
 - Node.js+Koa开发微信公众号个人笔记(二)响应事件
			
微信公众号中的事件有订阅事件/扫码事件/点击事件/跳转链接事件等等,具体可以查阅文档. 这里来实现一下订阅事件,其他的事件的实现过程也都类似. 当有人订阅了公众号后,微信服务器会向我们的服务器推送一个 ...