Spring Boot2 configates AOP pre-enhancement, post-enhancement, exception enhancement, surround enhancement, and final enhancement

Without going into too much detail about specific concepts related to AOP facets (conceptual understanding helps to understand the ideas), this is configuring AOP’s various enhanced logs to solve the hassle and unscientific problem of embedding logs in business code

Git demo project: github.com/zhang-xiao-… (Some newer)

1POM dependencies (log4J2 is used as the logging framework because it is more efficient and powerful than log4J or other logging frameworks)

<! > <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-log4j2</artifactId> </dependency> <! > <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> </exclusion> </exclusions> </dependency> <dependency> <! - add this to identify log4j2. Yml file (if the log output to local or specific disk, need to configure the yml) - > < groupId > com. Fasterxml. Jackson. Dataformat < / groupId > <artifactId>jackson-dataformat-yaml</artifactId> </dependency> <! GroupId > <artifactId> Spring-boot-starter-AOP </artifactId> </dependency>Copy the code

Write the section class

package com.example.nba.aop; Java.util.arrays; // Import java.util.arrays; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; /** * AopLog ** @author 10905 2019/Ap * @version 1.0 */ @aspect @Component Public class AopLog { Private final static Logger Logger = loggerFactory.getLogger (aoplog.class); // Pointcut annotation expression: // This is a regular expression that allows you to control the accuracy of logging (under package, under class, under method) and the type of aspect (at the business level, at the service interface level) @Pointcut("execution(* com.example.nba.controller.PlayerApi.*(..) )") // @Pointcut("execution(* com.example.nba.repository.PlayerRep.*(..) ") // Pointcut signature method, Public void mypointcut() {} @before (" myPointcut ()") public void Mybefore(JoinPoint JP) {logger.info("* prestack * calls the [" + jp.getSignature().getName() + "] method of [" + jp.gettarget ().getClass().getSimplename () + "] [" + Arrays. ToString (jp.getargs ()) + "] "); // When the request is received, Record the request content (again, you can enhance the configuration of the request information in the front) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); Logger.info (" requested URL: "+ request.getrequestURL ().toString()); Logger.info (" request method HTTP_METHOD: "+ request.getmethod ()); Logger.info (" Requested IP: "+ request.getremoteaddr ()); Logger.info (" Request full class name: "+ jp.getSignature().getingTypename () + "." + jp.getSignature().getName()); Logger.info (" Requested parameters (array form) : "+ array.toString (jp.getargs ())); } @afterreturing (pointcut = "mypointcut()", returning = "result") public void MyafterReturing(JoinPoint jp, Object result) {logger.info("* post-enhanced * calls the [" + jp.gettarget ().getClass().getSimplename () + "] of the [" +" Jp.getsignature ().getName() + "); } @afterthrowing (pointcut = "mypointcut()", throwing = "e") public void AfterThrowing(JoinPoint JP, RuntimeException e) {logger.error("* Exception enhancement * [" + jp.getSignature().getName().getClass().getSimplename () + "] Method exception [" + e + ") "); } @afterLogger (" myPointCut ()") public void afterLogger(JoinPoint JP) {logger.info("* Final afterLogger * [" +) Jp.getsignature ().getName() +" ); } @around ("mypointcut()") public Object aroundLogger(ProceedingJoinPoint JP) throws Throwable { Logger. The info (" in = = > > "+ jp. GetTarget (). The getClass (). The getName () +" class using AOP around enhanced = = "); Logger.info ("* wrap enhancement * calls the [" + jp.getSignature().getName() + "] method of [" + jp.gettarget ().getClass().getSimplename () + "]. [" + Arrays. ToString (jp.getargs ()) + "] "); try { Object result = jp.proceed(); Logger.info ("* wrap enhancement * calls the [" + jp.getSignature().getName() + "] method of "+ Jp.gettarget () +". Method return value [" + result + "] "); return result; } catch (Throwable e) {logger.error(jp.getsignature ().getName() + "; throw e; } finally {logger.info("* wrap enhancement * Execute the finally [" + jp.getSignature().getName() + "] method to end <<== = ". ); }}}Copy the code

3 tests (database code and business layer code will not be posted, mainly referring to AOP’s POM dependencies and aspect classes), such as my browser request

http://localhost:8080/player/findAll
Copy the code

Controller display:

 

 

Aop configuration in JSON format (note that the file format entry parameter needs to be judged (because excel,PDF, PNG and other formats cannot be converted to JSON)), if the current requirement is to request and return parameters are mainly in JSON format, and log to the database (exclude query log interface, because query log does not need to record to the database, this Where AOP excludes an interface or aspect, see below.

Log entities (tables)

package com.xinzuo.lvyou.pojo; import java.io.Serializable; import java.util.Date; import com.baomidou.mybatisplus.annotation.TableId; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.experimental.Accessors; /** ** <p> ** @author zhangxiaoxiang * @since 2019-07-08 */ @data @equalSandhashCode (callSuper = false)  @Accessors(chain = true) public class LogMonitor implements Serializable { private static final long serialVersionUID =  1L; /** * @tableId private String logId; /** * requestIp address */ private String requestIp; /** ** private String summarize; /** * private Integer responseCode; /** * private String requestType; /** * private String requestApi; /** * private String requestPara; /** * return parameter */ private String responsePara; /** * private Integer responseTime; /** * private Date createTime; /** * log belongTo (0 mobile front-end log,1PC log) */ private Integer belongTo; }Copy the code

The following is in JSON format with the requirement to record to the database, if the format returned is three-piece JSON, as follows

{" code ": 200," MSG ":" query clock frequency of success ", "data" : {" photoSave: "null," user ", null, "amount" : 27}}Copy the code

The section class is written as follows

package com.xinzuo.lvyou.aop;

import com.alibaba.fastjson.JSONArray;
import com.gexin.fastjson.JSON;
import com.gexin.fastjson.JSONObject;
import com.xinzuo.lvyou.dao.LogMonitorDao;
import com.xinzuo.lvyou.pojo.LogMonitor;
import com.xinzuo.lvyou.util.KeyUtil;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.Date;

/**
 * AopLog:日志切面类
 *
 * @author zhangxiaoxiang
 * @date: 2019/05/22
 */
@Component
@Aspect
public class AopJsonLog {
    /**
     * 记录日志到数据库
     */
    @Autowired
    private LogMonitorDao logMonitorDao;
    //记录到数据库
    LogMonitor logMonitor = new LogMonitor();
    long start = 0;

    /**
     * 使用org.slf4j.Logger,这是Spring实现日志的方法
     */
    private final static Logger logger = LoggerFactory.getLogger(AopJsonLog.class);

    @Pointcut("execution(* com.xinzuo.lvyou.admin..*(..)) ")
    public void myPointcut() {
    }

    /**
     * 前置增强 单独配置的前端接口切面
     * 日志打印排除在外,查询日志接口就不记录数据库
     * 排除某个方法使用!execution(* com.xinzuo.lvyou.controller.LogMonitorController.*(..))
     *
     * @param jp
     */
    @Before("execution(* com.xinzuo.lvyou.controller..*(..)) && !execution(* com.xinzuo.lvyou.controller.LogMonitorController.*(..))")
    public void MyBefore(JoinPoint jp) {
        logger.info("---------------------------------前端请求接口日志----------------------------------------------");
        // 接收到请求,记录请求内容(这里同样可以在前置增强配置请求的相关信息)
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        logger.info("访问的接口:" + jp.getTarget().getClass().getName() + "." + jp.getSignature().getName());
        try {
            logger.info("请求入参为:" + new JSONArray(Arrays.asList(jp.getArgs())).toString());
        } catch (Exception e) {
            logger.info("请求入参为:图片,视频,excel,PDF等格式(此时无法转换成JSON格式)");
            //由于知道这里异常的原因是json转换参数异常,所以就不打印了,不捕获,以免控制台难看或者日志难看
            //e.printStackTrace();
        }
        //logger.info("请求的地址URL:           " + request.getRequestURL().toString());
        //logger.info("请求的方式HTTP_METHOD:   " + request.getMethod());
        //logger.info("请求的IP:               " + request.getRemoteAddr());

        start = System.currentTimeMillis();
        logMonitor.setLogId(KeyUtil.genUniqueKey());
        logMonitor.setRequestType(request.getMethod());
        logMonitor.setRequestApi(jp.getTarget().getClass().getName() + "." + jp.getSignature().getName());
        try {
            logMonitor.setRequestPara(new JSONArray(Arrays.asList(jp.getArgs())).toString());
        } catch (Exception e) {
            logMonitor.setRequestPara("请求入参为:图片,视频,excel,PDF等格式(此时无法转换成JSON格式)");
        }
        logMonitor.setRequestIp(request.getRemoteAddr());
        logMonitor.setCreateTime(new Date());


    }

    /**
     * 后置增强
     * 排除某个方法使用!execution(* com.xinzuo.lvyou.controller.LogMonitorController.*(..))
     * 日志打印排除在外,查询日志接口就不记录数据库
     * @param jp
     * @param vo
     */
    @AfterReturning(pointcut = "execution(* com.xinzuo.lvyou.controller..*(..)) && !execution(* com.xinzuo.lvyou.controller.LogMonitorController.*(..))", returning = "vo")
    public void MyafterReturing(JoinPoint jp, Object vo) {

        //logger.info("访问的接口: " + jp.getTarget().getClass().getName() + "." + jp.getSignature().getName());
        //入参打印json数组格式
//        try {
//            logger.info("请求入参为:" + new JSONArray(Arrays.asList(jp.getArgs())).toString());
//        } catch (Exception e) {
//            logger.info("请求入参为图片,视频,excel,PDF等格式(此时无法转换成JSON格式)");
//            //由于知道这里异常的原因是json转换参数异常,所以就不打印了,不捕获,以免控制台难看或者日志难看
//            //e.printStackTrace();
//        }
        logger.info("方法返回值:" + JSON.toJSONString(vo));
        logMonitor.setResponsePara(JSON.toJSONString(vo));
        long end = System.currentTimeMillis();
        //解析json
        JSONObject json= null;
        try {
            json = JSON.parseObject(JSON.toJSONString(vo));
            logMonitor.setResponseCode(Integer.valueOf(json.get("code").toString()));
            logMonitor.setSummarize(json.get("msg").toString());
        } catch (Exception e) {
            logMonitor.setSummarize("进行图片,视频,excel,PDF等格式操作(由于这个操作特殊一点,所以后台直接把返回状态码默认为200,以实际为准)");
            logMonitor.setResponseCode(200);
            //e.printStackTrace();
        }


        logMonitor.setBelongTo(0);
        logMonitor.setResponseTime((int) (end - start));
        try {
            logMonitorDao.insert(logMonitor);
        } catch (Exception e) {
            logger.info("AOP系统故障!");
        }
    }

//    /**
//     * 异常抛出增强
//     *
//     * @param jp
//     * @param e
//     */
//    @AfterThrowing(pointcut = "myPointcut()", throwing = "e")
//    public void afterThrowing(JoinPoint jp, RuntimeException e) {
//        logger.error("异常增强:" + jp.getSignature().getName().getClass().getSimpleName() + "方法发生异常【" + e + "】");
//    }

    /**
     * 环绕增强 :测试的时候finally的切面日志注释不打印,因为日志多了反而不好调试,上线时再取消注释
     *
     * @param jp
     * @return
     * @throws Throwable
     */
    @Around("myPointcut()")
    public Object aroundLogger(ProceedingJoinPoint jp) throws Throwable {

        logger.info("admin---------------------------------后台请求接口日志----------------------------------------------");
        logger.info("访问的接口:" + jp.getTarget().getClass().getName() + "." + jp.getSignature().getName());
        //入参打印json数组格式

        try {
            logger.info("请求入参为:" + new JSONArray(Arrays.asList(jp.getArgs())).toString());
        } catch (Exception e) {
            logger.info("请求入参为:图片,视频,excel,PDF等格式(此时无法转换成JSON格式)");
            //由于知道这里异常的原因是json转换参数异常,所以就不打印了,不捕获,以免控制台难看或者日志难看
            //e.printStackTrace();
        }

        try {
            Object result = jp.proceed();
            logger.info("方法返回值:" + JSON.toJSONString(result));
            return result;
        } catch (Throwable e) {
            logger.info("访问的接口:" + jp.getTarget().getClass().getName() + "." + jp.getSignature().getName());
            logger.info("请求入参为:" + new JSONArray(Arrays.asList(jp.getArgs())).toString());
            logger.error(jp.getSignature().getName() + " 方法发生异常【" + e + "】");
            throw e;
        } finally {
            //logger.info("访问的接口: " + jp.getTarget().getClass().getName() + "."+jp.getSignature().getName());
            //logger.info("请求入参为: "+ new JSONArray(Arrays.asList(jp.getArgs())).toString());
            //logger.info("执行     :" + jp.getSignature().getName() + "方法结束。");
        }
    }


}
Copy the code

— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — here are spring AOP configuration requires additional depend on — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — –

 

<dependency> <groupId> <artifactId>aopalliance</artifactId> <version>1.0</version> </dependency> Aspectj </groupId> <artifactId> aspectJrt </artifactId> <version>1.6.12</version> </dependency> < the dependency > < groupId > org. Aspectj < / groupId > < artifactId > aspectjweaver < / artifactId > < version > 1.6.12 < / version > </dependency> <groupId> <artifactId>cglib</artifactId> <version>2.2</version> </dependency>Copy the code