当前位置: 首页 >Java技术 > Spring Aop (MethodInterceptor) (企业实战)

Spring Aop (MethodInterceptor) (企业实战)

Spring-AOP


* AOP 为 Aspect Oriented Programming 的缩写,意为:面向切面编程,通过预编译 方式和运行期动态代理实现程序功能的统一维护的一种技术。
* 主要功能
* 日志记录,性能统计,安全控制,事务处理,异常处理等等
* 主要意图
* 将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划 分出来,通过对这些行为的分离,我们希望可以将它们独立到非指导业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码。
* AOP与OOP的区别(面试题)
* OOP(面向对象编程)针对业务处理过程的实体及其属性和行为进行抽象封装,以获 得更加清晰高效的逻辑单元划分。
* 而 AOP 则是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果。这两种设计思想在目标上有 着本质的差异。
* 换而言之,OOD/OOP 面向名词领域,AOP 面向动词领域。
* AOP相关术语
* 目标对象(target)
* 指的是需要被增强的对象,由于 spring aop 是通过代理模式实现,从而这个对象永远是被代 理对象
* 连接点(join point)
* 所谓连接点是指那些被拦截到的点,在 spring 中这些点指的是方法(所有的(偏差)),因为 spring 只支持方法 类型的连接点
* 切入点(pointcut)
* 表示一组 joint point,这些 joint point 或是通过逻辑关系组合起来,或是通过通配、正表 达式等方式集中起来,它定义了相应的 Advice 将要发生的地方 简单说切入点是指我们要对哪些连接点进行拦截的定义(被挖掘走的共性方法(偏差))
* 通知(advice)
* 所谓通知是指拦截到连接点之后所要做的事情就是通知,通知分为前置通知,后置通知,异常通知,最终通知,环绕通知
* Advice 定义了在 pointcut 里面定义的程序点具体要做的操作
* 通知类别:共性内容的位置!
* 引介(introduction):特殊的通知
* 切面(aspect):切入点与通知的结合
* 织入(weaving):procedure-->一个将抽取的方法,在运行的时候,放回程序的过程
* 织入手段:(了解内容)
* 编译器织入:AOP将共性内容抽取,一旦共性内容抽取,原来的模块就没有这个功能,只有在最终运行时,添加回来,编译期间就将共性的内容织入回来-->运行的类或者方法(完整)-->快,不灵活!
* 类加载器织入:类加载的时候,织入
* 类运行时织入:代码运行阶段,动态的织入回来-->慢,灵活
* 代理(Proxy):一个类被 AOP 织入增强后,就产生一个结果代理类

* AOP底层实现
* JDK动态代理
* CGLIB动态代理(再次加强!)
* 问题:spring 采用的是哪一种动态机制:
* 如果目标对象,有接口,优先使用 jdk 动态代理
* 如果目标对象,无接口,使用 cglib 动态代理

* Spring 整合 aspectj 框架实现的 aop
* Aspect:切面 =切点+通知(多个切点与多个通知的组合)
* AspectJ 它是一个第三方框架,spring 从 2.0 后可以使用 aspectJ 框架的部分语法.
* AspectJ 框架它定义的通知类型有 6 种
* 1. 前置通知 Before 相当于 BeforeAdvice
* 2. 后置通知 AfterRetuing 相当于 AfterRetuingAdvice
* 3. 环绕通知 Around 相当于 MethodInterceptor
* 4. 抛出通知 AfterThrowing 相当于 ThrowAdvice
* 5. 引介通知 DeclareParents 相当于 IntroductionInterceptor
* 6. 最终通知 After 不管是否异常,该通知都会执行
* 相比 spring 的传统 AOP Advice 多了一个最终通知

* 切点表达式的写法

* 在开发中使用的比较多的是 execution 语法.

 

expression(表达式)="excution执行(*返回值 包.类.方法(形参..))", 其中* 代表通配符

* 关于 execution 语法常用:

 

* 1. execution(public * *()) 所有的 public 的方法
* 2. execution(* cn.test.aop.*(..)) 所有的 aop 包下的所有类的方法(不包含子包)
* 3. execution(* cn.test.aop..*(..)) 所有的 aop 包及其子包下的所有类的方法
* 4. execution(* cn.test.aop.IOrderService.*(..)) IOrderService 接口中定义的所有方法
* 5. execution(* cn.test.aop.IOrderService+.*(..)) 匹配实现特定接口所有类的方法
* 6. execution(* save*(..)) 匹配所有的以 save 开头的方法

* 基于xml方案
* 第一步:创建target类
* 第二步:创建通知(无需实现接口)
* 第三步:在applicationContext.xml文件中配置(一般都使用环绕通知)
* <aop:config>下的<aop:aspect>是 aspectJ 框架用来声明切面的
* 利用aspect标签上的,ref 关联 advice
*
* <aop:before>里面有 method方法和pointcut需要配置任意表达式,可以配置多个<aop:before>!
* <aop:pointcut expression="xxxx" id="xxx" />这里配置公共切入点,在advice中可以设置ref引用
*
* 注:环绕通知要有返回值Objcet!有参数!有目标行为!还要抛出异常
*
* 异常抛出通知 after-throwing
* 最终通知 after
* 关于通知上的参数:
* 前置通知(JoinPoint)放在advice的方法上,完成日志记录,权限控制
* 后置通知(JoinPoint jp,Object val),val是目标方法的返回值!同时在配置文件中<aop:after_retuing>标签上,相应的retuting参数与之对应!
*
* 环绕通知参数:ProceedingJoinPoint!!!它是我们开发中应用最多的,可以完成日志操作,权限操作,性能监控,事务管理
*
* 抛出异常通知上的参数:JoinPoint , Throwable ex ,配置文件也要配置
*
*
* 最终通知: JoinPoint,可以使用最终通知完成资源释放
* 代理方式的选择:
*
* Proxy-target-class 的值默认是 false,它代表有接口使用 proxy 代理
* 问题:如果现在对目标要使用 cglib 代理(不考虑是否有接口)?

 

* 只需要将 proxy-target-class 设置为 true.

 

企业中实战(干货):异常拦截器aop

MethodInterceptor注意这个导包为:

import org.aopalliance.intercept.MethodInterceptor;

 

<bean id="exceptionHandlerInterceptor"  class="com.test.interceptor.ExceptionHandlerInterceptor"/><aop:config><aop:pointcut id="rpcPointCut"  expression="execution(public * com.test.*FacadeImpl.*(..))"/><aop:advisor pointcut-ref="rpcPointCut" advice-ref="exceptionHandlerInterceptor" order="1"/></aop:config>

 

其中ExceptionHandlerInterceptor

ublic class ExceptionHandlerInterceptor implements MethodInterceptor {private static final Logger LOGGER = Logger.getLogger(ExceptionHandlerInterceptor.class);/** * 拦截调用执行 * * @param invocation * @retu * @throws Throwable */@Overridepublic Object invoke(MethodInvocation invocation) throws Throwable {Object response = invocation.getMethod().getRetuType().newInstance();DelegateResult result;if (response instanceof Result) {result = new DelegateResult((Result) response);} else if (response instanceof BaseResult) {result = new DelegateResult((BaseResult) response);} else {retu null;}try {retu invocation.proceed();} catch (TestISBusinessException e) {//暂不做重试处理LoggerUtil.info(LOGGER, "已知业务异常,直接返回!");result.setErrorContext(ErrorContextUtil.generateErrorContext(e));result.setSuccess(false);} catch (TestISUnRetryException e) {//暂不做重试处理LoggerUtil.wa(LOGGER, e, "不需要重试,直接返回!");result.setErrorContext(ErrorContextUtil.generateErrorContext(e));result.setSuccess(true);} catch (TestISException e) {LoggerUtil.error(LOGGER, e, "业务异常: 异常码: " + e.getCode() + ",异常码描述: "+ e.getCode().getDescription());result.setErrorContext(ErrorContextUtil.generateErrorContext(e));result.setSuccess(false);} catch (BPException e) {LoggerUtil.error(LOGGER, e, "流程(节点)处理异常:" + e.getMessage());TestISException testISException = getOrigException(e, 5);if (testISException != null) {result.setErrorContext(ErrorContextUtil.generateErrorContext(testISException));} else {result.setErrorContext(ErrorContextUtil.generateErrorContext(new TestISException(TestISErrorCodeEnum.UNKNOWN_EXCEPTION)));}result.setSuccess(false);} catch (DuplicateKeyException e) {LoggerUtil.error(LOGGER, e, "数据库幂等异常");TestISException mrchISException = new MrchISException(TestISErrorCodeEnum.DATA_DUPLICATE);result.setErrorContext(ErrorContextUtil.generateErrorContext(testISException));result.setSuccess(false);} catch (Throwable e) {LoggerUtil.error(LOGGER, e, "系统异常:" + e.getMessage());result.setErrorContext(ErrorContextUtil.generateErrorContext(new TestISException(TestISErrorCodeEnum.UNKNOWN_EXCEPTION)));result.setSuccess(false);}retu result.getResult();}

接下来是注解形式的切面, 我现在写一个日志记录切面

 

Spring的配置文件是如下的配置:

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd><bean id="registerDao" class="com.zxf.dao.RegisterDaoImpl"/><bean id="registerService" class="com.zxf.service.RegisterServiceImpl"><property name="registerDao" ref="registerDao"/></bean><!-- 把切面类交由Spring容器来管理 --><bean id="logAspectBean" class="com.zxf.aspect.LogAnnotationAspect"/><!-- 启用spring对AspectJ注解的支持 --><aop:aspectj-autoproxy/></beans>

 

 

这是那个切面的类LogAnnotationAspect

 

import org.aspectj.lang.JoinPoint;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.AfterRetuing;import org.aspectj.lang.annotation.AfterThrowing;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Pointcut;/** * 日志切面类 */@Aspect //定义切面类public class LogAnnotationAspect {@SuppressWaings("unused")//定义切入点,提供一个方法,这个方法的名字就是改切入点的id@Pointcut("execution(* com.zxf.service.*.*(..))")private void allMethod(){}//针对指定的切入点表达式选择的切入点应用前置通知@Before("execution(* com. zxf.service.*.*(..))")public void before(JoinPoint call) {String className = call.getTarget().getClass().getName();String methodName = call.getSignature().getName();System.out.println("【注解-前置通知】:" + className + "类的"+ methodName + "方法开始了");}//访问命名切入点来应用后置通知@AfterRetuing("allMethod()")public void afterRetu() {System.out.println("【注解-后置通知】:方法正常结束了");}//应用最终通知@After("allMethod()")public void after(){System.out.println("【注解-最终通知】:不管方法有没有正常执行完成,"+ "一定会返回的");}//应用异常抛出后通知@AfterThrowing("allMethod()")public void afterThrowing() {System.out.println("【注解-异常抛出后通知】:方法执行时出异常了");}//应用周围通知//@Around("allMethod()")public Object doAround(ProceedingJoinPoint call) throws Throwable{Object result = null;this.before(call);//相当于前置通知try {result = call.proceed();this.afterRetu(); //相当于后置通知} catch (Throwable e) {this.afterThrowing(); //相当于异常抛出后通知throw e;}finally{this.after(); //相当于最终通知}retu result;}}

 

 

作者:晴天小哥哥
来源链接:https://blog.csdn.net/weixin_38399962/article/details/79882226

版权声明:
1、JavaClub(https://www.javaclub.cn)以学习交流为目的,由作者投稿、网友推荐和小编整理收藏优秀的IT技术及相关内容,包括但不限于文字、图片、音频、视频、软件、程序等,其均来自互联网,本站不享有版权,版权归原作者所有。

2、本站提供的内容仅用于个人学习、研究或欣赏,以及其他非商业性或非盈利性用途,但同时应遵守著作权法及其他相关法律的规定,不得侵犯相关权利人及本网站的合法权利。
3、本网站内容原作者如不愿意在本网站刊登内容,请及时通知本站(javaclubcn@163.com),我们将第一时间核实后及时予以删除。





本文链接:https://www.javaclub.cn/java/117182.html

标签:SpringAOP
分享给朋友:

“Spring Aop (MethodInterceptor) (企业实战)” 的相关文章