1. 基于注解的Demo:
package demo.aop;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
/**
* @author xiesq
* @version 1.0
* @date 2021/3/22 23:45
*/
@Slf4j
@Component
@Aspect
public class LogAspect {
/**
* 切点 - 目标方法的匹配规则
* public void 包名.类名.方法名(..)
* *表示匹配所有字符串
* ..表示子孙包下 或 任意个参数的method
* 取反:!execution()
* 多个匹配规则可用 ||、&&、or、and连接:execution() and execution()
*/
@Pointcut("execution(public * demo.aop.service.UserServiceImpl.*(..))")
public void pointCut(){
//示例:
//1.方法匹配:拦截目标类下的任意方法
// execution(* demo.aop.service.UserServiceImpl.*(..))
//拦截目标类下的任意public方法
// execution(public * demo.aop.service.UserServiceImpl.*(..))
//拦截目标类下的任意public,且以add开头的方法
// execution(public * demo.aop.service.UserServiceImpl.add*(..))
//拦截目标类下的任意public,且返回类型为String的方法
// execution(public String demo.aop.service.UserServiceImpl.*(..))
//拦截目标类下的任意public,且返回类型为String的,delete方法
// execution(public String demo.aop.service.UserServiceImpl.delete())
//2.包匹配:拦截指定包下的所有类下的所有方法
// execution(* demo.aop.service.*.*(..)) demo.aop.service.所有类.所有方法(任意参数)
// execution(* demo.aop.*.*.*(..)) demo.aop.所有子包.所有类.所有方法(任意参数)
// execution(* demo.aop..*.*(..)) demo.aop.所有子孙包.所有类.所有方法(任意参数)
//3.子级匹配:
// execution(* demo.aop.service.BaseService+.*(..)) BaseService的子类.所有方法(任意参数)
//4.注解匹配:
// @annotation(com.demo.MyAnnotation)
}
/**
* 前置增强
* joinPoint - 切点:目标方法
*/
@Before(value = "pointCut()")
public void before(JoinPoint joinPoint){
//joinPoint.getTarget(); 获取被代理对象
//joinPoint.getThis(); 获取代理对象
//joinPoint.getSignature(); 获取方法签名(方法名、方法所属的类名等)
//joinPoint.getArgs(); 获取方法的参数
log.info("{}方法执行前", joinPoint.getSignature().getName());
}
/**
* 后置返回增强
* 可通过returning属性获取到方法返回值
*/
@AfterReturning(value = "pointCut()", returning = "result")
public void afterReturning(Object result){
log.info("方法执行后-afterReturning,返回值为:{}", result);
}
/**
* 后置异常增强
*/
@AfterThrowing(value = "pointCut()", throwing = "e")
public void afterThrowing(Exception e){
log.info("方法执行后-afterThrowing,exception:{}", e.getMessage());
}
/**
* 后置finally增强
*/
@After("pointCut()")
public void after(){
log.info("方法执行后");
}
/**
* 环绕增强 - 通过jp.proceed()手动去执行目标方法,可在执行前、后、发生异常时编写增强代码
* 环绕增强相当于包含了上面的所有增强功能,这里看实际需要,选择使用
*/
@Around("pointCut()")
public Object around(ProceedingJoinPoint jp) {
String methodName = jp.getSignature().getName();
log.info("环绕增强:{}方法执行前", methodName);
Object res = null;
try {
//调用执行目标方法
res = jp.proceed();
} catch (Throwable throwable) {
log.error("环绕增强:执行方法时出错了:{}", throwable.getMessage());
//此处 如果抛出异常将触发@AfterThrowing,如果不抛出异常则触发@AfterReturning
// throwable.printStackTrace();
throw new RuntimeException(throwable);
}
log.info("环绕增强:{}方法执行后", methodName);
return res;
}
}
2. 代理不生效的原因:
- 动态生成的代理类ProxyServiceA是内部持有的一个被代理对象:
public class ProxyServiceA implements ServiceA{ //内部持有一个被代理对象的实例 private ServiceA serviceA; public void methodA(){ //...前置增强 serviceA.methodA(); //...后置增强 } public void methodB(){ //...前置增强 serviceA.methodB(); //...后置增强 } }
- 当我们调用
proxyServiceA.methodA()
时,如果ServiceA#methodA()内部调用了this.methodB()的话,就相当于执行的是serviceA.methodB()
而不是proxyServiceA.methodB()
,那么methodB的增强逻辑将不会生效!