spring中AOP 注解开发示例详解

网友投稿 221 2023-06-13


spring中AOP 注解开发示例详解

一、简介

AOP主要包含了通知、切点和连接点等术语,介绍如下:

通知(advice)

通知定义了切面是什么以及何时调用,何时调用包含以下几种

Before 在方法被调用之前调用通知

After 在方法完成之后调用通知,无论jWcvAI方法执行是否成功

After-returning 在方法成功执行之后调用通知

After-throwing 在方法抛出异常后调用通知

Around 通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为

切点(PointCut)

通知定义了切面的什么和何时,切点定义了何处,切点的定义会匹配通知所要织入的一个或多个连接点,

我们通常使用明确的类的方法名称来指定这些切点,或是利用正则表达式定义匹配的类和方法名称来指定这些切点。

连接点(JoinPoint)

连接点是在应用执行过程中能够插入切面的一个点,这个点可以是调用方法时,抛出异常时,甚至是修改一个字段时,

切面代码可以利用这些连接点插入到应用的正常流程中,并添加新的行为,如日志、安全、事务、缓存等。

joinPoint.getSignature().getDeclaringTypeName() 可以获取到调用方法的类名(包括包名),

joinPoint.getSignature().getName()    可以获取方法名,

Arrays.toString(joinPoint.getArgs())   得到的是方法调用的参数列表,

joinPoint.proceed()   可以得到方法的返回结果

二、注解开发

声明一个切面,只需要在类名上添加@Aspect属性即可,具体的连接点,我们用@Pointcut和@Before、@After等标注。

在声明前 我们需要依赖配置pom

org.aspectj

aspectjrt

1.6.11

org.aspectj

aspectjweaver

1.6.11

例子

package com.ganji.demo.service.aspect;

import org.aspectj.lang.ProceedingJoinPoint;

import org.aspectj.lang.annotation.*;

import org.springframework.stereotype.Service;

/**

* Created by admin on 2015/9/2.

*/

@Aspect

@Service

public class XmlAopDemoUserLog {

// 配置切点 及要传的参数

@Pointcut("execution(* com.ganji.demo.service.user.UserService.GetDemoUser(..)) && args(id)")

public void pointCut(int id)

{

}

// 配置连接点 方法开始执行时通知

@Before("pointCut(id)")

public void beforeLog(int id) {

System.out.println("开始执行前置通知 日志记录:"+id);

}

// 方法执行完后通知

@After("pointCut(id)")

public void afterLog(int id) {

System.out.println("开始执行后置通知 日志记录:"+id);

}

// 执行成功后通知

@AfterReturning("pointCut(id)")

public void afterReturningLog(int id) {

System.out.println("方法成功执行后通知 日志记录:"+id);

}

// 抛出异常后通知

@AfterThrowing("pointCut(id)")

public void afterThrowingLog(int id) {

System.out.println("方法抛出异常后执行通知 日志记录"+id);

}

// 环绕通知

@Around("pointCut(id)")

public Object aroundLog(ProceedingJoinPoint joinpoint,int id) {

Object result = null;

try {

System.out.println("环绕通知开始 日志记录"+id);

long start = System.currentTimeMillis();

//有返回参数 则需返回值

result = joinpoint.proceed();

long end = System.currentTimeMillis();

System.out.println("总共执行时长" + (end - start) + " 毫秒");

System.out.println("环绕通知结束 日志记录");

} catch (Throwable t) {

System.out.println("出现错误");

}

return result;

}

}

AOP切面中的同步问题

在WebLogAspect切面中,分别通过doBefore和doAfterReturning两个独立函数实现了切点头部和切点返回后执行的内容,

若我们想统计请求的处理时间,就需要在doBefore处记录时间,并在doAfterReturning处通过当前时间与开始处记录的时间计算

得到请求处理的消耗时间。

那么我们是否可以在WebLogAspect切面中定义一个成员变量来给doBefore和doAfterReturning一起访问呢?是否会有同步问题呢?

的确,直接在这里定义基本类型会有同步问题,所以我们可以引入ThreadLocal对象,像下面这样进行记录:

@Aspect

@Component

public class WebLogAspect {

private Logger logger = Logger.getLogger(getClass());

ThreadLocal http://startTime = new ThreadLocal<>();

@Pointcut("execution(public * com.juzi.web..*.*(..))")

public void webLhttp://og(){}

@Before("webLog()")

public void doBefore(JoinPoint joinPoint) throws Throwable {

startTime.set(System.currentTimeMillis());

// 省略日志记录内容

}

@AfterReturning(returning = "ret", pointcut = "webLog()")

public void doAfterReturning(Object ret) throws Throwable {

// 处理完请求,返回内容

logger.info("RESPONSE : " + ret);

logger.info("SPEND TIME : " + (System.currentTimeMillis() - startTime.get()));

}

}

AOP切面的优先级

由于通过AOP实现,程序得到了很好的解耦,但是也会带来一些问题,比如:我们可能会对Web层做多个切面,校验用户,

校验头信息等等,这个时候经常会碰到切面的处理顺序问题。

所以,我们需要定义每个切面的优先级,我们需要@Order(i)注解来标识切面的优先级。i的值越小,优先级越高。

假设我们还有一个切面是CheckNameAspect用来校验name必须为didi,我们为其设置@Order(10),

而上文中WebLogAspect设置为@Order(5),所以WebLogAspect有更高的优先级,这个时候执行顺序是这样的:

在@Before中优先执行@Order(5)的内容,再执行@Order(10)的内容

在@After和@AfterReturning中优先执行@Order(10)的内容,再执行@Order(5)的内容

所以我们可以这样子总结:

在切入点前的操作,按order的值由小到大执行

在切入点后的操作,按order的值由大到小执行

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流。


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:微信小程序之MaterialDesign
下一篇:微信小程序 http请求封装详解及实例代码
相关文章

 发表评论

暂时没有评论,来抢沙发吧~