请不要自己写,Spring Boot非常实用的内置功能
在 Spring Boot 框架中,内置了许多实用的功能,这些功能可以帮助开发者高效地开发和维护应用程序。
松哥来和大家列举几个。
一 请求数据记录
Spring Boot提供了一个内置的日志记录解决方案,通过 AbstractRequestLoggingFilter 可以记录请求的详细信息。
AbstractRequestLoggingFilter 有两个不同的实现类,我们常用的是 CommonsRequestLoggingFilter。
通过 CommonsRequestLoggingFilter 开发者可以自定义记录请求的参数、请求体、请求头和客户端信息。
启用方式很简单,加个配置就行了:
@Configuration
public class RequestLoggingConfig {
@Bean
public CommonsRequestLoggingFilter logFilter() {
CommonsRequestLoggingFilter filter = new CommonsRequestLoggingFilter();
filter.setIncludeQueryString(true);
filter.setIncludePayload(true);
filter.setIncludeHeaders(true);
filter.setIncludeClientInfo(true);
filter.setAfterMessagePrefix("REQUEST DATA-");
return filter;
}
}
接下来需要配置日志级别为 DEBUG,就可以详细记录请求信息:
logging.level.org.springframework.web.filter.CommonsRequestLoggingFilter=DEBUG
二 请求/响应包装器
2.1 什么是请求和响应包装器
在 Spring Boot 中,请求和响应包装器是用于增强原生 HttpServletRequest 和 HttpServletResponse 对象的功能。这些包装器允许开发者在请求处理过程中拦截和修改请求和响应数据,从而实现一些特定的功能,如请求内容的缓存、修改、日志记录,以及响应内容的修改和增强。
请求包装器
ContentCachingRequestWrapper:这是 Spring 提供的一个请求包装器,用于缓存请求的输入流。它允许多次读取请求体,这在需要多次处理请求数据(如日志记录和业务处理)时非常有用。
响应包装器
ContentCachingResponseWrapper:这是 Spring 提供的一个响应包装器,用于缓存响应的输出流。它允许开发者在响应提交给客户端之前修改响应体,这在需要对响应内容进行后处理(如添加额外的头部信息、修改响应体)时非常有用。
2.2 使用场景
- 请求日志记录:在处理请求之前和之后记录请求的详细信息,包括请求头、请求参数和请求体。
- 修改请求数据:在请求到达控制器之前修改请求数据,例如添加或修改请求头。
- 响应内容修改:在响应发送给客户端之前修改响应内容,例如添加或修改响应头,或者对响应体进行签名。
- 性能测试:通过缓存请求和响应数据,可以进行性能测试,而不影响实际的网络 I/O 操作。
2.3 具体用法
请求包装器的使用
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component
public class RequestWrapperFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper(request);
// 可以在这里处理请求数据
byte[] body = requestWrapper.getContentAsByteArray();
// 处理body,例如记录日志
//。。。
filterChain.doFilter(requestWrapper, response);
}
}
响应包装器的使用
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component
public class ResponseWrapperFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(response);
filterChain.doFilter(request, responseWrapper);
// 可以在这里处理响应数据
byte[] body = responseWrapper.getContentAsByteArray();
// 处理body,例如添加签名
responseWrapper.setHeader("X-Signature", "some-signature");
// 必须调用此方法以将响应数据发送到客户端
responseWrapper.copyBodyToResponse();
}
}
在上面的案例中,OncePerRequestFilter 确保过滤器在一次请求的生命周期中只被调用一次,这对于处理请求和响应数据尤为重要,因为它避免了在请求转发或包含时重复处理数据。
通过使用请求和响应包装器,开发者可以在不改变原有业务逻辑的情况下,灵活地添加或修改请求和响应的处理逻辑。
三 单次过滤器
3.1 OncePerRequestFilter
OncePerRequestFilter 是 Spring 框架提供的一个过滤器基类,它继承自 Filter 接口。这个过滤器具有以下特点:
-
单次执行 :
OncePerRequestFilter确保在一次请求的生命周期内,无论请求如何转发(forwarding)或包含(including),过滤器逻辑只执行一次。这对于避免重复处理请求或响应非常有用。 -
内置支持:它内置了对请求和响应包装器的支持,使得开发者可以方便地对请求和响应进行包装和处理。
-
简化代码 :通过继承
OncePerRequestFilter,开发者可以减少重复代码,因为过滤器的执行逻辑已经由基类管理。 -
易于扩展 :开发者可以通过重写
doFilterInternal方法来实现自己的过滤逻辑,而不需要关心过滤器的注册和执行次数。
3.2 OncePerRequestFilter 使用场景
-
请求日志记录:在请求处理之前和之后记录请求的详细信息,如请求头、请求参数和请求体,而不希望在请求转发时重复记录。
-
请求数据修改:在请求到达控制器之前,对请求数据进行预处理或修改,例如添加或修改请求头,而不希望这些修改在请求转发时被重复应用。
-
响应数据修改:在响应发送给客户端之前,对响应数据进行后处理或修改,例如添加或修改响应头,而不希望这些修改在请求包含时被重复应用。
-
安全控制:实现安全控制逻辑,如身份验证、授权检查等,确保这些逻辑在一次请求的生命周期内只执行一次。
-
请求和响应的包装 :使用
ContentCachingRequestWrapper和ContentCachingResponseWrapper等包装器来缓存请求和响应数据,以便在请求处理过程中多次读取或修改数据。 -
性能监控:在请求处理前后进行性能监控,如记录处理时间,而不希望这些监控逻辑在请求转发时被重复执行。
-
异常处理:在请求处理过程中捕获和处理异常,确保异常处理逻辑只执行一次,即使请求被转发到其他处理器。
通过使用 OncePerRequestFilter,开发者可以确保过滤器逻辑在一次请求的生命周期内只执行一次,从而避免重复处理和潜在的性能问题。这使得 OncePerRequestFilter 成为处理复杂请求和响应逻辑时的一个非常有用的工具。
OncePerRequestFilter 的具体用法松哥就不举例了,第二小节已经介绍过了。
四 AOP 三件套
在 Spring 框架中,AOP(面向切面编程)是一个强大的功能,它允许开发者在不修改源代码的情况下,对程序的特定部分进行横向切入。AopContext、AopUtils 和 ReflectionUtils 是 Spring AOP 中提供的几个实用类。
我们一起来看下。
4.1 AopContext
AopContext 是 Spring 框架中的一个类,它提供了对当前 AOP 代理对象的访问,以及对目标对象的引用。
AopContext 主要用于获取当前代理对象的相关信息,以及在 AOP 代理中进行一些特定的操作。
常见方法有两个:
getTargetObject(): 获取当前代理的目标对象。currentProxy(): 获取当前的代理对象。
其中第二个方法,在防止同一个类中注解失效的时候,可以通过该方法获取当前类的代理对象。
举个栗子:
public void noTransactionTask(String keyword){ // 注意这里 调用了代理类的方法
((YourClass) AopContext.currentProxy()).transactionTask(keyword);
}
@Transactional
void transactionTask(String keyword) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) { //logger
//error tracking
}
System.out.println(keyword);
}
同一个类中两个方法,noTransactionTask 方法调用 transactionTask 方法,为了使事务注解不失效,就可以使用 AopContext.currentProxy() 去获取当前代理对象。
4.2 AopUtils
AopUtils 提供了一些静态方法来处理与 AOP 相关的操作,如获取代理对象、获取目标对象、判断代理类型等。
常见方法有三个:
getTargetObject(): 从代理对象中获取目标对象。isJdkDynamicProxy(Object obj): 判断是否是 JDK 动态代理。isCglibProxy(Object obj): 判断是否是 CGLIB 代理。
举个栗子:
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.aop.support.AopUtils;
public class AopUtilsExample {
public static void main(String[] args) {
MyService myService = ...
// 假设 myService 已经被代理
if (AopUtils.isCglibProxy(myService)) {
System.out.println("这是一个 CGLIB 代理对象");
}
}
}
4.3 ReflectionUtils
ReflectionUtils 提供了一系列反射操作的便捷方法,如设置字段值、获取字段值、调用方法等。这些方法封装了 Java 反射 API 的复杂性,使得反射操作更加简单和安全。
常见方法:
makeAccessible(Field field): 使私有字段可访问。getField(Field field, Object target): 获取对象的字段值。invokeMethod(Method method, Object target, Object... args): 调用对象的方法。
举个栗子:
import org.springframework.util.ReflectionUtils;
import java.lang.reflect.Field;
import java.util.Map;
public class ReflectionUtilsExample {
public static void main(String[] args) throws Exception {
ExampleBean bean = new ExampleBean();
bean.setMapAttribute(new HashMap<>());
Field field = ReflectionUtils.findField(ExampleBean.class, "mapAttribute");
ReflectionUtils.makeAccessible(field);
Object value = ReflectionUtils.getField(field, bean);
System.out.println(value);
}
static class ExampleBean {
private Map<String, String> mapAttribute;
public void setMapAttribute(Map<String, String> mapAttribute) {
this.mapAttribute = mapAttribute;
}
}
}
还有哪些实用内置类呢?欢迎小伙伴们留言~
关注公众号
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
-
上一篇
可观测性实战:从拨云见日到抽丝剥茧快速定位业务响应时延高问题
本文分享借助 DeepFlow 在某头部劵商业务压测场景中通过调用链追踪快速定位问题的过程。解决在容器云内等复杂调用场景中解决传统监控手段覆盖不全面、排障定位无手段等痛点。分享利用 DeepFlow 如何快速在复杂的业务调用过程中抽丝剥茧,快速排除网络问题,定位Pod服务自身业务逻辑问题,展现 DeepFlow 产品价值。 背景介绍 某头部券商OCR识别业务压测多个后端服务Pod时,偶发性业务响应时延高问题,当出现故障时集中某一Pod出现时延3s以上,其他Pod业务响应正常。 客户在内部已经建立了多套容器环境,随着业务规模的增长和技术架构的演进,也面临了若干运维痛点: • 基础设施监控手段限制导致的排障困难:由于无法采用实时插庄监控全栈链路,基础设施团队不得不依赖日志分析来逐步排查问题,这种方法显著延长了故障诊断的时间。 • 监控数据粒度不足导致追踪能力受限:现有的传统埋点技术提供的监控数据过于粗略,难以实现对基础服务、云组件和网络调用的深入追踪,影响了问题定位的准确性。 • 云原生环境下的网络诊断难题:在复杂的云原生环境中,Kubernetes的内置诊断工具效率不高,缺乏专门针对网络...
-
下一篇
使用 Elastic 和 Mistral 构建多语言 RAG(一)
作者:来自 ElasticGustavo Llermaly 使用 Elastic 和 Mixtral 8x22B 模型构建多语言 RAG 应用程序。 Mixtral 8x22B 是性能最高的开放式模型,其最强大的功能之一是能够流利使用多种语言;包括英语、西班牙语、法语、意大利语和德语。 想象一下,一家跨国公司拥有不同语言的支持单和解决方案,并希望跨部门利用这些知识。目前,知识仅限于代理使用的语言。让我们解决这个问题! 在本文中,将向你展示如何通过创建多语言 RAG 系统来测试 Mixtral 的语言能力。 你可以按照此笔记本在此处复现本文的示例。 如果你想在自己的自托管 Elasticsearch 集群中完成下面的展示,请详细阅读文章 “使用 Elastic 和 Mistral 构建多语言 RAG(二)”。 步骤 创建嵌入端点 创建映射 索引数据 提出问题 创建嵌入端点 我们针对此示例的支持票(suport tickets)将以英语、西班牙语和德语提供。Mistral 嵌入模型不是多语言的(嵌入模型和大模型还不是一回事!),但我们可以使用 e5 模型生成多语言嵌入,因此我们可以索引不同...
相关文章
文章评论
共有0条评论来说两句吧...



微信收款码
支付宝收款码