概念
AOP (Aspect Oriented Programming)是一种面向切面的编程的思想.
为什么用使用AOP
在写代码的过程中,如果把业务的核心代码和其他的一些代码堆放在一起,会造成代码的耦合度很高,不利于为维护,而导致牵一发动全身.如果使用面向对象OOP的思想,把每个业务逻辑中的公共方法都抽取出来作为一个公共类公共方法,需要就调用,但仍然还是需要去引用这个类然后去调用这个方法.为了解决重复代码,维护麻烦的问题,使用AOP动态的,不影响原先代码前提下执行的.
什么是切面(Aspect)
切面是是通知和切点的结合,一个切面就可以规定了在什么时候什么位置做什么事,例如在核心程序类执行前调用某个切面的通知
什么是切点(Pointcut)
切点存在于切面里面,我们需要定义这个不同的切点在哪些连接点上执行通知,如果说通知规定了切面是在什么时候,干了什么事的话,那么切点就是规定在哪个位置下去去切入这个切面
什么是通知
在AOP中,通知分为以下几种通知
- 前置通知,Begore
- 后置通知,After(在目标方法完成之后调用通知,此时不会关心方法的输出是什么)
- 返回通知,After-returning(在目标方法成功执行之后调用通知)
- 异常通知,After-throwing(抛出异常的通知)
- 环绕通知,Around,这是个很强大的通知,使用try...catch能够代替前面四种通知
什么是连接点
连接点是在应用执行过程中能够插入切面的一个点,简单来说就是调用切点的那个方法.一般连接点就是核心业务.
切入点表达式:通常在项目中的用法
execution(* *..*Service.*(..))
可以按照顺序读一下
execution是必须的
public修饰符可以省略,execution(public * *..*Service.*(..))
修饰符省略的情况下返回值不能省略execution(*int *..*Service.*(..))
这种就是错误的
*.. 表示多重包下
*Service 表示Service结尾的类名
*表示所有方法
(..)表示所参数列表任意
切入点表达式的使用
一般使用统一管理切入点的方式
参考代码
@Aspect
@Component
@Order(3)
public class LogAspect {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Pointcut("execution(* *..*Service.*(..))")
public void pointCutDecoration(){
}
@Before("com.ycy.aop.aspect.PointCut.pointCutDecoration()")
public void printLogBeforeCoreOperation(JoinPoint joinPoint){
//通过spring传入的JoinPoint对象获取方法名,方法实参,返回值,异常信息
String targetMethodName = joinPoint.getSignature().getName();
Object[] args = joinPoint.getArgs();
//把数组转成集合方便打印,调用Arrays的静态方法
List<Object> argumentList = Arrays.asList(args);
logger.debug("[AOP前置通知--------]"+"[目标方法:]"+targetMethodName+"[方法实参:]"+argumentList);
}
@AfterReturning(value = "com.ycy.aop.aspect.PointCut.pointCutDecoration()",returning ="returnValue" )
public void printLogAfterCoreOperation(JoinPoint joinPoint,Object returnValue){
logger.debug("[AOP返回通知--------]"+"[返回值:]"+returnValue);
}
@AfterThrowing(value = "com.ycy.aop.aspect.PointCut.pointCutDecoration()",throwing = "throwable")
public void printLogAfterCoreOperationFailed(JoinPoint joinPoint,Throwable throwable){
//从传入的throwable对象的原因
Throwable cause = throwable.getCause();
//循环查找原因,直到cause为null
while (cause!=null){
throwable =cause;
//如果这个cause为null了,那么异常信息就停留在throwable了
cause = throwable.getCause();
}
String throwableName = throwable.getClass().getName();
String message = throwable.getMessage();
String s = throwable.toString();
logger.debug("[AOP异常通知--------]"+"[异常类型:]"+throwableName+"[异常信息是:]"+message);
logger.debug("[AOP异常通知--------]"+"[异常信息是:]"+s);
}
@After("com.ycy.aop.aspect.PointCut.pointCutDecoration()")
public void printLogAfterCoreOperationFinished(JoinPoint joinPoint){
String name = joinPoint.getSignature().getName();
logger.debug("[AOP后置通知--------]"+"[目标方法;]"+name);
}
}
打印结果
@Order注解的使用
在开发过程中,可能会有多个切面的存在,我们需要规定切面的优先级
@Order(较小的数):优先级高
@Order(较大的数):优先级低
经典的应用场景就是缓存切面和事务切面,如果事务的切面优先于缓存的切面的话,会让所有的请求都去开启事务查询数据库,频繁操作数据库这是非常不合理的,所以应该把缓存切面的优先级高于事务切面,一旦有请求进来,应该先查询缓存,一旦缓存有数据就直接返回,不去查询数据库,如果缓存没有该数据才进数据库,并且查到后也应该再把数据置于缓存.
...
先到这