面向切面编程AOP
AOP:【动态代理】
-
指在程序运行期间动态的将某段代码切入到指定方法指定位置进行运行的编程方式。
用处:对数据的统一处理,日志收集,异常信息等。
通知方法:
-
前置通知(@Before):在目标方法(div)运行之前运行
-
后置通知(@After):在目标方法(div)运行结束之后运行(无论方法正常结束还是异常结束)
-
返回通知(@AfterReturning):在目标方法(div)正常返回之后运行
-
异常通知(@AfterThrowing):在目标方法(div)出现异常以后运行
-
环绕通知(@Around):动态代理,手动推进目标方法运行(joinPoint.procced())
上个项目涉及到,对返回前端参数做反转义处理,差点就用aop了。
springboot对AOP的集成很简单。
首先导包:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
1.写一个目标类,就要切入的类。
/**
* @author
*/
@RestController
@RequestMapping("/aop")
public class MyAopTestController {
@RequestMapping("/mathcalculate")
public int MathCalculate(int i,int j){
System.out.println("进入计算函数方法………………");
return i/j;
}
}
②写一个切面类
@Component
@Aspect
public class MathAspect {
//true为公平锁,false或缺省为非公平锁
private static Lock lock = new ReentrantLock(true);
/**
* 定义切入点,切入点为com.example.demo.chinaunicom.aopcontroller中的所有函数
* 通过@Pointcut注解声明频繁使用的切点表达式
* 第一个*表示返回值,可以为int等,*表示任意返回类型,第二个*表示该类下的任意方法都过切面,(..)表示任意参数列表
*/
@Pointcut("execution(* com.example.demo.chinaunicom.aopcon.MyAopTestController.*(..))")
public void pointcut() {
}
/**
* @param joinPoint
* @Before 在目标方法之前切入;切入点表达式(指定在哪个方法切入)
*/
@Before("pointcut()")
public void beforeExcute(JoinPoint joinPoint) {
System.out.println("我是执行前,方法名是:" + joinPoint.getSignature().getName() + "参数列表是:" + Arrays.asList(joinPoint.getArgs()));
}
/**
* @After 在目标方法之后切入;切入点表达式(指定在哪个方法切入) 可以写上面的切入点也可以写全路径
*/
@After("com.example.demo.chinaunicom.utils.MathAspect.pointcut()")
public void afterExcute(JoinPoint joinPoint) {
System.out.println("执行方法后:" + joinPoint.getSignature().getName() + "执行参数:" + Arrays.asList(joinPoint.getArgs()));
}
/**
* 目标方法执行之后的返回值
* JoinPoint一定要出现在参数表的第一位,放在其他位置无效
*
* @param joinPoint
* @param obj
*/
@AfterReturning(value = "pointcut()", returning = "obj")
public void afterReturning(JoinPoint joinPoint, Object obj) {
System.out.println("请求参数名是:" + joinPoint.getSignature().getName() + "请求参数是:" + Arrays.asList(joinPoint.getArgs()) + ",返回的参数是:" + obj);
}
/**
* 出现异常抛出异常信息
*
* @param joinPoint
* @param e
*/
@AfterThrowing(value = "pointcut()", throwing = "e")
public void afterThrowing(JoinPoint joinPoint, Throwable e) {
System.out.println("请求参数名是:" + joinPoint.getSignature().getName() + "参数列表是:" + Arrays.asList(joinPoint.getArgs()) + "出现异常,异常信息为:" + e);
}
/**
*环绕:在目标类执行前后都会执行这个方法,执行前,@around会执行到proceedingJoinPoint.proceed();
*然后线程阻塞
*在目标方法执行完毕、后又接着proceedingJoinPoint.proceed()往下执行。
*他应该保持线程安全
**/
@Around("pointcut()")
public Object aroundMethod(ProceedingJoinPoint proceedingJoinPoint) {
System.out.println("我是环绕……………………………………………………");
Object result = null;
lock.lock();
try {
result = proceedingJoinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
} finally {
lock.unlock();
}
System.out.println("result:" + result);
return result;
}
}
执行顺序可以这么理解:(最后一步可能是@AfterReturning或者@AfterThrowing)
注:①JoinPoint一定要出现在参数表的第一位,放在其他位置无效
②获取请求参数除了用Arrays.asList(joinPoint.getArgs()) 还可以用上一篇提到的采用上下文的模式去获取。
③Arrays.asList()用来 将数组对象(map也可以)转为list集合对象的。
但是!!但是!! 创建之后的list 不支持 add(),remove()操作,只支持遍历读操作!慎用