spring 面向切面(AOP)编程例子

aop原理:
在这里插入图片描述
将共同方法封装起来,然后找到切面点插入进业务流程即可
JoinPoint要了解

框架:springcloud+consul+gateway+springboot
业务环境:系统log日志,每个操作得参数都要加入到数据库中
数据库:oracle

日志表:

package com.cuslink.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;

import java.io.Serializable;
import java.time.LocalDateTime;

/**
 * <p>
 * 日志表
 * </p>
 *
 */
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("TBL_LOG")
@ApiModel(value="TblLog对象", description="日志表")
public class TblLog implements Serializable {

    private static final long serialVersionUID = 1L;

    @TableId(value = "ID",type = IdType.UUID)
    private String id;

    @ApiModelProperty(value = "访问接口路径")
    @TableField("URL")
    private String url;

    @ApiModelProperty(value = "客户端类型,1电脑端,2移动端")
    @TableField("CLIENT_TYPE")
    private Double clientType;

    @ApiModelProperty(value = "IP地址")
    @TableField("IP_ADDRESS")
    private String ipAddress;

    @ApiModelProperty(value = "方法名称")
    @TableField("FUNC_NAME")
    private String funcName;

    @ApiModelProperty(value = "方法参数")
    @TableField("FUNC_PARAM")
    private String funcParam;

    @ApiModelProperty(value = "类型,1操作日志,2错误日志,3系统日志")
    @TableField("TYPE")
    private Double type;

    @ApiModelProperty(value = "内容")
    @TableField("CONTENT")
    private String content;

    @ApiModelProperty("请求状态 0正常 1错误")
    @TableField("STATUS")
    private Double status;

    @ApiModelProperty("错误信息")
    @TableField("ERROR_MSG")
    private String errorMsg;

    @ApiModelProperty(value = "创建时间")
    @TableField("CREATE_TIME")
    private LocalDateTime createTime;

    @ApiModelProperty(value = "更新时间")
    @TableField("UPDATE_TIME")
    private LocalDateTime updateTime;

    @ApiModelProperty(value = "创建人")
    @TableField("CREATE_ID")
    private String createId;

    @ApiModelProperty(value = "更新人")
    @TableField("UPDATE_ID")
    private String updateId;

    @ApiModelProperty(value = "是否删除,0不删除,1删除")
    @TableField("DELETE_FLAG")
    private Double deleteFlag;


}

切面编程设置一个log注解

package com.cuslink.annotation;

import java.lang.annotation.*;

/**
 * 操作日志元注解
 *

 */
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log {

    /**
     * 日志类型,1操作日志,2错误日志,3系统日志
     */
    double type() default 1;

    /**
     * 是否保存请求数据
     */
    boolean isSaveRequestData() default true;
}

log注解写在方法头上

@RestController
@RequestMapping("/v1/param-type")
@Validated
@Api(value = "TblParamTypeController",description = "参数类型")
public class TblParamTypeController {

    @Autowired
    private ITblParamTypeService iTblParamTypeService;

    @Log
    @PostMapping("/save")
    @ApiOperation("新增参数类型")
    public AjaxResult save(@Valid @RequestBody TblParamType tblParamType){
        if(iTblParamTypeService.saveParamType(tblParamType)){
            return new AjaxResult().success("true","新增成功");
        }

        return new AjaxResult().fail(ConstantsCode.FAIL_TO_SAVE,"false","新增失败");
    }

接下来是核心重点:
找到切入点,然后将再接口运行后将信息存到log表中

package com.cuslink.common.aspectj;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.cuslink.annotation.Log;
import com.cuslink.common.mapper.TblLogMapper;
import com.cuslink.common.util.IpUtil;
import com.cuslink.common.util.ServletUtils;
import com.cuslink.entity.TblLog;
import com.cuslink.service.AjaxResult;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.Arrays;

/**

 */
@Aspect
@Component
@Slf4j
public class LogAspectj {

    private TblLogMapper tblLogMapper;

    @Autowired
    public void setTblLogMapper(TblLogMapper tblLogMapper) {
        this.tblLogMapper = tblLogMapper;
    }


    private final String[] mobileAgents = {"iphone", "android", "phone", "mobile", "wap", "netfront", "java", "opera mobi", "opera mini", "ucweb", "windows ce", "symbian", "series", "webos", "sony", "blackberry", "dopod", "nokia", "samsung", "palmsource", "xda", "pieplus", "meizu", "midp", "cldc", "motorola", "foma", "docomo", "up.browser", "up.link", "blazer", "helio", "hosin", "huawei", "novarra", "coolpad", "webos", "techfaith", "palmsource", "alcatel", "amoi", "ktouch", "nexian", "ericsson", "philips", "sagem", "wellcom", "bunjalloo", "maui", "smartphone", "iemobile", "spice", "bird", "zte-", "longcos", "pantech", "gionee", "portalmmm", "jig browser", "hiptop", "benq", "haier", "^lct", "320x320", "240x320", "176x220", "w3c ", "acs-", "alav", "alca", "amoi", "audi", "avan", "benq", "bird", "blac", "blaz", "brew", "cell", "cldc", "cmd-", "dang", "doco", "eric", "hipt", "inno", "ipaq", "java", "jigs", "kddi", "keji", "leno", "lg-c", "lg-d", "lg-g", "lge-", "maui", "maxo", "midp", "mits", "mmef", "mobi", "mot-", "moto", "mwbp", "nec-", "newt", "noki", "oper", "palm", "pana", "pant", "phil", "play", "port", "prox", "qwap", "sage", "sams", "sany", "sch-", "sec-", "send", "seri", "sgh-", "shar", "sie-", "siem", "smal", "smar", "sony", "sph-", "symb", "t-mo", "teli", "tim-", "tosh", "tsm-", "upg1", "upsi", "vk-v", "voda", "wap-", "wapa", "wapi", "wapp", "wapr", "webc", "winw", "winw", "xda", "xda-", "Googlebot-Mobile"};

    @Pointcut("@annotation(com.cuslink.annotation.Log)")
    public void logPointCut() {
    }


    /**
     * 处理完请求后执行
     *
     * @param joinPoint 切点
     */
    @AfterReturning(pointcut = "logPointCut()", returning = "jsonResult")
    public void doAfterReturning(JoinPoint joinPoint, Object jsonResult) {
        handleLog(joinPoint, null, jsonResult);
    }

    /**
     * 拦截异常操作
     *
     * @param joinPoint 切点
     * @param e         异常
     */
    @AfterThrowing(value = "logPointCut()", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Exception e) {
        handleLog(joinPoint, e, null);
    }

    protected void handleLog(final JoinPoint joinPoint, final Exception e, Object o) {
        try {
            // 获得注解
            Log controllerLog = getAnnotationLog(joinPoint);
            if (controllerLog == null) {
                return;
            }
            HttpServletRequest request = ServletUtils.getRequest();
            // *========数据库日志=========*//
            TblLog tblLog = new TblLog();
            // 请求的地址
            String ip = IpUtil.getIpAddress(request);
            tblLog.setIpAddress(ip);
            // 返回参数
           // tblLog.setContent(JSONObject.toJSONString(o));
            tblLog.setType(controllerLog.type());
            tblLog.setUrl(ServletUtils.getRequest().getRequestURI());
            tblLog.setStatus(0d);
            tblLog.setCreateTime(LocalDateTime.now());
            tblLog.setUpdateTime(LocalDateTime.now());
            tblLog.setClientType(isMobile(request));
            if (joinPoint.getArgs() != null && joinPoint.getArgs().length > 0) {
                tblLog.setFuncParam(JSON.toJSONString(joinPoint.getArgs()[0]));
            }
            if (o != null) {
                tblLog.setContent(JSON.toJSONString(o));
                if (o instanceof AjaxResult) {
                    String status = ((AjaxResult) o).getError();
                    if ("true".equals(status)) {
                        tblLog.setStatus(0d);
                    } else {
                        tblLog.setStatus(1d);
                    }
                }
            }

            if (e != null) {
                //如果有异常
                tblLog.setStatus(1d);
                //tblLog.setErrorMsg(ExceptionUtils.getStackTrace(e)); 文本过大
                tblLog.setErrorMsg(e.getMessage());
            }
            log.info("URL : {}", request.getRequestURL().toString());
            log.info("HTTP_METHOD : {}", request.getMethod());
            log.info("IP : {}", IpUtil.getIpAddress(request));
            log.info("CLASS_METHOD : {}", joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
            log.info("ARGS : {}", Arrays.toString(joinPoint.getArgs()));
            log.info("RESPONSE : {}", o);
            // 设置方法名称
            String className = joinPoint.getTarget().getClass().getName();
            String methodName = joinPoint.getSignature().getName();
            tblLog.setFuncName(className + "." + methodName + "()");
            // 保存数据库
            saveLogData(tblLog);
        } catch (Exception exp) {
            // 记录本地异常日志
            log.error("[日志服务]----日志记录日常,e->: {}", ExceptionUtils.getStackTrace(exp));
        }
    }

    protected void saveLogData(TblLog tblLog) throws Exception {
        tblLogMapper.insert(tblLog);
    }

    /**
     * 判断是否为移动端
     *
     * @return 1:PC端 2.移动端
     */
    private Double isMobile(HttpServletRequest request) {
        String userAgent = request.getHeader("User-Agent");
        if (StringUtils.isNotBlank(userAgent)) {
            for (String mobileAgent : mobileAgents) {
                if (userAgent.toLowerCase().contains(mobileAgent)) {
                    return 2d;
                }
            }
        }
        return 1d;
    }

    /**
     * 是否存在注解,如果存在就获取
     */
    private Log getAnnotationLog(JoinPoint joinPoint) throws Exception {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();

        if (method != null) {
            return method.getAnnotation(Log.class);
        }
        return null;
    }
}

用到得工具类

package com.cuslink.common.util;


import io.micrometer.core.instrument.util.StringUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;

import javax.servlet.http.HttpServletRequest;
import java.util.Objects;

/**
 * IP操作相关工具类
 */
public class IpUtil {

    private IpUtil() {
    }

    /**
     * 获取IP地址
     *
     * @param request 当前请求
     * @return IP地址
     */
    public static String getIpAddress(HttpServletRequest request) {
        String xIp = request.getHeader("X-Real-IP");
        String xFor = request.getHeader("X-Forwarded-For");
        if (StringUtils.isNotEmpty(xFor) && !"unKnown".equalsIgnoreCase(xFor)) {
            //多次反向代理后会有多个IP值,第一个IP才是真实IP
            int index = xFor.indexOf(",");
            if (index != -1) {
                return xFor.substring(0, index);
            } else {
                return xFor;
            }
        }
        xFor = xIp;
        if (StringUtils.isNotEmpty(xFor) && !"unKnown".equalsIgnoreCase(xFor)) {
            return xFor;
        }
        if (StringUtils.isBlank(xFor) || "unKnown".equalsIgnoreCase(xFor)) {
            xFor = request.getHeader("Proxy-Client-IP");
        }
        if (StringUtils.isBlank(xFor) || "unKnown".equalsIgnoreCase(xFor)) {
            xFor = request.getHeader("WL-Proxy-Client-IP");
        }
        if (StringUtils.isBlank(xFor) || "unKnown".equalsIgnoreCase(xFor)) {
            xFor = request.getHeader("HTTP-CLIENT-IP");
        }
        if (StringUtils.isBlank(xFor) || "unKnown".equalsIgnoreCase(xFor)) {
            xFor = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (StringUtils.isBlank(xFor) || "unKnown".equalsIgnoreCase(xFor)) {
            xFor = request.getRemoteAddr();
        }
        return xFor;
    }

    public static String getIpAddress(ServerHttpRequest request){
        HttpHeaders headers = request.getHeaders();
        String xIp = headers.getFirst("X-Real-IP");
        String xFor = headers.getFirst("X-Forwarded-For");
        if (StringUtils.isNotEmpty(xFor) && !"unKnown".equalsIgnoreCase(xFor)) {
            //多次反向代理后会有多个IP值,第一个IP才是真实IP
            int index = xFor.indexOf(",");
            if (index != -1) {
                return xFor.substring(0, index);
            } else {
                return xFor;
            }
        }
        xFor = xIp;
        if (StringUtils.isNotEmpty(xFor) && !"unKnown".equalsIgnoreCase(xFor)) {
            return xFor;
        }
        if (StringUtils.isBlank(xFor) || "unKnown".equalsIgnoreCase(xFor)) {
            xFor = headers.getFirst("Proxy-Client-IP");
        }
        if (StringUtils.isBlank(xFor) || "unKnown".equalsIgnoreCase(xFor)) {
            xFor = headers.getFirst("WL-Proxy-Client-IP");
        }
        if (StringUtils.isBlank(xFor) || "unKnown".equalsIgnoreCase(xFor)) {
            xFor = headers.getFirst("HTTP-CLIENT-IP");
        }
        if (StringUtils.isBlank(xFor) || "unKnown".equalsIgnoreCase(xFor)) {
            xFor = headers.getFirst("HTTP_X_FORWARDED_FOR");
        }
        if (StringUtils.isBlank(xFor) || "unKnown".equalsIgnoreCase(xFor)) {
            xFor = Objects.requireNonNull(request.getRemoteAddress()).getAddress().getHostAddress();
        }
        return xFor;
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值