java练习生 AOP【日志】【异常处理】

一、添加依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

二、添加AOP操作类

2.1 指定方法切面,做日志输出

package com.vcredit.fts.common.aop.exception;

import com.alibaba.fastjson.JSON;
import com.vcredit.fts.common.util.IPUtil;
import lombok.Setter;
import lombok.extern.log4j.Log4j2;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;

@Log4j2
@Aspect
@Component
public class LogAspect {
    private ThreadLocal<ApiLogAspect.ApiLog> apiLogThreadLocal = new ThreadLocal<>();


    /**
     * 声明切点
     */
    @Pointcut("execution(* com..web.*Controller.*(..))")
    public void pointCut() {
    }

    @Around("pointCut()")
    public final Object doPrintLog(ProceedingJoinPoint joinPoint) throws Throwable {
        return check(joinPoint);
    }

    protected Object check(ProceedingJoinPoint joinPoint) throws Throwable {
        Object result = null;
        try {
            apiLogThreadLocal.set(ApiLogAspect.ApiLog.newInstance());
            doBefore(joinPoint);
            result = joinPoint.proceed(joinPoint.getArgs());
            doAfterReturning(result);
        } catch (Throwable e) {
            doException(e);
            throw e;
        } finally {
            apiLogThreadLocal.remove();
        }
        return result;
    }

    private void doException(Throwable e) {
        // 处理完请求,返回内容
        ApiLogAspect.ApiLog apiLog = apiLogThreadLocal.get();
        apiLog.setResponseBody("exception:" + e.getMessage());
        apiLog.setEndTime(System.currentTimeMillis());
        log.info(apiLog.toString());
    }

    private void doBefore(ProceedingJoinPoint joinPoint) {
        // 接收到请求,记录请求内容
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        // 记录下请求内容
        ApiLogAspect.ApiLog apiLog = apiLogThreadLocal.get();
        apiLog.setApiClassName(joinPoint.getSignature().getDeclaringTypeName());
        apiLog.setApiMethodName(joinPoint.getSignature().getName());
        apiLog.setRequestBody(JSON.toJSONString(joinPoint.getArgs()));
        apiLog.setRequestMethod(request.getMethod());
        apiLog.setRequestIp(IPUtil.getIpAddr(request));
        apiLog.setRequestUrl(request.getRequestURL().toString());
    }

    private void doAfterReturning(Object result) {
        // 处理完请求,返回内容
        ApiLogAspect.ApiLog apiLog = apiLogThreadLocal.get();
        apiLog.setResponseBody(JSON.toJSONString(result));
        apiLog.setEndTime(System.currentTimeMillis());
        log.info(apiLog.toString());
    }


    @Setter
    public static class ApiLog {
        private Long startTime;
        private Long endTime;
        private Long consumeTime;
        private String requestUrl;
        private String requestMethod;
        private String requestIp;
        private String apiClassName;
        private String apiMethodName;
        private String requestBody;
        private String responseBody;

        public static LogAspect.ApiLog newInstance() {
            return new LogAspect.ApiLog();
        }

        private ApiLog() {
            startTime = System.currentTimeMillis();
        }

        public void setEndTime(Long endTime) {
            this.endTime = endTime;
            this.consumeTime = this.endTime - this.startTime;
        }

        @Override
        public String toString() {
            StringBuffer buffer = new StringBuffer();
            buffer.append("耗时:").append(consumeTime).append("ms").append(" , ");
            buffer.append("URL:").append(requestUrl).append(" , ");
            buffer.append("IP:").append(requestIp).append(" , ");
            buffer.append("方式:").append(requestMethod).append(" , ");
            buffer.append("请求方法:").append(apiClassName).append(".").append(apiMethodName).append(" , ");
            buffer.append("\r\n");
            buffer.append("输入:").append(requestBody);
            buffer.append("\r\n");
            buffer.append("输出:").append(responseBody);
            return buffer.toString();
        }

    }
}

2.2 指定注解切面,做日志输出

A:创建标记注解

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OperationLog {
    /**
     * 操作方法
     *
     * @return
     */
    String value() default "";
}

B:创建切面处理类(包括mongo类)

package com.vcredit.fts.common.aop.exception;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Maps;
import com.vcredit.fts.common.aop.exception.annotation.OperationLog;
import com.vcredit.fts.common.service.mongo.MongoService;
import com.vcredit.fts.common.util.IPUtil;
import io.netty.util.internal.StringUtil;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.time.LocalDateTime;
import java.util.Map;

@Slf4j
@Aspect
@Component
public class OperationLogAspect {
    private ThreadLocal<WebOperationLogPO> sysOperatorLogThreadLocal = new ThreadLocal<>();

    @Resource
    private MongoService mongoService;

    @Pointcut("@annotation(com.vcredit.fts.common.aop.exception.annotation.OperationLog)")
    public void pointcut() {
    }

    @Around("pointcut()")
    public final Object doSaveUserLog(ProceedingJoinPoint joinPoint) throws Throwable {
        return check(joinPoint);
    }

    /**
     * 功能描述: 切面检查<br> 〈功能详细描述〉
     *
     * @param joinPoint 切面
     * @return Object
     * @throws Throwable
     */
    protected Object check(ProceedingJoinPoint joinPoint) throws Throwable {
        setSysOperatorLogBefore(joinPoint);
        Object result = joinPoint.proceed();
        setSysOperatorLogAfter(joinPoint);
        saveUserOperatorLog();
        return result;
    }

    /**
     * 设置操作日志属性(之前)
     *
     * @param joinPoint 代理对象
     */
    private void setSysOperatorLogBefore(ProceedingJoinPoint joinPoint) {
        WebOperationLogPO sysOperatorLog = new WebOperationLogPO();
        // 操作类型
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        OperationLog logAnnotation = method.getAnnotation(OperationLog.class);
        if (logAnnotation != null) {
            sysOperatorLog.setOperation(logAnnotation.value());
        }
        // 用户信息
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String userName = request.getHeader(USER_NAME_KEY);
        if (StringUtil.isNullOrEmpty(userName)) {
            userName = "未知";
        }
        sysOperatorLog.setMethod(request.getMethod());
        sysOperatorLog.setCreaterUser(userName);
        sysOperatorLog.setCreateTime(LocalDateTime.now());
        sysOperatorLog.setIp(IPUtil.getIpAddr(request));
        sysOperatorLogThreadLocal.set(sysOperatorLog);
    }

    /**
     * 获取用户名key
     */
    private static final String USER_NAME_KEY = "userName";

    /**
     * 设置操作日志属性(之后)
     *
     * @param joinPoint 代理对象
     */
    private void setSysOperatorLogAfter(ProceedingJoinPoint joinPoint) {
        WebOperationLogPO sysOperatorLog = sysOperatorLogThreadLocal.get();
        // 请求参数
//        Map paramMap = generateValueMap(joinPoint);
//        sysOperatorLog.setContext(JSON.toJSONString(paramMap));
        sysOperatorLog.setContext(JSON.toJSONString(joinPoint.getArgs()));
    }

    /**
     * 保存操作日志
     */
    private void saveUserOperatorLog() {
        try {
            // 用户操作异常时,不保存操作日志
            WebOperationLogPO sysOperatorLog = sysOperatorLogThreadLocal.get();
            if (sysOperatorLog == null) {
                return;
            }
            // 保存日志
            mongoService.save(sysOperatorLog, "Demo.OperationLog");
        } catch (Exception e) {
            log.warn("用户操作日志保存失败:{} , log : {}", e.getMessage(), JSON.toJSONString(sysOperatorLogThreadLocal.get()));
        } finally {
            // 清除threadLocal
            sysOperatorLogThreadLocal.remove();
        }
    }


    /**
     * 生成参数信息
     *
     * @param joinPoint
     * @return
     */
    protected final Map<String, Object> generateValueMap(ProceedingJoinPoint joinPoint) {
        Map<String, Object> valueMap = Maps.newHashMap();
        // 获取当前注解方法
        Object[] args = joinPoint.getArgs();
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        for (int i = 0; i < method.getParameters().length; i++) {
            Parameter parameter = method.getParameters()[i];
            if (parameter.getAnnotation(RequestBody.class) != null) {
                JSONObject jsonObject;
                if (args[i] instanceof String) {
                    jsonObject = JSON.parseObject(args[i].toString());
                } else {
                    jsonObject = JSON.parseObject(JSON.toJSONString(args[i]));
                }
                generateValueMap(valueMap, jsonObject);
            } else {
                valueMap.put(parameter.getName(), args[i]);
            }
        }
        return valueMap;
    }

    /**
     * 生成参数信息
     *
     * @param valueMap
     * @param jsonObject
     */
    protected final void generateValueMap(Map<String, Object> valueMap, JSONObject jsonObject) {
        for (Map.Entry<String,Object> entry : jsonObject.entrySet()) {
            String key = entry.getKey();
            Object value = entry.getValue();
            valueMap.put(key, value);
            if (value instanceof JSONObject) {
                generateValueMap(valueMap, jsonObject.getJSONObject(key));
            }
        }
    }


    @Data
    @Document(collection = "Demo.OperationLog")
    public static class WebOperationLogPO{
        /**
         * 创建人
         */
        private String createrUser;
        /**
         * 内容
         */
        private String context;
        /**
         * ip地址
         */
        private String ip;
        /**
         * 目标方法
         */
        private String method;
        /**
         * 操作
         */
        @Indexed
        private String operation;
        /**
         * 创建时间
         */
        @Indexed
        private LocalDateTime createTime;
    }
}

C: 在需要添加操作记录的方法上添加注解

/**
* 清除指定缓存
*/
@GetMapping(value = "/clear-redis/{redisKey}")
@OperationLog("测试-clearRedis")
public CommonResult clearRedis(@PathVariable String redisKey) {
if (StringUtil.isNullOrEmpty(redisKey)) {
return CommonResult.init(SystemStatus.PARAM_VALID_ERROR);
}
redisKey = RedisKeyFlag.BASE + redisKey;
// 任务结束时,移除redis
if (redisService.exists(redisKey)) {
redisService.del(redisKey);
} else if (redisKey.endsWith("*")) {
var keys = redisService.keys(redisKey);
if (!ListUtil.isNullOrEmpty(keys)) {
redisService.del(keys);
}
}
return CommonResult.successBase();
}

D:查看结果

2.3 全局异常切面,做统一输出及报警

A:创建系统公共返回枚举

/**
 * 系统通用状态枚举
 *
 * @author huangzhongqing
 * @date 2020-12-04
 */
public enum SystemStatus {
    // --------------- 成功 ---------------
    SYS_SUCCESS(0,"成功!"),
    // --------------- 系统异常 ---------------
    SYS_ERROR(1001,"系统异常!"),
    PARAM_VALID_ERROR(1002,"参数校验失败!"),
    CONGIG_ERROR(1003,"配置异常!"),
    EMPTY_ERROR(1003,"结果为空!"),
    PARAM_VALIDATE_ERROR(1003,"用户交互验证失败!"),
    IDEMPOTENT_CHECK_ERROR(1004,"幂等校验不通过!"),
    PROCESS_IS_RUNNING(1005,"当前流程正在处理中"),
    ENCRYPT_ERROR(1006,"数据加密异常"),
    DECRYPT_ERROR(1007,"数据解密异常"),
    // --------------- 外部异常 ---------------
    OUT_REQUEST_ERROR(2001,"调用外部接口异常"),
    ECM_DOWNLOAD_ERROR(2002,"调用ecm下载服务异常"),
    ECM_UPLOAD_ERROR(2003,"调用ecm上传服务异常"),
    SFTP_LOGIN_ERROR(2004,"sftp连接异常"),
    SFTP_CREAT_PATH_ERROR(2005,"sftp创建路径异常"),
    SFTP_SERVICE_ERROR(2006,"sftp服务器异常"),
    SFTP_UPLOAD_ERROR(2007,"sftp上传文件异常"),
    // --------------- 其他异常 ---------------
    EMAIL_SEND_ERROR(3001,"发送邮件异常"),
    CONTRACT_QUERY_DOING(3002,"合同文件列表为空,可能原因bid不存在或者合同生成中"),
    CONTRACT_QUERY_SERVICE_ERROR(3002,"查询文件异常"),
    FILE_QUERY_SERVICE_ERROR(3002,"查询合同文件列表异常"),
    FILE_QUERY_SQL_ERROR(3002,"查询合同文件异常"),
    FILE_TYPE_LIST_ERROR(3002,"文件类型列表为空"),
    DOC_SAVE_SQL_ERROR(3002,"保存文件信息异常"),
    QUERY_COUNT_CONFIG_ERROR(3002,"获取查询数量限制配置异常"),
    QUERY_COUNT_OVER_LIMIT_ERROR(3002,"查询数量查过配置限制查询数量"),
    UPDATE_QUERY_ERROR(3002,"更新流程信息失败,未查询到订单数据"),
    UPDATE_PROCESS_ERROR(3002,"更新流程信息异常"),
    SYNC_DATA_PROCESS_ERROR(3002,"流程类型不存在"),
    QUERY_SERVICE_ERROR(3002,"未查询到数据"),
    FILE_QUERY_CANCEL_ERROR(9000,"订单已解约"),
    NOT_ENUM_TYPE(9001,"aop参数类型错误,非枚举类型!"),
    ;

    private int code;

    private String message;

    SystemStatus(int code, String message) {
        this.code = code;
        this.message = message;
    }

    public int getCode() {
        return code;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) { this.message = message; }
}

B:创建公共返回类

import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;

/**
 * 系统通用返回参数
 *
 * @author huangzhongqing
 * @date 2020-12-04
 */
@Data
@Accessors(chain = true)
@NoArgsConstructor
public class CommonResult<T> extends BaseModel {
    /**
     * 状态码
     */
    private int Code;
    /**
     * 消息
     */
    private String Message;
    /**
     * 结果
     */
    private T Result;

    private CommonResult(SystemStatus baseEnum) {
        this.Code = baseEnum.getCode();
        this.Message = baseEnum.getMessage();
    }
    private CommonResult(T result) {
        this.Code = SystemStatus.SYS_SUCCESS.getCode();
        this.Message = SystemStatus.SYS_SUCCESS.getMessage();
        this.Result = result;
    }

    private CommonResult( Integer code, String message) {
        this.Code = code;
        this.Message = message;
    }

    /**
     * 基本正确的返回
     * @return Result
     */
    public static CommonResult successBase() { return new CommonResult( SystemStatus.SYS_SUCCESS); }

    /**
     * 正确的返回帶返回体
     * @return Result
     */
    public static <T> CommonResult<T> successBase(T body) {
        return new CommonResult(body);
    }

    /**
     * 基本异常的返回
     * @return Result
     */
    public static CommonResult errorBase() {
        return new CommonResult(SystemStatus.SYS_ERROR);
    }

    /**
     * 自定义返回
     * @return Result
     */
    public static CommonResult init(SystemStatus code) {
        return new CommonResult(code);
    }
    /**
     * 指定异常枚举,返回错误信息
     * @param baseEnum 异常枚举
     * @return Result
     */
    public static CommonResult error(SystemStatus baseEnum) {
        return new CommonResult(baseEnum);
    }

    public static CommonResult error(Integer code, String alertMessage) {
        return new CommonResult(code,alertMessage);
    }
}

C:创建自定义异常类

import com.vcredit.fts.common.model.enums.SystemStatus;
import com.vcredit.fts.common.model.consts.ConstParam;
import com.vcredit.fts.common.util.StringUtil;
import lombok.Data;

import java.util.Objects;

/**
 * 基本异常
 *
 * @author huangzhongqing
 * @date 2020-12-04
 */
@Data
public class SysException extends RuntimeException {
    /**
     * serialVersionUID
     */
    private static final long serialVersionUID = ConstParam.ONE_L;
    /**
     * 异常详情
     */
    private Exception Exp;
    /**
     * 系统状态
     */
    private SystemStatus Status;
    /**
     * 是否通知
     */
    private Boolean IsNotice;

    // --------------- 构造函数 begain ---------------
    /**
     * 基本异常
     */
    public SysException() {
        this.Status = SystemStatus.SYS_ERROR;
    }
    /**
     * 基本异常
     *
     * @param message
     */
    public SysException(String message) {
        this.Status = SystemStatus.SYS_ERROR;
        this.Status.setMessage(message);
    }
    /**
     * 基本异常
     */
    public SysException(SystemStatus status) {
        this.Status = status;
    }
    /**
     * 基本异常
     *
     * @param status
     * @param message
     */
    public SysException(SystemStatus status, String message) {
        this.Status = status;
        this.Status.setMessage(message);
    }
    /**
     * 基本异常
     *
     * @param ex
     */
    public SysException(Exception ex) {
        this.Status = SystemStatus.SYS_ERROR;
        this.Status.setMessage(ex.getMessage());
        this.Exp = ex;
    }
    /**
     * 基本异常
     *
     * @param status
     * @param ex
     */
    public SysException(SystemStatus status, Exception ex) {
        this.Status = status;
        this.Exp = ex;
    }
    /**
     * 基本异常
     *
     * @param isNotice
     */
    public SysException(Boolean isNotice) {
        this.Status = SystemStatus.SYS_ERROR;
        this.IsNotice = isNotice;
    }
    /**
     * 基本异常
     *
     * @param message
     * @param isNotice
     */
    public SysException(String message, Boolean isNotice) {
        this.Status = SystemStatus.SYS_ERROR;
        this.Status.setMessage(message);
        this.IsNotice = isNotice;
    }
    /**
     * 基本异常
     *
     * @param status
     * @param isNotice
     */
    public SysException(SystemStatus status, Boolean isNotice) {
        this.Status = status;
        this.IsNotice = isNotice;
    }
    /**
     * 基本异常
     *
     * @param status
     * @param message
     * @param isNotice
     */
    public SysException(SystemStatus status, String message, Boolean isNotice) {
        this.Status = status;
        this.Status.setMessage(message);
        this.IsNotice = isNotice;
    }
    /**
     * 基本异常
     *
     * @param ex
     * @param isNotice
     */
    public SysException(Exception ex, Boolean isNotice) {
        this.Status = SystemStatus.SYS_ERROR;
        this.Status.setMessage(ex.getMessage());
        this.Exp = ex;
        this.IsNotice = isNotice;
    }
    /**
     * 基本异常
     *
     * @param status
     * @param ex
     * @param isNotice
     */
    public SysException(SystemStatus status, Exception ex, Boolean isNotice) {
        this.Status = status;
        this.Exp = ex;
        this.IsNotice = isNotice;
    }
    // --------------- 构造函数 end ---------------
    /**
     * 获取消息
     *
     * @return 结果
     */
    @Override
    public String getMessage() {
        if(!Objects.isNull(this.Status) && !StringUtil.isNullOrEmpty(this.Status.getMessage())){

            return this.Status.getMessage();
        }
        else{
            return "未知";
        }
    }
}

D:创建切面处理类

import com.alibaba.fastjson.JSON;
import com.vcredit.fts.common.exception.SysException;
import com.vcredit.fts.common.model.consts.ConstParam;
import com.vcredit.fts.common.model.dto.CommonResult;
import com.vcredit.fts.common.model.enums.SystemStatus;
import com.vcredit.fts.common.util.EmailUtil;
import com.vcredit.fts.common.util.StringUtil;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.http.converter.HttpMessageConversionException;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.annotation.Resource;

@RestControllerAdvice
@Log4j2
public class ExceptionAspect {
    @Resource
    private EmailUtil email;

    @Autowired
    protected MessageSource messageSource;

    /**
     * 全局异常处理
     *
     * @param e 异常对象
     * @return CommonResult
     */
    @ExceptionHandler(Throwable.class)
    public CommonResult handleException(Throwable e) {
        log.error("Exception :", e);
        String subject = e.getMessage();
        if(StringUtil.isNullOrEmpty(subject)) {
            subject="系统异常";
        }
        email.sendMail(e.toString(), "【异常】" + e.getMessage());
        return CommonResult.errorBase().setMessage(e.getMessage());
    }

    /**
     * HTTP消息转换异常
     *
     * @param e 异常对象
     * @return Result
     */
    @ExceptionHandler(HttpMessageConversionException.class)
    public CommonResult handleHttpMessageConversionException(HttpMessageConversionException e) {
        log.error("HTTP消息转换异常 : {}", e.getMessage());
        return CommonResult.error(SystemStatus.PARAM_VALIDATE_ERROR);

    }

    /**
     * 参数验证异常处理
     *
     * @param ex 异常对象
     * @return Result
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public CommonResult handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
        log.debug("参数验证异常处理 :{}" + ex.getMessage());
        return CommonResult.errorBase().setCode(SystemStatus.PARAM_VALID_ERROR.getCode()).setMessage(errorMessage(ex));
    }

    /**
     * 内置异常处理
     *
     * @param e 异常对象
     * @return CommonResult
     */
    @ExceptionHandler(SysException.class)
    public CommonResult handleSysException(SysException e) {
        log.error("sysException :", e);
        if(e.getIsNotice() != null && e.getIsNotice().compareTo(true) == ConstParam.ZERO){
            email.sendMail(JSON.toJSONString(e),"【异常】XX系统");
        }
        return CommonResult.init(e.getStatus());
    }


    private String errorMessage(MethodArgumentNotValidException e) {
        BindingResult bindingResult = e.getBindingResult();
        StringBuilder stringBuilder = new StringBuilder();
        // 只返回一个错误
        if (bindingResult.hasErrors()) {
            ObjectError firstError = bindingResult.getAllErrors().get(0);
            String messages = firstError.getArguments()[0].toString().split("default message")[1];
            stringBuilder.append(StringUtils.isEmpty(firstError.getDefaultMessage()) ? "param is invalid" : String.format("参数%s : %s", messages, firstError.getDefaultMessage()));
        }
        return stringBuilder.toString();
    }
}

三、详细解析

生命周期相关注解主要包括@Around、@Before、@After、@AfterReturning、@AfterThrowing

 执行顺序为

@Around作用
1.可以在目标方法之前进行增强动作,也可以执行完目标方法后执行
2.可以决定目标方法何时,以何种方式执行,甚至可以不执行
3.可以改变目标方法的参数值,也可以改变执行目标方法之后的返回值;当需要改变目标方法的返回值时只能使用@Around

注意: 使用@Around的方法的第一个形参必须是ProceedJoinPoint类型,只有调用该参数的proceed()方法才会执行目标函数

ProceedJoinPoint是JoinPoint的子类,多了proceed()方法,并可以抛出异常。

@Pointcut("execution(* com..web.*Controller.*(..))")
@Pointcut("@annotation(com.vcredit.fts.common.aop.exception.annotation.OperationLog)")

execution表达式基本语法格式为:

execution(<修饰符模式>?<返回类型模式><方法名模式>(<参数模式>)<异常模式>?)

说明1:
1、除了返回类型模式,方法名模式和参数模式外,其它项都是可选的。
2、多个execution表达式通过 || 进行连接

说明2:
1、【非必填】修饰符模式。public 表示public 级别方法。 可以不写,不写就是所有的方法(public,private,protected等级别的方法)。
2、【必填】返回类型模式。表示方法返回值的类型, * 表示全部。
3、【必填】方法名模式。表示通配符; .. 表示包以及包下面的子包。
4、【必填】参数模式。括号表示参数,两个点表示任何参数类型。 *(..) 表示全部方法。
5、【非必填】异常模式。

四、补充

springboot添加启动日志

@Log4j2
public class MainShowInfoUtil {
    private MainShowInfoUtil() {
    }

    private static final String NOT_SET = "[Not Set]";

    public static void showApplicationSummary(Environment env) {
        String protocol = StringUtils.hasText(env.getProperty("server.ssl.key-store")) ? "https" : "http";
        String appId = env.getProperty("spring.application.appId");
        if (!StringUtils.hasText(appId)) {
            appId = NOT_SET;
        }
        String nodeId = env.getProperty("nodeId");
        if (!StringUtils.hasText(nodeId)) {
            nodeId = NOT_SET;
        }
        try {
            log.info("\n---------------------------程序启动完成-------------------------------\n\t" +
                            "Application '{}' is running! \n\t" +
                            "Access URLs:\n\t" +
                            " - Local: {}://localhost:{}/swagger-ui.html\n\t" +
                            " - External: {}://{}:{}/swagger-ui.html\n\t" +
                            " - Prometheus: {}://localhost:{}/actuator/prometheus\n\t" +
                            "Runtime Parameters: \n\t" +
                            " - appID: {}\n\t" +
                            " - nodeId: {}\n\t" +
                            "Active Profile(s): {}\n" +
                            "-------------------------------------------------------------------",
                    env.getProperty("spring.application.name"),
                    protocol, env.getProperty("local.server.port"),
                    protocol,InetAddress.getLocalHost().getHostAddress(), env.getProperty("local.server.port"),
                    protocol, env.getProperty("local.server.port"),
                    appId, nodeId, env.getActiveProfiles());
        } catch (UnknownHostException e) {
            log.catching(e);
        }
    }
}
@EnableApolloConfig
@SpringBootApplication(scanBasePackages = "com.hzq.fts")
@EnableEcmService
@Slf4j
public class FtsProcessApplication {

    public static void main(String[] args) {
        ConfigurableEnvironment environment = SpringApplication.run(FtsProcessApplication.class,args).getEnvironment();
        MainShowInfoUtil.showApplicationSummary(environment);
    }
}
原文地址:https://www.cnblogs.com/ariter/p/15528629.html