aop


AOP 基本使用教程

什么是AOP?

AOP简介

AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。

为什么使用AOP编程范式?

分离功能性需求和非功能性需求

集中处理某一关注点

侵入性少,增强代码可读性及可维护性

AOP应用场景

权限控制、缓存控制、事务控制、分布式追踪、异常处理等

举个栗子

如果你要在Service层的某些特定方法需加上权限验证,使用OOP思想的话只能在方法内部添加验证身份的代码,例如

public void insert() {
  checkUserAdmin.check();    //加入权限验证方法
  repository.insert();        //调用dao层插入数据库一条记录
}

这样看起来功能是实现了,但如果service层有很多insert和delete方法呢?这样插入代码的方式不易于我们去统一管理,且修改了原代码,具有侵入性。

那么使用了AOP之后呢?你可以建一个切面类,对要进行权限验证的方法进行切入。

即在程序运行时,动态地将代码切入到类的指定方法或位置上的思想,就是面向切面编程。

AOP 术语

要想使用面向对象编程的思想,首先要了解几个专有名词

• Target:目标类,即需要被代理的类。例如:UserService

• Joinpoint(连接点):所谓连接点是指那些可能被拦截到的方法。例如:所有的方法

• PointCut 切入点:已经被增强的连接点。例如:addUser()

• Advice 通知/增强,增强代码。例如:after、before

• Weaving(织入):是指把增强advice应用到目标对象target来创建新的代理对象proxy的过程.

• Proxy 代理类

• Aspect(切面): 是切入点pointcut和通知advice的结合

Advice-五种增强方式

例如在执行某个特定方法的时候,我们可以选择不同的增强方式(如前置通知/增强,在方法运行前执行),达到我们织入后的不同效果。

  1. 前置通知:在我们执行目标方法之前运行(@Before)

  2. 后置通知:在我们目标方法运行结束之后 ,不管有没有异常(@After)

  3. 返回通知:在我们的目标方法正常返回值后运行(@AfterReturning)

  4. 异常通知:在我们的目标方法出现异常后运行(@AfterThrowing)

  5. 环绕通知:动态代理, 需要手动执行joinPoint.procced()(其实就是执行我们的目标方法执行之前相当于前置通知, 执行之后就相当于我们后置通知(@Around)

切面表达式

excution表达式

execution(
    修饰符pattern
    返回值pattern
    描述包名
    方法名(参数)
    方法抛出异常pattern
)

实例

通过注解打印每个方法花费的时间

注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LogCostTime {

}

AOP实现

@Aspect
@Component
public class LogCostTimeAop {
    final static Logger LOG = LoggerFactory.getLogger(LogCostTimeAop.class);

    @Value("${log.cost.time.enable:true}")
    private boolean logCostTimeEnable;


    @Pointcut("@annotation(com.york.common.annotation.LogCostTime)")
    public void costTimePointCut(){

    }

    @Around("costTimePointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        //判断开关
        if(!logCostTimeEnable){
           return point.proceed();
        }
        //记录开始时间
        long startTime = System.currentTimeMillis();
        //执行方法
        Object result = point.proceed();
        //执行时长(毫秒)
        long time = System.currentTimeMillis() - startTime;
        logCostTime(point, time);
        return result;
    }

    private void logCostTime(ProceedingJoinPoint joinPoint, long time) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        String className = joinPoint.getTarget().getClass().getName();
        LOG.info("{}.{} cost:{}ms", className, signature.getName(), time);
    }

}

配置文件,开启与否

log.cost.time.enable = true

环绕打印日志

@Aspect
@Component
@Slf4j
public class LogAop {
    @Before(value = "execution(public * com.jktest.controller.*.*(..))")
    public void before(JoinPoint joinPoint) {
        String className = joinPoint.getTarget().getClass().getName();
        String classMethod = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        StringBuilder params = new StringBuilder();
        for (Object arg : args) {
            params.append(arg).append(" ");
        }
        log.info("{} # {} # {}", className, classMethod, params);
    }

    @Around(value = "execution(public * com.jktest.controller.*.*(..))")
    public Object catchException(ProceedingJoinPoint joinPoint) {
        try {
            return joinPoint.proceed();
        } catch (Throwable throwable) {
            String className = joinPoint.getTarget().getClass().getName();
            String classMethod = joinPoint.getSignature().getName();
            log.info("在{}类中{} 方法 发生了错误", className, classMethod);
            HashMap<String, Object> ret = new HashMap<>(2);
            ret.put("code", "4xx");
            ret.put("msg", "异常");
            return ret;
        }
    }

    @AfterReturning(value = "execution(public * com.jktest.controller.*.*(..))", returning = "returnVal")
    public void afterReturn(JoinPoint joinPoint, Object returnVal) {
        String className = joinPoint.getTarget().getClass().getName();
        String classMethod = joinPoint.getSignature().getName();
        log.info("{} # {} # {}", className, classMethod, JSON.toJSON(returnVal));
    }
}

文章作者: anlen123
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 anlen123 !
 本篇
aop aop
AOP 基本使用教程什么是AOP?AOP简介 AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。 为什么使用AOP编程范式? 分离功能
2021-03-26 anlen123
下一篇 
Java中文档的转换问题 Java中文档的转换问题
Java中文档的转换问题我的经验来看,转换为word是不会出现乱码问题的 1. 把文件变成响应流//把zip文件流变成响应流 OutputStream os = null; try { response.setHeader("Con
2021-03-16 anlen123
  目录