一、AOP基础
导入aspect包
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
创建切面类设置
@Configuration
@ComponentScan("com.lijun")
@EnableAspectJAutoProxy //开启注解开发AOP功能
public class SpringConfig {
}
@Component
@Aspect //设置当前类为切面类类
public class MyAdvice {
//设置切入点,要求配置在方法上方
@Pointcut("execution(void com.lijun.dao.BookDao.update())")
private void pt(){}
//设置在切入点pt()的前面运行当前操作(前置通知)
@Before("pt()")
public void method(){
System.out.println(System.currentTimeMillis());
}
}
切入点表达式
@Pointcut("execution(void com.lijun.dao.BookDao.update())")
@Pointcut("execution(void com.lijun.dao.impl.BookDaoImpl.update())")
@Pointcut("execution(* com.lijun.dao.impl.BookDaoImpl.update(*))")
@Pointcut("execution(void com.*.*.*.update())")
@Pointcut("execution(* *..*(..))")
@Pointcut("execution(* *..*e(..))")
@Pointcut("execution(void com..*())")
@Pointcut("execution(* com.lijun.*.*Service.find*(..))")
通知类型
public class MyAdvice {
@Pointcut("execution(void com.lijun.dao.BookDao.update())")
private void pt(){}
@Pointcut("execution(int com.lijun.dao.BookDao.select())")
private void pt2(){}
//@Before:前置通知,在原始方法运行之前执行
@Before("pt()")
public void before() {
System.out.println("before advice ...");
}
//@After:后置通知,在原始方法运行之后执行
@After("pt2()")
public void after() {
System.out.println("after advice ...");
}
//@Around:环绕通知,在原始方法运行的前后执行
@Around("pt2()")
public Object aroundSelect(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("around before advice ...");
//表示对原始操作的调用
Integer ret = (Integer) pjp.proceed();
System.out.println("around after advice ...");
return ret;
}
//@AfterReturning:返回后通知,在原始方法执行完毕后运行,且原始方法执行过程中未出现异常现象
@AfterReturning("pt2()")
public void afterReturning() {
System.out.println("afterReturning advice ...");
}
//@AfterThrowing:抛出异常后通知,在原始方法执行过程中出现异常后运行
@AfterThrowing("pt2()")
public void afterThrowing() {
System.out.println("afterThrowing advice ...");
}
}
二、案例实现
1、实现监听业务层运行时间
@Component
@Aspect
public class ProjectAdvice {
//匹配业务层的所有方法
@Pointcut("execution(* com.lijun.service.*Service.*(..))")
private void servicePt(){}
//设置环绕通知,在原始操作的运行前后记录执行时间
@Around("ProjectAdvice.servicePt()")
public void runSpeed(ProceedingJoinPoint pjp) throws Throwable {
//获取执行的签名对象
Signature signature = pjp.getSignature();
String className = signature.getDeclaringTypeName();
String methodName = signature.getName();
long start = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
pjp.proceed();
}
long end = System.currentTimeMillis();
System.out.println("万次执行:"+ className+"."+methodName+"---->" +(end-start) + "ms");
}
}
2、通知获取数据(参数、返回值、异常)
@Component
@Aspect
public class MyAdvice {
@Pointcut("execution(* com.itheima.dao.BookDao.findName(..))")
private void pt(){}
//JoinPoint:用于描述切入点的对象,必须配置成通知方法中的第一个参数,可用于获取原始方法调用的参数
@Before("pt()")
public void before(JoinPoint jp) {
Object[] args = jp.getArgs();
System.out.println(Arrays.toString(args));
System.out.println("before advice ..." );
}
@After("pt()")
public void after(JoinPoint jp) {
Object[] args = jp.getArgs();
System.out.println(Arrays.toString(args));
System.out.println("after advice ...");
}
//ProceedingJoinPoint:专用于环绕通知,是JoinPoint子类,可以实现对原始方法的调用
@Around("pt()")
public Object around(ProceedingJoinPoint pjp) {
Object[] args = pjp.getArgs();
System.out.println(Arrays.toString(args));
args[0] = 666;
Object ret = null;
try {
ret = pjp.proceed(args);
} catch (Throwable t) {
t.printStackTrace();
}
return ret;
}
//设置返回后通知获取原始方法的返回值,要求returning属性值必须与方法形参名相同
@AfterReturning(value = "pt()",returning = "ret")
public void afterReturning(JoinPoint jp,String ret) {
System.out.println("afterReturning advice ..."+ret);
}
//设置抛出异常后通知获取原始方法运行时抛出的异常对象,要求throwing属性值必须与方法形参名相同
@AfterThrowing(value = "pt()",throwing = "t")
public void afterThrowing(Throwable t) {
System.out.println("afterThrowing advice ..."+t);
}
}