springboot利用AOP完成日志统计的详细步骤

目录
  • 1、创建日志表
  • 2、创建实体类
  • 3、创建枚举类
  • 4、创建自定义注解
  • 5、获取ip的util
  • 6、线程池util
  • 7、HttpServletRequest实现类
  • 8、添加过滤器
  • 9、添加AOP核心类
  • 10、接口测试

步骤写的很详细,可以直接复制拿来用的,其中用到了过滤器、自定义注解以及AOP切面,来完成日志记录统计,感兴趣的收藏起来,以后遇到了可以直接用。

可能步骤会比较多,但是整体跟着思路下来,应该没什么大问题的。

项目用到了过滤器,可能有的人会不理解,之所以用过滤器是因为想要在日志记录post请求的json数据。

请求的时候,是通过request的body来传输的。在AOP后置方法中获取request里面的body,是取不到,直接为空。

原因很简单:因为是流。想想看,java中的流也是只能读一次,因为我是在AOP后置方法获取的,控制器实际上已经读过了一次,后置方法再读自然为空了。所以用过滤器来进行解决了这个问题。

1、创建日志表

这里我用的是mysql,假如您用的别的数据库,可以自行根据数据库类型进行修改。

CREATE TABLE `log`  (
  `id` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '主键',
  `create_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '创建人',
  `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',
  `update_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '最近更新时间',
  `update_time` datetime NULL DEFAULT NULL COMMENT '最近更新人',
  `update_count` int(11) NULL DEFAULT NULL COMMENT '更新次数',
  `delete_flag` char(1) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '删除标志',
  `delete_time` datetime NULL DEFAULT NULL COMMENT '删除日期',
  `delete_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '删除人',
  `cost_time` int(11) NULL DEFAULT NULL COMMENT '花费时间',
  `ip` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'ip',
  `description` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '日志描述',
  `request_param` longtext CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '请求参数',
  `request_json` longtext CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '请求json数据',
  `request_type` varchar(16) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '请求类型',
  `request_url` varchar(36) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '请求路径',
  `username` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '请求用户',
  `operation_type` int(3) NULL DEFAULT NULL COMMENT '操作类型',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

2、创建实体类

我的项目运用到了mybatisplus、swagger、lombok,你们可以根据自己项目框架写对应的实体类。BaseModel 是我们封装了一个基础实体类,专门存放关于操作人的信息,然后实体类直接继承。

import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModelProperty;
import cn.org.xaas.mybatis.model.BaseModel;
import lombok.Data;
import lombok.ToString;

@TableName(value = "log")
@Data
@ToString(callSuper = true)
public class Log extends BaseModel {

    @ApiModelProperty(value = "花费时间")
    @TableField(value = "cost_time")
    private Integer costTime;

    @ApiModelProperty(value = "ip")
    @TableField(value = "ip")
    private String ip;

    @ApiModelProperty(value = "日志描述")
    @TableField(value = "description")
    private String description;

    @ApiModelProperty(value = "请求参数")
    @TableField(value = "request_param")
    private String requestParam;

    @ApiModelProperty(value = "请求json数据")
    @TableField(value = "request_json")
    private String requestJson;

    @ApiModelProperty(value = "请求类型")
    @TableField(value = "request_type")
    private String requestType;

    @ApiModelProperty(value = "请求路径")
    @TableField(value = "request_url")
    private String requestUrl;

    @ApiModelProperty(value = "请求用户")
    @TableField(value = "username")
    private String username;

    @ApiModelProperty(value = "操作类型")
    @TableField(value = "operation_type")
    private Integer operationType;
}

3、创建枚举类

用来记录日志操作类型

public enum OperationType {
    /**
     * 操作类型
     */
    UNKNOWN("unknown"),
    DELETE("delete"),
    SELECT("select"),
    UPDATE("update"),
    INSERT("insert");

    OperationType(String s) {
        this.value = s;
    }

    private String value;

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }
}

4、创建自定义注解

import java.lang.annotation.*;

@Target({ElementType.PARAMETER, ElementType.METHOD})//作用于参数或方法上
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SystemLog {

    /**
     * 日志名称
     *
     * @return
     */
    String description() default "";

    /**
     * 操作类型
     *
     * @return
     */
    OperationType type() default OperationType.UNKNOWN;
}

5、获取ip的util

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.UnknownHostException;
@Slf4j
@Component
public class IpInfoUtil {

    /**
     * 获取客户端IP地址
     *
     * @param request 请求
     * @return
     */
    public String getIpAddr(HttpServletRequest request) {

        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
            if ("127.0.0.1".equals(ip)) {
                //根据网卡取本机配置的IP
                InetAddress inet = null;
                try {
                    inet = InetAddress.getLocalHost();
                } catch (UnknownHostException e) {
                    e.printStackTrace();
                }
                ip = inet.getHostAddress();
            }
        }
        // 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
        if (ip != null && ip.length() > 15) {
            if (ip.indexOf(",") > 0) {
                ip = ip.substring(0, ip.indexOf(","));
            }
        }
        if ("0:0:0:0:0:0:0:1".equals(ip)) {
            ip = "127.0.0.1";
        }
        return ip;
    }
}

6、线程池util

利用线程异步记录日志。所以直接用了一个util维护线程池。

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ThreadPoolUtil {

    /**
     * 线程缓冲队列
     */
    private static BlockingQueue<Runnable> bqueue = new ArrayBlockingQueue<Runnable>(100);
    /**
     * 核心线程数,会一直存活,即使没有任务,线程池也会维护线程的最少数量
     */
    private static final int SIZE_CORE_POOL = 5;
    /**
     * 线程池维护线程的最大数量
     */
    private static final int SIZE_MAX_POOL = 10;
    /**
     * 线程池维护线程所允许的空闲时间
     */
    private static final long ALIVE_TIME = 2000;

    private static ThreadPoolExecutor pool = new ThreadPoolExecutor(SIZE_CORE_POOL, SIZE_MAX_POOL, ALIVE_TIME, TimeUnit.MILLISECONDS, bqueue, new ThreadPoolExecutor.CallerRunsPolicy());

    static {

        pool.prestartAllCoreThreads();
    }

    public static ThreadPoolExecutor getPool() {
        return pool;
    }

    public static void main(String[] args) {
        System.out.println(pool.getPoolSize());
    }
}

7、HttpServletRequest实现类

这个就是重写的一个HttpServletRequest类。

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;

public class BodyReaderRequestWrapper extends HttpServletRequestWrapper {

    private final String body;

    /**
     * @param request
     */
    public BodyReaderRequestWrapper(HttpServletRequest request) {
        super(request);
        StringBuilder sb = new StringBuilder();
        InputStream ins = null;
        BufferedReader isr = null;
        try {
            ins = request.getInputStream();
            if (ins != null) {
                isr = new BufferedReader(new InputStreamReader(ins));
                char[] charBuffer = new char[128];
                int readCount = 0;
                while ((readCount = isr.read(charBuffer)) != -1) {
                    sb.append(charBuffer, 0, readCount);
                }
            } else {
                sb.append("");
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (isr != null) {
                    isr.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (ins != null) {
                    ins.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        body = sb.toString();
    }

    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(this.getInputStream()));
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        final ByteArrayInputStream byteArrayIns = new ByteArrayInputStream(body.getBytes());
        ServletInputStream servletIns = new ServletInputStream() {
            @Override
            public boolean isFinished() {
                return false;
            }

            @Override
            public boolean isReady() {
                return false;
            }

            @Override
            public void setReadListener(ReadListener readListener) {

            }

            @Override
            public int read() throws IOException {
                return byteArrayIns.read();
            }
        };
        return servletIns;
    }
}

8、添加过滤器

这个过滤器我添加了一个路径,就是代表需要json日志的接口,可以在list当中添加路径,不需要取request当中json数据的可以不配置。

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

public class BodyReaderRequestFilter implements Filter {

    private static final Pattern SHOULD_NOT_FILTER_URL_PATTERN;

    static {
        List<String> urlList = new ArrayList<>();
        // 想要通过aop记录request当中body数据的,就需要进行配置路径
        urlList.add("(socket/.*)");
        urlList.add("(test/test1)");
        urlList.add("(test/test2)");
        StringBuilder sb = new StringBuilder();
        for (String url : urlList) {
            sb.append(url);
            sb.append("|");
        }
        sb.setLength(sb.length() - 1);
        SHOULD_NOT_FILTER_URL_PATTERN = Pattern.compile(sb.toString());
    }

    @Override
    public void init(FilterConfig filterConfig) {

    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;
        // 获取访问的url
        String servletPath = request.getServletPath();
        if (SHOULD_NOT_FILTER_URL_PATTERN.matcher(servletPath).find()) {
            BodyReaderRequestWrapper requestWrapper = new BodyReaderRequestWrapper(request);
            if (requestWrapper == null) {
                filterChain.doFilter(request, response);
            } else {
                filterChain.doFilter(requestWrapper, response);
            }
        }else {
            filterChain.doFilter(request, response);
        }
    }

    @Override
    public void destroy() {

    }
}

想要让过滤器生效需要注入到容器当中。

import cn.org.bjca.szyx.xaas.equipment.filter.BodyReaderRequestFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MyServerConfig {

    @Bean
    public FilterRegistrationBean myFilter(){
        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
        registrationBean.setFilter(new BodyReaderRequestFilter());
        return registrationBean;
    }
}

9、添加AOP核心类

对于切面,我们可以通过指定包名,进行日志统计,也可以选择根据自定义的注解在方法上添加,然后进行统计,根据自己的实际情况,在切点进行配置即可。

LogDao我是没有提供的,每个项目框架不一样,自行根据情况进行编写,就是保存数据库就可以了。

import cn.hutool.core.util.IdUtil;
import cn.hutool.json.JSONUtil;
import cn.org.xaas.core.util.HeaderSecurityUtils;
import cn.org.xaas.equipment.annotation.SystemLog;
import cn.org.xaas.equipment.dao.LogDao;
import cn.org.xaas.equipment.model.base.Log;
import cn.org.xaas.equipment.utils.IpInfoUtil;
import cn.org.xaas.equipment.utils.ThreadPoolUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.NamedThreadLocal;
import org.springframework.stereotype.Component;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Aspect
@Component
@Slf4j
public class SystemLogAspect {

    private static final ThreadLocal<Date> beginTimeThreadLocal = new NamedThreadLocal<Date>("ThreadLocal beginTime");

    @Autowired
    private LogDao logDao;

    @Autowired
    private IpInfoUtil ipInfoUtil;

    @Autowired(required = false)
    private HttpServletRequest request;

    /**
     * Controller层切点,注解方式
     */
    //@Pointcut("execution(* *..controller..*Controller*.*(..))")
    @Pointcut("@annotation(cn.org.xaas.equipment.annotation.SystemLog)")
    public void controllerAspect() {

    }

    /**
     * 前置通知 (在方法执行之前返回)用于拦截Controller层记录用户的操作的开始时间
     *
     * @param joinPoint 切点
     * @throws InterruptedException
     */
    @Before("controllerAspect()")
    public void doBefore(JoinPoint joinPoint) throws InterruptedException {

        //线程绑定变量(该数据只有当前请求的线程可见)
        Date beginTime = new Date();
        beginTimeThreadLocal.set(beginTime);
    }

    /**
     * 后置通知(在方法执行之后并返回数据) 用于拦截Controller层无异常的操作
     *
     * @param joinPoint 切点
     */
    @AfterReturning("controllerAspect()")
    public void after(JoinPoint joinPoint) {
        try {
        	// 获取操作人,每个系统不一样,一般存储与session,此处就不展示了
            String username = HeaderSecurityUtils.getUserName();

            // 读取json数据
            String openApiRequestData = getJSON(request);
            Map<String, String[]> requestParams = request.getParameterMap();

            Log log = new Log();
            if (openApiRequestData != null) {
                log.setRequestJson(JSONUtil.toJsonStr(openApiRequestData));
            }
            log.setId(IdUtil.simpleUUID());
            log.setUsername(username);
            //日志标题
            String description = getControllerMethodInfo(joinPoint).get("description").toString();
            log.setDescription(description);
            //日志类型
            log.setOperationType((int) getControllerMethodInfo(joinPoint).get("type"));
            //日志请求url
            log.setRequestUrl(request.getRequestURI());
            //请求方式
            log.setRequestType(request.getMethod());
            //请求参数
            log.setRequestParam(JSONUtil.toJsonStr(requestParams));

            //其他属性
            log.setIp(ipInfoUtil.getIpAddr(request));
            log.setCreateBy(username);
            log.setUpdateBy(username);
            log.setCreateTime(new Date());
            log.setUpdateTime(new Date());
            log.setDeleteFlag("0");

            //请求开始时间
            long beginTime = beginTimeThreadLocal.get().getTime();
            long endTime = System.currentTimeMillis();
            //请求耗时
            Long logElapsedTime = endTime - beginTime;
            log.setCostTime(logElapsedTime.intValue());

            //持久化(存储到数据或者ES,可以考虑用线程池)
            ThreadPoolUtil.getPool().execute(new SaveSystemLogThread(log, logDao));

        } catch (Exception e) {
            log.error("AOP后置通知异常", e);
        }
    }

    /**
     * 获取request的body
     *
     * @param request
     * @return
     */
    public String getJSON(HttpServletRequest request) {
        ServletInputStream inputStream = null;
        InputStreamReader inputStreamReader = null;
        BufferedReader streamReader = null;
        StringBuilder responseStrBuilder = new StringBuilder();
        try {
            inputStream = request.getInputStream();
            inputStreamReader = new InputStreamReader(inputStream, "UTF-8");
            streamReader = new BufferedReader(inputStreamReader);
            String inputStr;
            while ((inputStr = streamReader.readLine()) != null) {
                responseStrBuilder.append(inputStr);
            }
        } catch (IOException ioException) {
            ioException.printStackTrace();
        } finally {
            try {
                if (inputStream != null) {
                    inputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (inputStreamReader != null) {
                    inputStreamReader.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (streamReader != null) {
                    streamReader.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return responseStrBuilder.toString();
    }

    /**
     * 保存日志至数据库
     */
    private static class SaveSystemLogThread implements Runnable {

        private Log log;
        private LogDao logDao;

        public SaveSystemLogThread(Log esLog, LogDao logDao) {
            this.log = esLog;
            this.logDao = logDao;
        }

        @Override
        public void run() {
            logDao.insert(log);
        }
    }

    /**
     * 获取注解中对方法的描述信息 用于Controller层注解
     *
     * @param joinPoint 切点
     * @return 方法描述
     * @throws Exception
     */
    public static Map<String, Object> getControllerMethodInfo(JoinPoint joinPoint) throws Exception {

        Map<String, Object> map = new HashMap<String, Object>(16);
        //获取目标类名
        String targetName = joinPoint.getTarget().getClass().getName();
        //获取方法名
        String methodName = joinPoint.getSignature().getName();
        //获取相关参数
        Object[] arguments = joinPoint.getArgs();
        //生成类对象
        Class targetClass = Class.forName(targetName);
        //获取该类中的方法
        Method[] methods = targetClass.getMethods();

        String description = "";
        Integer type = null;

        for (Method method : methods) {
            if (!method.getName().equals(methodName)) {
                continue;
            }
            Class[] clazzs = method.getParameterTypes();
            if (clazzs.length != arguments.length) {
                //比较方法中参数个数与从切点中获取的参数个数是否相同,原因是方法可以重载哦
                continue;
            }
            description = method.getAnnotation(SystemLog.class).description();
            type = method.getAnnotation(SystemLog.class).type().ordinal();
            map.put("description", description);
            map.put("type", type);
        }
        return map;
    }

}

10、接口测试

import cn.org.xaas.equipment.annotation.SystemLog;
import cn.org.xaas.equipment.constant.OperationType;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/test")
public class TestController {

    @PostMapping("/test1")
    @SystemLog(description = "根据id查询某某数据",type = OperationType.SELECT)
    public void test1(@RequestParam("id")String id){
        System.out.println(id);
    }

    @PostMapping("/test2")
    @SystemLog(description = "根据id查询某某数据,传json",type = OperationType.SELECT)
    public void test2(@RequestBody String id){
        System.out.println(id);
    }
}

调用第一个测试接口:


调用第二个测试接口:


到此这篇关于springboot利用AOP完成日志统计的文章就介绍到这了,更多相关springboot日志统计内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • springboot aop配合反射统一签名验证实践

    目录 aop配合反射统一签名验证 接口统一签名校验 第一种aop 方式实现 第二种拦截器 aop配合反射统一签名验证 直接上代码,作为记录. CheckSignAspect.java @Aspect //定义一个切面 @Configuration @Log4j2 public class CheckSignAspect { // 定义切点Pointcut @Pointcut("execution(* com.lsj.xxl.controller.*.*CheckSign(..))")

  • SpringBoot AOP @Pointcut切入点表达式排除某些类方式

    目录 SpringBoot AOP @Pointcut切入点表达式排除某些类 场景 使用以下方法满足了开发需求 AOP排除某些类型不拦截 SpringBoot AOP @Pointcut切入点表达式排除某些类 场景 希望给service包下的所有public方法添加开始和结束的info log,但是需要排除和数据库相关的service 其他博文都推荐了 @Pointcut("execution(* com.demo.service.*.*(..)) && !execution(*

  • SpringBoot中创建的AOP不生效的原因及解决

    目录 SpringBoot 创建AOP不生效的原因 SpringBoot aop无效的情况 项目结构 SpringBoot 创建AOP不生效的原因 最近在学习SpringBoot,今天学习了Aop的注册方式,原理很简单,配置也很简单,但是我注册了切面之后切面一直不生效,是为什么呢?查了好久的资料终于发现了原因,可以看下图我的切面注册类并没有问题 然后在网上偶然看到可能是主程序扫描的原因,才发现了原因,可以看到我的显示方式本来是flat的,那样的话就很难找出原因了 修改为hirerchical就可

  • springboot利用AOP完成日志统计的详细步骤

    目录 1.创建日志表 2.创建实体类 3.创建枚举类 4.创建自定义注解 5.获取ip的util 6.线程池util 7.HttpServletRequest实现类 8.添加过滤器 9.添加AOP核心类 10.接口测试 步骤写的很详细,可以直接复制拿来用的,其中用到了过滤器.自定义注解以及AOP切面,来完成日志记录统计,感兴趣的收藏起来,以后遇到了可以直接用. 可能步骤会比较多,但是整体跟着思路下来,应该没什么大问题的. 项目用到了过滤器,可能有的人会不理解,之所以用过滤器是因为想要在日志记录p

  • springboot利用@Aspect实现日志工具类的详细代码

    目录 一.导包 二.在启动类上进行注解自动扫描 三.工具类 四.结果 一.导包 <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.8.12</version> </dependency> <dependency> <groupId>org.aspectj<

  • SpringBoot利用AOP实现一个日志管理详解

    目录 1. 需求 2. 新建一张日志表 3. 写相应的Controller层 4.Service接口层 5.Service实现 6.Mapper接口 7.Mapper.xml(我用的是Mybatis) 8.CspLog 9.实体类SysOperCspLog 10. 定义日志管理的切面 11.AsyncFactoryCsp 12. 写一个Controller的Demo来执行一条日志试试 1. 需求 目前有这么个问题,有两个系统CSP和OMS,这俩系统共用的是同一套日志操作:Log;目前想区分下这俩

  • springboot配置aop切面日志打印过程解析

    这篇文章主要介绍了springboot配置aop切面日志打印过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 一.SpringBoot Aop说明 1. Aop AOP(Aspect-Oriented Programming,面向切面编程),它利用一种"横切"的技术,将那些多个类的共同行为封装到一个可重用的模块.便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性. 2. AOP相关概念: Aspect

  • 解析springboot集成AOP实现日志输出的方法

    开发接口系统中主要的一环就是日志输出,如果系统出现问题,日志能帮我们去定位问题,最常见的日志是调用方 所调用的IP 接口地址 对应方法 参数值 以及接口方接收到请求 所返回的参数.如果这需要在每一个controller层去写的话代码过于重复,于是就使用AOP定义切面 对其接口调用前后进行拦截日志输出. 1.加入依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spr

  • springboot利用aop实现接口异步(进度条)的全过程

    目录 一.前言 二.时序图 三.功能演示 四.关键代码 Controller AsyncAop AsyncService 五.源码地址 总结 一.前言 在项目中发现有接口(excel导入数据)处理数据需要耗时比较长的时间,是因为数据量比较大,同时数据的校验需要耗费一定时间,决定使用一种通用的方法解决这个问题. 解决方案:通过aop使接口异步处理,前端轮询另外一个接口查询进度. 目标: 1接口上一个注解即可实现接口异步(优化:可以通过header参数动态控制是否异步) 2一个方法实现进度条的更新

  • SpringBoot使用AOP统一日志管理的方法详解

    目录 前言 实现 1.引入依赖 2.定义logback配置 3.编写切面类 4.测试 前言 请问今天您便秘了吗?程序员坐久了真的会便秘哦,如果偶然点进了这篇小干货,就麻烦您喝杯水然后去趟厕所一边用左手托起对准嘘嘘,一边用右手滑动手机看完本篇吧. 实现 本篇AOP统一日志管理写法来源于国外知名开源框架JHipster的AOP日志管理方式 1.引入依赖 <!-- spring aop --> <dependency> <groupId>org.springframework

  • Nginx利用Logrotate实现日志分割的详细过程

    目录 前言 Logrotate用法 1.安装 2.基本用法详解 2.1 入门 2.2 分割文件压缩 2.3 按照时间分割 2.4 按照文件大小分割 2.5 自定义每小时分割 2.6 自定义分割执行时间 nginx日志分割步骤 总结 前言 nginx默认没有提供对日志文件的分割功能,所以随着时间的增长,access.log和error.log文件会越来越大,尤其是access.log,其日志记录量比较大,更容易增长文件大小,影响日志写入性能 分割nginx日志的方法有很多,这里推荐利用Logrot

  • 在SpringBoot项目中整合拦截器的详细步骤

    目录 引言 1.创建一个SpringBoot项目工程 2.配置自定义的拦截器 3.注册拦截器 4.编写控制器 总结 引言 拦截器在Web系统中非常常见,对于某些全局统一的操作,我们可以把它提取到拦截器中实现.总结起来,拦截器大致有以下几种使用场景: 1.权限检查:如登录检测,进入处理程序检测用户是否登录,如果没有,则直接返回登录页面或error错误页面: 2.性能检测:有时系统在某段时间莫名其妙很慢,我们可以通过拦截器在进入处理程序之前记录开始时间,在处理完后记录结束时间,从而得到该请求的处理时

  • Linux中一对多配置日志服务器的详细步骤

    目录 配置发送端:从server发送到syslog 配置接收端:配置协议,端口号,接收条件,存储位置 取消注释如下代码: 接收条件配置 创建日志接收模板 结语 配置发送端:从server发送到syslog 打开以下文件 在第90行(附近也行)输入以下代码,authpriv代表所有级别的登录日志,@@代表tcp,172.168.2.100代表目标ip,514代表目标端口号,保存退出 authpriv.* @@10.99.8.6:514 配置接收端:配置协议,端口号,接收条件,存储位置 输入以下代码

随机推荐