1、基础

1.1、配置applicationContext.xml文件

<!--AOP配置-->
<bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator" />
<bean id="aspectBean" class="com.fht.aop.ApiMonitor" />

1.2、添加依赖

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <!--注意与spring版本一直致-->
    <version>5.2.2.RELEASE</version>
</dependency>

1.3、模板一

@Aspect
public class ApiMonitor {
    private static Logger logger = LogManager.getLogger(ApiMonitor.class);

    @Pointcut("execution(* com.fht.service.*.*(..))")
    private void pointCutMethod() {}

    //声明前置通知
    @Before("pointCutMethod()")
    public void doBefore(JoinPoint point) {
        logger.info("@Before:模拟权限检查...");
        logger.info("@Before:目标方法为:" +
                point.getSignature().getDeclaringTypeName() +
                "." + point.getSignature().getName());
        logger.info("@Before:参数为:" + Arrays.toString(point.getArgs()));
        logger.info("@Before:被织入的目标对象为:" + point.getTarget());
    }

    //声明后置通知
    @AfterReturning(pointcut = "pointCutMethod()", returning = "returnValue")
    public void doAfterReturning(JoinPoint point, Object returnValue) {
        logger.info("@AfterReturning:模拟日志记录功能...");
        logger.info("@AfterReturning:目标方法为:" +
                point.getSignature().getDeclaringTypeName() +
                "." + point.getSignature().getName());
        logger.info("@AfterReturning:参数为:" +
                Arrays.toString(point.getArgs()));
        logger.info("@AfterReturning:返回值为:" + returnValue);
        logger.info("@AfterReturning:被织入的目标对象为:" + point.getTarget());
    }

    //声明例外通知
    @AfterThrowing(pointcut = "pointCutMethod()", throwing = "e")
    public void doAfterThrowing(Exception e) {
        logger.info("例外通知");
        logger.info(e.getMessage());
    }

    //声明最终通知
    @After("pointCutMethod()")
    public void doAfter(JoinPoint point) {
        logger.info("@After:模拟释放资源...");
        logger.info("@After:目标方法为:" +
                point.getSignature().getDeclaringTypeName() +
                "." + point.getSignature().getName());
        logger.info("@After:参数为:" + Arrays.toString(point.getArgs()));
        logger.info("@After:被织入的目标对象为:" + point.getTarget());
    }

    //声明环绕通知
    @Around("pointCutMethod()")
    public Object doAround(ProceedingJoinPoint point) throws Throwable {
        logger.info("@Around:执行目标方法之前...");
        //访问目标方法的参数:
        Object[] args = point.getArgs();
        if (args != null && args.length > 0 && args[0].getClass() == String.class) {
            args[0] = "改变后的参数1";
        }
        //用改变后的参数执行目标方法
        Object returnValue = point.proceed(args);
        logger.info("@Around:执行目标方法之后...");
        logger.info("@Around:被织入的目标对象为:" + point.getTarget());
        return "原返回值:" + returnValue + ",这是返回结果的后缀";
    }
}

1.4、模板二

import java.text.SimpleDateFormat;  
import java.util.HashMap;  
import java.util.Map;  
  
import javax.servlet.http.HttpServletRequest;  
  
import org.aspectj.lang.JoinPoint;  
import org.aspectj.lang.ProceedingJoinPoint;  
import org.aspectj.lang.annotation.After;  
import org.aspectj.lang.annotation.Around;  
import org.aspectj.lang.annotation.Aspect;  
import org.aspectj.lang.annotation.Before;  
import org.slf4j.Logger;  
import org.slf4j.LoggerFactory;  
import org.springframework.web.context.request.RequestAttributes;  
import org.springframework.web.context.request.RequestContextHolder;  
import org.springframework.web.context.request.ServletRequestAttributes;  
  
import com.google.gson.Gson;  
  
/** 
 *  
* @ClassName: LogAspect  
* @Description: 日志记录AOP实现  
* @author shaojian.yu 
* @date 2014年11月3日 下午1:51:59  
* 
 */  
@Aspect  
public class LogAspect {  
    private final Logger logger = LoggerFactory.getLogger(this.getClass());  
  
    private String requestPath = null ; // 请求地址  
    private String userName = null ; // 用户名  
    private Map<?,?> inputParamMap = null ; // 传入参数  
    private Map<String, Object> outputParamMap = null; // 存放输出结果  
    private long startTimeMillis = 0; // 开始时间  
    private long endTimeMillis = 0; // 结束时间  
  
    /** 
     *  
     * @Title:doBeforeInServiceLayer 
     * @Description: 方法调用前触发  
     *  记录开始时间  
     * @author shaojian.yu  
     * @date 2014年11月2日 下午4:45:53 
     * @param joinPoint 
     */  
    @Before("execution(* com.yusj.controller..*.*(..))")  
    public void doBeforeInServiceLayer(JoinPoint joinPoint) {  
        startTimeMillis = System.currentTimeMillis(); // 记录方法开始执行的时间  
    }  
  
    /** 
     *  
     * @Title:doAfterInServiceLayer 
     * @Description: 方法调用后触发  
     *  记录结束时间 
     * @author shaojian.yu  
     * @date 2014年11月2日 下午4:46:21 
     * @param joinPoint 
     */  
    @After("execution(* com.yusj.controller..*.*(..))")  
    public void doAfterInServiceLayer(JoinPoint joinPoint) {  
        endTimeMillis = System.currentTimeMillis(); // 记录方法执行完成的时间  
        this.printOptLog();  
    }  
  
    /** 
     *  
     * @Title:doAround 
     * @Description: 环绕触发  
     * @author shaojian.yu  
     * @date 2014年11月3日 下午1:58:45 
     * @param pjp 
     * @return 
     * @throws Throwable 
     */  
    @Around("execution(* com.yusj.controller..*.*(..))")  
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {  
        /** 
         * 1.获取request信息 
         * 2.根据request获取session 
         * 3.从session中取出登录用户信息 
         */  
        RequestAttributes ra = RequestContextHolder.getRequestAttributes();  
        ServletRequestAttributes sra = (ServletRequestAttributes)ra;  
        HttpServletRequest request = sra.getRequest();  
        // 从session中获取用户信息  
        String loginInfo = (String) session.getAttribute("username");  
        if(loginInfo != null && !"".equals(loginInfo)){  
            userName = operLoginModel.getLogin_Name();  
        }else{  
            userName = "用户未登录" ;  
        }  
        // 获取输入参数  
        inputParamMap = request.getParameterMap();  
        // 获取请求地址  
        requestPath = request.getRequestURI();  
        
        // 执行完方法的返回值:调用proceed()方法,就会触发切入点方法执行  
        outputParamMap = new HashMap<String, Object>();  
        Object result = pjp.proceed();// result的值就是被拦截方法的返回值  
        outputParamMap.put("result", result);  
        
        return result;  
    }  
  
    /** 
     *  
     * @Title:printOptLog 
     * @Description: 输出日志  
     * @author shaojian.yu  
     * @date 2014年11月2日 下午4:47:09 
     */  
    private void printOptLog() {  
        Gson gson = new Gson(); // 需要用到google的gson解析包  
        String optTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(startTimeMillis);  
        logger.info("\n user:"+userName  
                +"  url:"+requestPath+"; op_time:" + optTime + " pro_time:" + (endTimeMillis - startTimeMillis) + "ms ;"  
                +" param:"+gson.toJson(inputParamMap)+";"+"\n result:"+gson.toJson(outputParamMap));  
    }  
}  

1.5、模板三

https://github.com/MusicXi/demo-aop-log

1.6、ELK:日志分析

2、自定义日志+性能检测版

2.1、执行顺序测试

@Aspect
public class ServiceMonitor {
    private static final Logger logger = LogManager.getLogger(ServiceMonitor.class);
    private static final String BASE_PRE_STR = "======> ";
    private String methodNameCache = "";
    private String okMsg = "";
    private String errMsg = "";

    @Pointcut("execution(* com.fht.service.*.*(..))")
    private void pointCutMethod() {}

    // 声明前置通知
    @Before("pointCutMethod()")
    public void doBefore(JoinPoint joinPoint) {
        System.out.println("@Before");
    }

    // 声明最终通知
    @After("pointCutMethod()")
    public void doAfter() {
        System.out.println("@After");
    }

    // 声明后置通知:全部执行完成之后
    @AfterReturning("pointCutMethod()")
    public void doAfterReturning() {
        System.out.println("AfterReturning");
    }

    // 声明报错处理
    @AfterThrowing(pointcut = "pointCutMethod()", throwing = "e")
    public void doAfterThrowing(Exception e) {
        System.out.println("AfterThrowing");
    }

    // 声明环绕通知
    @Around("pointCutMethod()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("环绕通知开");
        Object result = joinPoint.proceed();
        System.out.println("环绕通知关");
        return result;
    }
}

结果

环绕通知开
@Before
2020-08-11 08:51:09 DEBUG [BaseJdbcLogger.java : 143] - ==>  Preparing: select count(1) from `vote_page` where `v_title`=? 
2020-08-11 08:51:09 DEBUG [BaseJdbcLogger.java : 143] - ==> Parameters: 大叔大婶大(String)
2020-08-11 08:51:09 TRACE [BaseJdbcLogger.java : 149] - <==    Columns: count(1)
2020-08-11 08:51:09 TRACE [BaseJdbcLogger.java : 149] - <==        Row: 0
2020-08-11 08:51:09 DEBUG [BaseJdbcLogger.java : 143] - <==      Total: 1
环绕通知关
@After
AfterReturning

2.1、自定义注解

用来保存方法的功能信息,如果没有则输出方法名

/**
 * 自定义注解:主要用来保存方法信息
 *
 * @author hubozhi
 * @email 1310225330@qq.com
 * @date 2020/8/10 15:32
 **/

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Desc {
    String value() default "";
    String okMsg() default "";
    String errMsg() default "";
}

2.2、AOP工具类

获取方法的注解

public class AOPUtil {
    public static <T extends Annotation> T getAnn(JoinPoint joinPoint, Class<T> annotationClass) {
        // 获取方法名
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        Class<?>[] argTypes = new Class[args.length];
        for (int i = 0; i < args.length; i++) {
            argTypes[i] = args[i].getClass();
        }
        Method method = null;
        try {
            method = joinPoint.getTarget().getClass().getMethod(methodName, argTypes);
        } catch (NoSuchMethodException | SecurityException e) {
            e.printStackTrace();
        }
        if (method != null) {
            return method.getAnnotation(annotationClass);
        }else{
            return null;
        }
    }
}