Skip to content

Commit 21649fc

Browse files
committed
feat: implement custom @TimestampRequestBody annotation to enhance request body processing with timestamp injection
1 parent 1908309 commit 21649fc

File tree

9 files changed

+219
-0
lines changed

9 files changed

+219
-0
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
## Decorator Pattern
2+
3+
## 介绍
4+
5+
实现一个自定义注解 `@TimestampRequestBody`,用于拦截并增强 `@RequestBody` 注解解析过程,
6+
在反序列化完成后,自动为请求体对象注入当前时间戳字段,无需客户端显式传递。
7+
8+
## 参考
9+
10+
> [Decorator Pattern](https://refactoringguru.cn/design-patterns/decorator)
11+
> [装饰模式](https://www.bilibili.com/video/BV1bgPBesENK)
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
7+
<parent>
8+
<groupId>com.helltractor.demo</groupId>
9+
<artifactId>pattern-design</artifactId>
10+
<version>1.0-SNAPSHOT</version>
11+
<relativePath>../pom.xml</relativePath>
12+
</parent>
13+
14+
<artifactId>pattern-decorator</artifactId>
15+
16+
<dependencies>
17+
<dependency>
18+
<groupId>org.springframework.boot</groupId>
19+
<artifactId>spring-boot-starter-web</artifactId>
20+
<version>3.4.1</version>
21+
</dependency>
22+
</dependencies>
23+
24+
</project>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.helltractor.demo;
2+
3+
import org.springframework.boot.SpringApplication;
4+
import org.springframework.boot.autoconfigure.SpringBootApplication;
5+
6+
@SpringBootApplication
7+
public class Application {
8+
9+
public static void main(String[] args) {
10+
SpringApplication.run(Application.class, args);
11+
}
12+
13+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package com.helltractor.demo.config;
2+
3+
import org.springframework.beans.factory.SmartInitializingSingleton;
4+
import org.springframework.beans.factory.annotation.Autowired;
5+
import org.springframework.stereotype.Component;
6+
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
7+
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
8+
import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor;
9+
10+
import java.util.ArrayList;
11+
import java.util.List;
12+
13+
@Component
14+
public class MyArgumentResolverConfigurer implements SmartInitializingSingleton {
15+
16+
@Autowired
17+
private RequestMappingHandlerAdapter requestMappingHandlerAdapter;
18+
19+
@Override
20+
public void afterSingletonsInstantiated() {
21+
List<HandlerMethodArgumentResolver> argumentResolvers = requestMappingHandlerAdapter.getArgumentResolvers();
22+
List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>(argumentResolvers.size() + 1);
23+
for (HandlerMethodArgumentResolver resolver : argumentResolvers) {
24+
resolvers.add(resolver);
25+
if (resolver instanceof RequestResponseBodyMethodProcessor) {
26+
resolvers.add(new TimestampRequestBodyMethodProcess(
27+
(RequestResponseBodyMethodProcessor) resolver));
28+
}
29+
}
30+
requestMappingHandlerAdapter.setArgumentResolvers(resolvers);
31+
}
32+
33+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package com.helltractor.demo.config;
2+
3+
import org.springframework.beans.factory.annotation.Autowired;
4+
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
5+
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
6+
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
7+
import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor;
8+
9+
import java.util.List;
10+
11+
/**
12+
* This class will cause a circular dependency, instead, use MyArgumentResolverConfigurer.
13+
*/
14+
// @Configuration
15+
public class MyWebMvcConfigurer implements WebMvcConfigurer {
16+
17+
@Autowired
18+
private RequestMappingHandlerAdapter requestMappingHandlerAdapter;
19+
20+
@Override
21+
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
22+
List<HandlerMethodArgumentResolver> argumentResolvers = requestMappingHandlerAdapter.getArgumentResolvers();
23+
for (HandlerMethodArgumentResolver resolver : argumentResolvers) {
24+
if (resolver instanceof RequestResponseBodyMethodProcessor) {
25+
resolvers.add(new TimestampRequestBodyMethodProcess(
26+
(RequestResponseBodyMethodProcessor) resolver));
27+
return;
28+
}
29+
}
30+
}
31+
32+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.helltractor.demo.config;
2+
3+
import java.lang.annotation.Documented;
4+
import java.lang.annotation.ElementType;
5+
import java.lang.annotation.Retention;
6+
import java.lang.annotation.RetentionPolicy;
7+
import java.lang.annotation.Target;
8+
9+
@Target({ElementType.PARAMETER})
10+
@Retention(RetentionPolicy.RUNTIME)
11+
@Documented
12+
public @interface TimestampRequestBody {
13+
14+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package com.helltractor.demo.config;
2+
3+
import org.springframework.core.MethodParameter;
4+
import org.springframework.web.bind.support.WebDataBinderFactory;
5+
import org.springframework.web.context.request.NativeWebRequest;
6+
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
7+
import org.springframework.web.method.support.ModelAndViewContainer;
8+
import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor;
9+
10+
import java.util.Map;
11+
12+
public class TimestampRequestBodyMethodProcess implements HandlerMethodArgumentResolver {
13+
14+
private final RequestResponseBodyMethodProcessor processor;
15+
16+
TimestampRequestBodyMethodProcess(RequestResponseBodyMethodProcessor processor) {
17+
this.processor = processor;
18+
}
19+
20+
@Override
21+
public boolean supportsParameter(MethodParameter parameter) {
22+
return parameter.hasParameterAnnotation(TimestampRequestBody.class);
23+
}
24+
25+
@Override
26+
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
27+
28+
Object result = processor.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
29+
if (!(result instanceof Map<?, ?>)) {
30+
return result;
31+
}
32+
((Map) result).put("timestamp", System.currentTimeMillis());
33+
return result;
34+
}
35+
36+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package com.helltractor.demo.controller;
2+
3+
import com.helltractor.demo.config.TimestampRequestBody;
4+
import org.springframework.web.bind.annotation.PostMapping;
5+
import org.springframework.web.bind.annotation.RequestBody;
6+
import org.springframework.web.bind.annotation.RequestMapping;
7+
import org.springframework.web.bind.annotation.RestController;
8+
9+
import java.util.Map;
10+
11+
@RestController
12+
@RequestMapping("/api")
13+
public class MyController {
14+
15+
@PostMapping("/")
16+
public Map<String, Object> origin(@RequestBody Map<String, Object> request) {
17+
return request;
18+
}
19+
20+
@PostMapping("/timestamp")
21+
public Map<String, Object> originWithTimestamp(@TimestampRequestBody Map<String, Object> request) {
22+
return request;
23+
}
24+
25+
}

pattern-design/pom.xml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
<module>pattern-proxy</module>
1414
<module>pattern-command</module>
1515
<module>pattern-memento</module>
16+
<module>pattern-decorator</module>
1617
</modules>
1718

1819
<properties>
@@ -24,4 +25,34 @@
2425
<mysql.version>8.1.0</mysql.version>
2526
</properties>
2627

28+
<build>
29+
<plugins>
30+
<plugin>
31+
<groupId>org.codehaus.mojo</groupId>
32+
<artifactId>flatten-maven-plugin</artifactId>
33+
<version>1.1.0</version>
34+
<configuration>
35+
<updatePomFile>true</updatePomFile>
36+
<flattenMode>resolveCiFriendliesOnly</flattenMode>
37+
</configuration>
38+
<executions>
39+
<execution>
40+
<id>flatten</id>
41+
<phase>process-resources</phase>
42+
<goals>
43+
<goal>flatten</goal>
44+
</goals>
45+
</execution>
46+
<execution>
47+
<id>flatten.clean</id>
48+
<phase>clean</phase>
49+
<goals>
50+
<goal>clean</goal>
51+
</goals>
52+
</execution>
53+
</executions>
54+
</plugin>
55+
</plugins>
56+
</build>
57+
2758
</project>

0 commit comments

Comments
 (0)