demo
@Aspect
@Component
public class LoginLogAspect {
@Pointcut("execution(public * com.test.sg.service.impl.LoginServiceImpl.login(..))")
public void login() {
}
@AfterThrowing("login()")
public void loginFail(JoinPoint joinPoint) {
// fail
// 从切点获取方法的参数
Object[] args = joinPoint.getArgs();
}
@AfterReturning("login()")
public void loginSuccess(JoinPoint joinPoint) {
// success
// 从切点获取方法的参数
Object[] args = joinPoint.getArgs();
}
}
对com.test.sg.service.impl.LoginServiceImpl包下的login方法进行增强,
如果抛出异常,则认为登录失败,
如果没有异常,则认为登录成功
方法中可以进行具体的操作
相关注解
@Aspect
申明切面类
@Component
将类交给spring管理
@Pointcut
申明切点
参数格式:
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern)throws-pattern?)
括号中各个pattern分别表示:
- 修饰符匹配(modifier-pattern?)
- 返回值匹配(ret-type-pattern)可以为*表示任何返回值,全路径的类名等
- 类路径匹配(declaring-type-pattern?)
- 方法名匹配(name-pattern)可以指定方法名 或者 代表所有, set 代表以set开头的所有方法
- 参数匹配((param-pattern))可以指定具体的参数类型,多个参数间用“,”隔开,各个参数也可以用“”来表示匹配任意类型的参数,如(String)表示匹配一个String参数的方法;(,String) 表示匹配有两个参数的方法,第一个参数可以是任意类型,而第二个参数是String类型;可以用(…)表示零个或多个任意参数
- 异常类型匹配(throws-pattern?)
- 其中后面跟着“?”的是可选项
示例:
1)execution(* *(..))
//表示匹配所有方法
2)execution(public * com. test.service.UserService.*(..))
//表示匹配com.test.server.UserService中所有的公有方法
3)execution(* com.test.server..*.*(..))
//表示匹配com.test.server包及其子包下的所有方法
不同写法
@Pointcut("execution(public * com.test.sg.service.impl.LoginServiceImpl.login(..))")
public void login() {
}
@AfterThrowing("login()")//如果login()不在一个类中,可以使用全名
public void loginFail(JoinPoint joinPoint) {
}
等同于=====
@AfterThrowing("execution(public * com.test.sg.service.impl.LoginServiceImpl.login(..))")
public void loginFail(JoinPoint joinPoint) {
}
可以使用&&,||,!
@Pointcut("execution(public * com.test.service.impl.LoginServiceImpl.login(..)) " +
"|| execution(public * com.test.service.impl.LoginServiceImpl.test2(..))")
public void login() {
}
表示匹配com.test.service.impl.LoginServiceImpl包中的login或者test2方法
@Order
如果有需要,可以指定AOP的执行顺序,数值越小越先执行,取值范围是int的范围,默认是int的最大值
执行顺序
该段落原文链接:https://blog.csdn.net/zhanglf02/article/details/78132304
-
@Before 前置通知(Before advice) :在某连接点(JoinPoint)——核心代码(类或者方法)之前执行的通知,但这个通知不能阻止连接点前的执行。为啥不能阻止线程进入核心代码呢?因为@Before注解的方法入参不能传ProceedingJoinPoint,而只能传入JoinPoint。要知道从aop走到核心代码就是通过调用ProceedingJionPoint的proceed()方法。而JoinPoint没有这个方法。
这里牵扯区别这两个类:Proceedingjoinpoint 继承了 JoinPoint 。是在JoinPoint的基础上暴露出 proceed 这个方法。proceed很重要,这个是aop代理链执行的方法。暴露出这个方法,就能支持 aop:around 这种切面(而其他的几种切面只需要用到JoinPoint,这跟切面类型有关), 能决定是否走代理链还是走自己拦截的其他逻辑。建议看一下 JdkDynamicAopProxy的invoke方法,了解一下代理链的执行原理。这样你就能明白 proceed方法的重要性。
-
@After 后通知(After advice) :当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。
-
@AfterReturning 返回后通知(After return advice) :在某连接点正常完成后执行的通知,不包括抛出异常的情况。
-
@Around 环绕通知(Around advice) :包围一个连接点的通知,类似Web中Servlet规范中的Filter的doFilter方法。可以在方法的调用前后完成自定义的行为,也可以选择不执行。这时aop的最重要的,最常用的注解。用这个注解的方法入参传的是ProceedingJionPoint pjp,可以决定当前线程能否进入核心方法中——通过调用pjp.proceed();
-
@AfterThrowing 抛出异常后通知(After throwing advice) : 在方法抛出异常退出时执行的通知。
针对一个方法只被一个aspect类拦截时,aspect类内部的 advice 将按照以下的顺序进行执行情况如下:
正常返回就走@AfterReturn,有异常就走@AfterThrowing
JoinPoint和ProceedingJoinPoint
AspectJ使用org.aspectj.lang.JoinPoint接口表示目标类连接点对象,如果是环绕增强时,使用org.aspectj.lang.ProceedingJoinPoint表示连接点对象,该类是JoinPoint的子接口。任何一个增强方法都可以通过将第一个入参声明为JoinPoint访问到连接点上下文的信息。我们先来了解一下这两个接口的主要方法:
1)JoinPoint
java.lang.Object[] getArgs():获取连接点方法运行时的入参列表;
Signature getSignature() :获取连接点的方法签名对象;
java.lang.Object getTarget() :获取连接点所在的目标对象;
java.lang.Object getThis() :获取代理对象本身;
2)ProceedingJoinPoint
ProceedingJoinPoint继承JoinPoint子接口,它新增了两个用于执行连接点方法的方法:
java.lang.Object proceed() throws java.lang.Throwable:通过反射执行目标对象的连接点处的方法;
java.lang.Object proceed(java.lang.Object[] args) throws java.lang.Throwable:通过反射执行目标对象连接点处的方法,不过使用新的入参替换原来的入参。