0%

springMvc自动注册api

前言

懒得自己手写api
干脆让他根据包名、类名 函数名自动生成算了

思路

  • 自定义注解 注册到spring ioc容器中
  • 借助原本requestMapping及其衍生的注解来为接口除了地址以外的属性做处理 如method consumer 等参数
  • 使用RequestMappingHandlerMapping来搭配 相关注解 来构建 api

实战

注解
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.ming.base.mvc.annotation;

import org.springframework.core.annotation.AliasFor;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.ResponseBody;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* 自动控制器
*
* @author ming
* @date 2021-10-09 09:45:12
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
@ResponseBody
public @interface AutoController {

@AliasFor(annotation = Component.class)
String value() default "";
}
自动注册
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
package com.ming.base.mvc;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.ming.base.mvc.annotation.AutoController;
import com.ming.core.utils.JSONSingleton;
import com.ming.core.utils.MyStringUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import javax.annotation.PostConstruct;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.List;

/**
* 自动初始化controller
* 只在指定包名 && RestController注解下的 bean才自动注册
*
* @author ming
* @date 2021-09-29 15:49:47
*/
@Component
@Slf4j
public class ControllerAutoRegister implements ApplicationContextAware {

/**
* 包名前缀
*/
private static String PREFIX_PACKAGE_NAME = "com.ming.controller";
private static ImmutableList<String> DEFAULT_METHOD_NAME_LIST = ImmutableList.<String>builder()
.add("view")
.add("delete")
.add("save")
.build();
/**
* spring bean上下文
*
* @author ming
* @date 11:00
*/
private ApplicationContext applicationContext;
@Autowired
private RequestMappingHandlerMapping requestMappingHandlerMapping;

@PostConstruct
public void init() {
log.info("auto register api......");
//扫描所有的restController
List<String> beanNameList = Lists.newArrayList(applicationContext.getBeanNamesForAnnotation(AutoController.class));
log.info("scan rest controller number:{}", beanNameList.size());
//删除非指定前缀的 bean
beanNameList.removeIf(r -> !applicationContext.getBean(r).getClass().getPackageName().startsWith(PREFIX_PACKAGE_NAME));
log.info("register controller number:{}", beanNameList.size());
for (String beanName : beanNameList) {
registerMapping(beanName);
}
}

private boolean registerMapping(String beanName) {
Object obj = applicationContext.getBean(beanName);
Class<?> objClass = obj.getClass();
List<Method> methodList = Lists.newArrayList(objClass.getDeclaredMethods());
//删除静态函数
methodList.removeIf(r -> Modifier.isStatic(r.getModifiers()));
//删除私有函数
methodList.removeIf(r -> Modifier.isPrivate(r.getModifiers()));

String classSimpleName = objClass.getSimpleName().replace("Controller", "").replace("Entity", "");
//构建包路径
String prefix = "/api" + objClass.getPackageName().replace(PREFIX_PACKAGE_NAME, "").replace(".", "/");
//构建class路径
prefix = prefix + "/" + MyStringUtils.toLowerCaseJoiner(classSimpleName, "-");
for (Method method : methodList) {
//构建mapping
RequestMethod[] requestMethods = buildRequestMethod(method);
String path = prefix;
if (!DEFAULT_METHOD_NAME_LIST.contains(method.getName())) {
path = path + "/" + MyStringUtils.toLowerCaseJoiner(method.getName(), "-");
}
//按需实现对应的 mapping 注册即可 这里可以自由调整
RequestMappingInfo.Builder requestMappingInfoBuilder = RequestMappingInfo
.paths(path)
.produces(MediaType.APPLICATION_JSON_VALUE)
.methods(requestMethods);

RequestMappingInfo requestMappingInfo = requestMappingInfoBuilder.build();
requestMappingHandlerMapping.registerMapping(requestMappingInfo, beanName, method);
log.info("register api :【{}】{},{}", StringUtils.join(requestMethods, ","), JSONSingleton.writeString(requestMappingInfo.getDirectPaths()), objClass.getName() + "#" + method.getName());
}
return true;
}

/**
* 构建请求参数
*
* @param method
* @author ming
* @date 2021-10-09 15:03:43
*/
private RequestMethod[] buildRequestMethod(Method method) {
RequestMapping requestMapping = AnnotationUtils.findAnnotation(method, RequestMapping.class);
if (requestMapping != null) {
return requestMapping.method();
} else {
return new RequestMethod[]{RequestMethod.GET};
}
}

@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}

使用方式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package com.ming.controller;

import com.ming.base.mvc.annotation.AutoController;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@AutoController
public class TestController {

public String get() {
return "xxxxxxxxxxxxxxxxx";
}


@PutMapping
public String put() {
return "nnnnnn";
}

@PostMapping
public String post() {
return "nnnnnn";
}

@PatchMapping
public String patch() {
return "nnnn";
}

@DeleteMapping
public String delete() {
return "nnnn";
}

@RequestMapping(method = RequestMethod.OPTIONS)
public String options() {
return "nnnnn";
}
}
查看注册api列表

直接看输出日志

2021-10-11 11:12:35.433 [restartedMain] INFO  com.ming.base.mvc.ControllerAutoRegister- auto register api......
2021-10-11 11:12:35.439 [restartedMain] INFO  com.ming.base.mvc.ControllerAutoRegister- scan rest controller number:1
2021-10-11 11:12:36.257 [restartedMain] INFO  com.ming.base.mvc.ControllerAutoRegister- register controller number:1
2021-10-11 11:12:36.284 [restartedMain] INFO  com.ming.base.mvc.ControllerAutoRegister- register api :【GET】["/api/test/get"],com.ming.controller.TestController#get
2021-10-11 11:12:36.284 [restartedMain] INFO  com.ming.base.mvc.ControllerAutoRegister- register api :【PUT】["/api/test/put"],com.ming.controller.TestController#put
2021-10-11 11:12:36.285 [restartedMain] INFO  com.ming.base.mvc.ControllerAutoRegister- register api :【DELETE】["/api/test"],com.ming.controller.TestController#delete
2021-10-11 11:12:36.285 [restartedMain] INFO  com.ming.base.mvc.ControllerAutoRegister- register api :【OPTIONS】["/api/test/options"],com.ming.controller.TestController#options
2021-10-11 11:12:36.285 [restartedMain] INFO  com.ming.base.mvc.ControllerAutoRegister- register api :【PATCH】["/api/test/patch"],com.ming.controller.TestController#patch
2021-10-11 11:12:36.286 [restartedMain] INFO  com.ming.base.mvc.ControllerAutoRegister- register api :【POST】["/api/test/post"],com.ming.controller.TestController#post

总结

为了偷懒 直接依托于class的 包名 类名 函数名来避免重复 构建api
免得自己去自定义使用
要用自动构建就用指定注解 要用mvc的标准注解 也可以接着使用 互不影响
方便开发