【小家Spring】从OncePerRequestFilter的源码解读去了解Spring内置的Filter的特别之处以及常见过滤器使用介绍(下)

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 【小家Spring】从OncePerRequestFilter的源码解读去了解Spring内置的Filter的特别之处以及常见过滤器使用介绍(下)


下面书写一个我们最实用的例子,也是我用在我们所有微服务项目中的一个例子,此处贴出来供以参考:



    @ResponseBody
    @PostMapping(value = "/hello")
    public String helloPost(@RequestBody Map<String, Object> object) {
        //System.out.println(object);
        return "hello...Post";
    }


get请求,不输出日志,来一个put请求如下:


image.png



日志输出如下:


image.png


日志输出里有个小细节,我们看到请求开始里没有payload,输出的时候却有了,怎么回事呢?原因其实是Spring提供的ContentCachingRequestWrapper具有懒加载的特性,所以才会出现这种现象。我个人觉得没什么影响,payload输出一遍反倒是我最希望的效果。至于根本原因,后面的博文会专门分析Spring给我们提供的一些web工具类里,会提到~


贴上我书写的代码,各位若有需要,只需要配置此Filter即可。这个Filter我放在我们公司的私服里,所有的服务都可以直接使用了,若有任何问题,随时留言交流~~

/**
 * 打印请求日志,默认打印get放,只打印POST、PUT、DELETE方法的请求日志哦~
 * 会计算出请求耗时、client请求的ip地址等等  contentType也会记录打印出来 信息比较全 方便查找问题~
 *
 * @author fangshixiang
 * @description
 * @date 2019-02-16 22:04
 */
@Slf4j
@WebFilter(urlPatterns = "/*")
public class RequestLogFilter extends AbstractRequestLoggingFilter {
    //这些配置都可以在init-param中进行设置,但是基于注解的,这里就不要这么麻烦了,统一在初始化的时候设置值吧
    //private boolean includeQueryString = false;
    //private boolean includeClientInfo = false;
    //private boolean includeHeaders = false;
    //private boolean includePayload = false;
    private static final String PROCESS_START_TIME_SUFFIX = ".PROCESS_START_TIME";
    @Override
    protected void initFilterBean() throws ServletException {
        super.setIncludeQueryString(true);
        super.setIncludeClientInfo(true);
        //因为headers里面的信息太多了 所以这里不输出了,否则过于干扰,只手动把content-type等关键信息输出即可
        super.setIncludeHeaders(false);
        super.setIncludePayload(true);
        super.setMaxPayloadLength(1000); //最大支持到1000个字符
        //头信息
        super.setBeforeMessagePrefix("请求开始 [");
        super.setBeforeMessageSuffix("]");
        super.setAfterMessagePrefix("请求结束 [");
        super.setAfterMessageSuffix("]");
    }
    @Override
    protected boolean shouldLog(HttpServletRequest request) {
        String method = request.getMethod();
        return HttpMethod.POST.matches(method) || HttpMethod.PUT.matches(method) || HttpMethod.DELETE.matches(method);
    }
    @Override
    protected void beforeRequest(HttpServletRequest request, String message) {
        logger.info(calcRequestTime(request)
                .concat(getConfigTypeLog(request))
                .concat(getThreadId())
                .concat(message));
    }
    @Override
    protected void afterRequest(HttpServletRequest request, String message) {
        logger.info(calcRequestTime(request)
                .concat(getConfigTypeLog(request))
                .concat(getThreadId())
                .concat(message));
    }
    //拼装contentType
    private String getConfigTypeLog(HttpServletRequest request) {
        String contentType = request.getContentType();
        String method = request.getMethod();
        return "[contentType=" + contentType + "] " + method.toUpperCase() + " ";
    }
    //计算请求耗时
    private String calcRequestTime(HttpServletRequest request) {
        long mills = 0;
        String requestTimeUniqueName = getRequestTimeUniqueName();
        Object processStartTime = request.getAttribute(requestTimeUniqueName);
        if (processStartTime == null) { //首次 放置值
            request.setAttribute(requestTimeUniqueName, Instant.now());
        } else { //请求结束的处理  开始计算吧
            Instant start = (Instant) processStartTime;
            Instant now = Instant.now();
            mills = Duration.between(start, now).toMillis();
            request.removeAttribute(requestTimeUniqueName);
        }
        return mills == 0 ? "" : ("[耗时:" + mills + "ms] ");
    }
    private String getRequestTimeUniqueName() {
        return this.getClass().getName().concat(PROCESS_START_TIME_SUFFIX);
    }
    private String getThreadId() {
        return "[ThreadId:" + Thread.currentThread().getId() + "] ";
    }
}


备注:此Filter自己定制日志。若你结束请求时还想输出response里面的的内容。比如状态码,返回body里面的内容等等(其实我倒觉得还挺重要的),自己可以去加工实现。


这里比较坑爹的是,Spring提供给我们复写的两个方法,都没返回给我们response,所以若有需要,各位小伙伴自己实现哈。我在我项目中有实现,每个人实现方式不一样,这里就不贴出这部分源码了哈~


此请求日志Filter一般放在TokenFilter后面执行(如果业务特殊,放在前面执行也可)


拦截器(Interceptor)是基于Java的反射机制,而过滤器(Filter)是基于函数回调


最后


Spring作为当下实际的Java EE标准,非常的流行,设计也是非常的好。因此熟练的掌握Spring为我们提供的一些实用类,很多时候能够让我们事半功倍~

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
2月前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
110 2
|
2月前
|
数据采集 监控 前端开发
二级公立医院绩效考核系统源码,B/S架构,前后端分别基于Spring Boot和Avue框架
医院绩效管理系统通过与HIS系统的无缝对接,实现数据网络化采集、评价结果透明化管理及奖金分配自动化生成。系统涵盖科室和个人绩效考核、医疗质量考核、数据采集、绩效工资核算、收支核算、工作量统计、单项奖惩等功能,提升绩效评估的全面性、准确性和公正性。技术栈采用B/S架构,前后端分别基于Spring Boot和Avue框架。
114 5
|
14天前
|
监控 JavaScript 数据可视化
建筑施工一体化信息管理平台源码,支持微服务架构,采用Java、Spring Cloud、Vue等技术开发。
智慧工地云平台是专为建筑施工领域打造的一体化信息管理平台,利用大数据、云计算、物联网等技术,实现施工区域各系统数据汇总与可视化管理。平台涵盖人员、设备、物料、环境等关键因素的实时监控与数据分析,提供远程指挥、决策支持等功能,提升工作效率,促进产业信息化发展。系统由PC端、APP移动端及项目、监管、数据屏三大平台组成,支持微服务架构,采用Java、Spring Cloud、Vue等技术开发。
|
3月前
|
Java API Spring
在 Spring 配置文件中配置 Filter 的步骤
【10月更文挑战第21天】在 Spring 配置文件中配置 Filter 是实现请求过滤的重要手段。通过合理的配置,可以灵活地对请求进行处理,满足各种应用需求。还可以根据具体的项目要求和实际情况,进一步深入研究和优化 Filter 的配置,以提高应用的性能和安全性。
|
3月前
|
搜索推荐 Java Spring
Spring Filter深度解析
【10月更文挑战第21天】Spring Filter 是 Spring 框架中非常重要的一部分,它为请求处理提供了灵活的控制和扩展机制。通过合理配置和使用 Filter,可以实现各种个性化的功能,提升应用的安全性、可靠性和性能。还可以结合具体的代码示例和实际应用案例,进一步深入探讨 Spring Filter 的具体应用和优化技巧,使对它的理解更加全面和深入。
|
1月前
|
JavaScript Java Kotlin
深入 Spring Cloud Gateway 过滤器
Spring Cloud Gateway 是新一代微服务网关框架,支持多种过滤器实现。本文详解了 `GlobalFilter`、`GatewayFilter` 和 `AbstractGatewayFilterFactory` 三种过滤器的实现方式及其应用场景,帮助开发者高效利用这些工具进行网关开发。
259 1
|
1月前
|
存储 缓存 Java
Spring面试必问:手写Spring IoC 循环依赖底层源码剖析
在Spring框架中,IoC(Inversion of Control,控制反转)是一个核心概念,它允许容器管理对象的生命周期和依赖关系。然而,在实际应用中,我们可能会遇到对象间的循环依赖问题。本文将深入探讨Spring如何解决IoC中的循环依赖问题,并通过手写源码的方式,让你对其底层原理有一个全新的认识。
65 2
|
2月前
|
前端开发 Java 开发者
Spring生态学习路径与源码深度探讨
【11月更文挑战第13天】Spring框架作为Java企业级开发中的核心框架,其丰富的生态系统和强大的功能吸引了无数开发者的关注。学习Spring生态不仅仅是掌握Spring Framework本身,更需要深入理解其周边组件和工具,以及源码的底层实现逻辑。本文将从Spring生态的学习路径入手,详细探讨如何系统地学习Spring,并深入解析各个重点的底层实现逻辑。
79 9
|
3月前
|
Java Spring
Spring底层架构源码解析(三)
Spring底层架构源码解析(三)
215 5
|
3月前
|
XML Java 数据格式
Spring底层架构源码解析(二)
Spring底层架构源码解析(二)