1. AOP概述
    AOP即面向切面编程, 是对OOP的补充,OOP从纵向方向切入对象,比如接口,继承,抽象,多态等,但是它无法满足我们日常的一些重复代码的一个抽取,比如我们要实现一个日志功能,但是日志和业务是不相干的,如果在每个业务中添加日志输出,那我们不得不重新写很多重复的代码放进业务层代码中,这样很不友好还很费精力和时间,但是AOP很好解决了这些问题,它可以从横向切入对象,将多个对象中重复的代码抽取出来作为一个切面,用这个切面从三个层面(前置,环绕,后置)去代理这些业务层代码,然后就可以对这些重复代码去填充代理对象,这样就减少了代码的重复,也将这些和业务没有关系的代码从业务层中抽离出来,提高了业务层代码的可读性。
  2. 关于AOP几个关键点
  • Aspect: Aspect 声明类似于 Java 中的类声明,在 Aspect 中会包含着一些 Pointcut 以及相应的 Advice。
  • Joint point(被切入点):是指具体要实现前面所说的例如打印日志,数据库事务管理的被切入的点。也就是通过 AOP 将切面功能动态加入进去的程序位置。在 Spring AOP 里面这个指的都是某个方法
  • Pointcut(切点):。用来指明如何通过规则匹配 Joint point。这个规则是一个表达式。在 Spring 中,默认使用的是 AspectJ 的 pointcut 表达式语言
  • Advice(通知):指明在一个切入点的不同位置上采取的动作。例如对于一个数据库访问事务管理来说,在进入方法后要开启事务,在方法结束前要提交事务,在发生错误的时候要回滚事务。这属于三个不同的 Advice,要分别进行实现。Advice 通常和具体的 Pointcut 关联在一起。
  • AOP proxy(AOP 代理):用来实现将 Advice 功能动态加入到 Pointcut 的方法。在 Spring 的 AOP 中采用动态代理和 CGLIB 代理的方式来实现。而 AspectJ 则采用了特定编译器侵入字节码的方式来实现。
  • Target(目标对象):织入 Advice 的目标对象.。
  • Weaving(织入):将 Aspect 和其他对象连接起来, 并创建 Adviced object 的过程
  1. @poIncut 切点
    这个注解包含两部分,PointCut表达式和PointCut签名。

execution

对于匹配方法执行连接点,这是您在使用Spring AOP时将使用的主要切入点设计器

within

将匹配限制为特定类型中的连接点(当使用Spring AOP时,只需执行在匹配类型中声明的方法)

this

将匹配限制为连接点(使用Spring AOP时方法的执行),其中bean引用(Spring AOP代理)是给定类型的实例

target

限制与连接点的匹配(使用Spring AOP时方法的执行),其中目标对象(代理的应用程序对象)是给定类型的实例

@target

将匹配限制为连接点(使用Spring AOP时方法的执行),其中执行对象的类具有给定类型的注释

@args

限制连接点的匹配(使用Spring AOP时方法的执行),其中传递的实际参数的运行时类型具有给定类型的注释

@within

匹配具有给定注释的类型中的连接点的限制(使用Spring AOP时,使用给定注释在类型中声明的方法的执行)

@annotation

将匹配限制为连接点的主题(在Spring AOP中执行的方法)具有给定注释的连接点

  1. 通知类型

前置通知

方法执行之前执行响应AOP代理的方法

@Before()

环绕通知

方法执行过程中执行响应AOP代理的方法

@Around() 注意环绕通知的参数是ProceedingJoinPoint JoinPoint的子类

后置通知

方法执行过程中执行响应AOP代理的方法(注意后置通知又分为成功后置通知即方法成功执行完成执行响应的AOP后置成功方法,和异常后置通知即方法在执行过程中出现异常执行AOP响应的异常通知方法)

@After() 可以细分为 @AfterThrowing和AfterReturning

  1. demo
    aspect类
package com.skindow.logAop;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.stream.Collectors;

/**
 * Created by Administrator on 2019/8/8.
 * */
@Component
@Aspect
@Slf4j
public class LogAspect {
    private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    @Pointcut("execution(public * com.skindow.controller..*.*(..))")//切入点描述 这个是controller包的切入点
    public void webLog() {//签名,可以理解成这个切入点的一个名称
    }
    @Around("webLog()") //环绕通知,执行webLog方法
    public Object doBefore(ProceedingJoinPoint joinPoint) throws Throwable {
        Date startDate = new Date();
        //返回目标方法的签名
        Signature signature = joinPoint.getSignature();
        //返回目标方法所有的参数
        Object[] args = joinPoint.getArgs();
        log.info("signature.toString()" + signature.toString());
        log.info("{} 调用时间:{}", signature, sdf.format(startDate));
        long start = System.currentTimeMillis();
        //用改变后的参数执行目标方法
        Object object = joinPoint.proceed();
        //目标执行完成并记录结束时间
        long end = System.currentTimeMillis();
        String time = this.formatExecuteTime(end - start);
        log.info("{} 执行时间:{}", signature.toString(), time);
        (new Thread(() -> {
            log.info("{} 方法入参{}", signature.toString(),new ArrayList<>(Arrays.asList(args)).stream().map(a -> a.toString()).collect(Collectors.joining(",")));
        })).start();
        return object;
    }

    /**通过毫秒计算出时分秒并输出xxmxxsxxms的格式字符
     * @param executeTime
     * @return
     */
    private String formatExecuteTime(long executeTime) {
        long min = executeTime % 3600000L / 60000L;
        long sec = executeTime % 60000L / 1000L;
        long msec = executeTime % 10000L;
        StringBuilder sb = new StringBuilder();
        if(min > 0L) {
            sb.append(min).append("m ");
        }
        if(sec > 0L) {
            sb.append(sec).append("s ");
        }
        sb.append(msec).append("ms");
        return sb.toString();
    }
}

测试结果如下:

android studio 日志打印太多断节 aop实现日志打印_AOP


android studio 日志打印太多断节 aop实现日志打印_连接点_02


OK 成功了