|
@@ -7,6 +7,7 @@ import org.springframework.web.bind.annotation.RequestMethod;
|
|
|
import org.springframework.web.context.request.NativeWebRequest;
|
|
|
import org.springframework.web.context.request.RequestAttributes;
|
|
|
import org.springframework.web.context.request.ServletWebRequest;
|
|
|
+import org.springframework.web.method.HandlerMethod;
|
|
|
import org.springframework.web.servlet.HandlerMapping;
|
|
|
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
|
|
|
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
|
|
@@ -15,10 +16,7 @@ import org.ssssssss.magicapi.provider.ApiServiceProvider;
|
|
|
import javax.servlet.http.HttpServletRequest;
|
|
|
import javax.servlet.http.HttpServletResponse;
|
|
|
import java.lang.reflect.Method;
|
|
|
-import java.util.ArrayList;
|
|
|
-import java.util.Collections;
|
|
|
-import java.util.List;
|
|
|
-import java.util.Map;
|
|
|
+import java.util.*;
|
|
|
import java.util.concurrent.ConcurrentHashMap;
|
|
|
|
|
|
/**
|
|
@@ -26,205 +24,228 @@ import java.util.concurrent.ConcurrentHashMap;
|
|
|
*/
|
|
|
public class MappingHandlerMapping {
|
|
|
|
|
|
- /**
|
|
|
- * 已缓存的映射信息
|
|
|
- */
|
|
|
- private static Map<String, ApiInfo> mappings = new ConcurrentHashMap<>();
|
|
|
-
|
|
|
- private static Logger logger = LoggerFactory.getLogger(MappingHandlerMapping.class);
|
|
|
-
|
|
|
- /**
|
|
|
- * spring中的请求映射处理器
|
|
|
- */
|
|
|
- private RequestMappingHandlerMapping requestMappingHandlerMapping;
|
|
|
-
|
|
|
- /**
|
|
|
- * 请求处理器
|
|
|
- */
|
|
|
- private RequestHandler handler;
|
|
|
-
|
|
|
- /**
|
|
|
- * 请求到达时处理的方法
|
|
|
- */
|
|
|
- private Method method = RequestHandler.class.getDeclaredMethod("invoke", HttpServletRequest.class, HttpServletResponse.class, Map.class, Map.class);
|
|
|
-
|
|
|
- /**
|
|
|
- * 接口信息读取
|
|
|
- */
|
|
|
- private ApiServiceProvider magicApiService;
|
|
|
- /**
|
|
|
- * 统一接口前缀
|
|
|
- */
|
|
|
- private String prefix;
|
|
|
-
|
|
|
- /**
|
|
|
- * 缓存已映射的接口信息
|
|
|
- */
|
|
|
- private List<ApiInfo> apiInfos = Collections.synchronizedList(new ArrayList<>());
|
|
|
-
|
|
|
- public MappingHandlerMapping() throws NoSuchMethodException {
|
|
|
- }
|
|
|
-
|
|
|
- public void setPrefix(String prefix) {
|
|
|
- this.prefix = prefix;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 根据request获取对应的接口信息
|
|
|
- */
|
|
|
- public static ApiInfo getMappingApiInfo(HttpServletRequest request) {
|
|
|
- NativeWebRequest webRequest = new ServletWebRequest(request);
|
|
|
- // 找到注册的路径
|
|
|
- String requestMapping = (String) webRequest.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);
|
|
|
- // 根据请求方法和路径获取接口信息
|
|
|
- return getMappingApiInfo(buildMappingKey(request.getMethod(), requestMapping));
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 根据绑定的key获取接口信息
|
|
|
- */
|
|
|
- public static ApiInfo getMappingApiInfo(String key) {
|
|
|
- return mappings.get(key);
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 构建缓存map的key
|
|
|
- *
|
|
|
- * @param requestMethod 请求方法
|
|
|
- * @param requestMapping 请求路径
|
|
|
- * @return
|
|
|
- */
|
|
|
- public static String buildMappingKey(String requestMethod, String requestMapping) {
|
|
|
- //TODO 判断 requestMapping 是否已 “/” 开头
|
|
|
- if (!StringUtils.isEmpty(requestMapping) && !requestMapping.startsWith("/")) {
|
|
|
- requestMapping = "/" + requestMapping;
|
|
|
- }
|
|
|
- return requestMethod.toUpperCase() + ":" + requestMapping;
|
|
|
- }
|
|
|
-
|
|
|
- public void setRequestMappingHandlerMapping(RequestMappingHandlerMapping requestMappingHandlerMapping) {
|
|
|
- this.requestMappingHandlerMapping = requestMappingHandlerMapping;
|
|
|
- }
|
|
|
-
|
|
|
- public void setHandler(RequestHandler handler) {
|
|
|
- this.handler = handler;
|
|
|
- }
|
|
|
-
|
|
|
- public void setMagicApiService(ApiServiceProvider magicApiService) {
|
|
|
- this.magicApiService = magicApiService;
|
|
|
- }
|
|
|
-
|
|
|
- public List<ApiInfo> getApiInfos() {
|
|
|
- return apiInfos;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 注册请求
|
|
|
- */
|
|
|
- public void registerAllMapping() {
|
|
|
- List<ApiInfo> list = magicApiService.listWithScript();
|
|
|
- if (list != null) {
|
|
|
- apiInfos.addAll(list);
|
|
|
- for (ApiInfo info : list) {
|
|
|
- registerMapping(info, false);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 根据请求方法和路径获取接口信息
|
|
|
- *
|
|
|
- * @param method 请求方法
|
|
|
- * @param requestMapping 请求路径
|
|
|
- */
|
|
|
- public ApiInfo getApiInfo(String method, String requestMapping) {
|
|
|
- return mappings.get(buildMappingKey(method, requestMapping));
|
|
|
- }
|
|
|
-
|
|
|
- public void updateGroupPrefix(String oldGroupName, String newGroupName, String prefix) {
|
|
|
- for (ApiInfo info : apiInfos) {
|
|
|
- if (oldGroupName.equals(info.getGroupName())) {
|
|
|
- unregisterMapping(info.getId(), false);
|
|
|
- info.setGroupName(newGroupName);
|
|
|
- info.setGroupPrefix(prefix);
|
|
|
- registerMapping(info, false);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 注册请求映射
|
|
|
- *
|
|
|
- */
|
|
|
- public void registerMapping(ApiInfo info, boolean delete) {
|
|
|
- // 先判断是否已注册,如果已注册,则先取消注册在进行注册。
|
|
|
- if (mappings.containsKey(info.getId())) {
|
|
|
- ApiInfo oldInfo = mappings.get(info.getId());
|
|
|
- logger.info("取消注册接口:{}", oldInfo.getName());
|
|
|
- // 取消注册
|
|
|
- mappings.remove(getMappingKey(info));
|
|
|
- requestMappingHandlerMapping.unregisterMapping(getRequestMapping(oldInfo));
|
|
|
- }
|
|
|
- logger.info("注册接口:{}", info.getName());
|
|
|
- // 注册
|
|
|
- RequestMappingInfo requestMapping = getRequestMapping(info);
|
|
|
- mappings.put(info.getId(), info);
|
|
|
- mappings.put(getMappingKey(info), info);
|
|
|
- requestMappingHandlerMapping.registerMapping(requestMapping, handler, method);
|
|
|
- if (delete) { // 刷新缓存
|
|
|
- apiInfos.removeIf(i -> i.getId().equalsIgnoreCase(info.getId()));
|
|
|
- apiInfos.add(info);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 取消注册请求映射
|
|
|
- */
|
|
|
- public void unregisterMapping(String id, boolean delete) {
|
|
|
- ApiInfo info = mappings.remove(id);
|
|
|
- if (info != null) {
|
|
|
- logger.info("取消注册接口:{}", info.getName());
|
|
|
- mappings.remove(getMappingKey(info));
|
|
|
- requestMappingHandlerMapping.unregisterMapping(getRequestMapping(info));
|
|
|
- if (delete) { //刷新缓存
|
|
|
- apiInfos.removeIf(i -> i.getId().equalsIgnoreCase(info.getId()));
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 根据接口信息获取绑定map的key
|
|
|
- */
|
|
|
- private String getMappingKey(ApiInfo info) {
|
|
|
- return buildMappingKey(info.getMethod(), getRequestPath(info.getGroupPrefix(), info.getPath()));
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 处理前缀
|
|
|
- *
|
|
|
- * @param groupPrefix 分组前缀
|
|
|
- * @param path 请求路径
|
|
|
- */
|
|
|
- public String getRequestPath(String groupPrefix, String path) {
|
|
|
- groupPrefix = groupPrefix == null ? "" : groupPrefix;
|
|
|
- while (groupPrefix.endsWith("/")) {
|
|
|
- groupPrefix = groupPrefix.substring(0, groupPrefix.length() - 1);
|
|
|
- }
|
|
|
- while (path.startsWith("/")) {
|
|
|
- path = path.substring(1);
|
|
|
- }
|
|
|
- path = groupPrefix + "/" + path;
|
|
|
- if (prefix != null) {
|
|
|
- path = prefix + (path.startsWith("/") ? path.substring(1) : path);
|
|
|
- }
|
|
|
- return path;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 根据接口信息构建 RequestMappingInfo
|
|
|
- */
|
|
|
- private RequestMappingInfo getRequestMapping(ApiInfo info) {
|
|
|
- return RequestMappingInfo.paths(getRequestPath(info.getGroupPrefix(), info.getPath())).methods(RequestMethod.valueOf(info.getMethod().toUpperCase())).build();
|
|
|
- }
|
|
|
+ /**
|
|
|
+ * 已缓存的映射信息
|
|
|
+ */
|
|
|
+ private static Map<String, ApiInfo> mappings = new ConcurrentHashMap<>();
|
|
|
+
|
|
|
+ private static Logger logger = LoggerFactory.getLogger(MappingHandlerMapping.class);
|
|
|
+
|
|
|
+ /**
|
|
|
+ * spring中的请求映射处理器
|
|
|
+ */
|
|
|
+ private RequestMappingHandlerMapping requestMappingHandlerMapping;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 请求处理器
|
|
|
+ */
|
|
|
+ private RequestHandler handler;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 请求到达时处理的方法
|
|
|
+ */
|
|
|
+ private Method method = RequestHandler.class.getDeclaredMethod("invoke", HttpServletRequest.class, HttpServletResponse.class, Map.class, Map.class);
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 接口信息读取
|
|
|
+ */
|
|
|
+ private ApiServiceProvider magicApiService;
|
|
|
+ /**
|
|
|
+ * 统一接口前缀
|
|
|
+ */
|
|
|
+ private String prefix;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 缓存已映射的接口信息
|
|
|
+ */
|
|
|
+ private List<ApiInfo> apiInfos = Collections.synchronizedList(new ArrayList<>());
|
|
|
+
|
|
|
+ public MappingHandlerMapping() throws NoSuchMethodException {
|
|
|
+ }
|
|
|
+
|
|
|
+ public void setPrefix(String prefix) {
|
|
|
+ this.prefix = prefix;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 根据request获取对应的接口信息
|
|
|
+ */
|
|
|
+ public static ApiInfo getMappingApiInfo(HttpServletRequest request) {
|
|
|
+ NativeWebRequest webRequest = new ServletWebRequest(request);
|
|
|
+ // 找到注册的路径
|
|
|
+ String requestMapping = (String) webRequest.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);
|
|
|
+ // 根据请求方法和路径获取接口信息
|
|
|
+ return getMappingApiInfo(buildMappingKey(request.getMethod(), requestMapping));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 根据绑定的key获取接口信息
|
|
|
+ */
|
|
|
+ public static ApiInfo getMappingApiInfo(String key) {
|
|
|
+ return mappings.get(key);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 构建缓存map的key
|
|
|
+ *
|
|
|
+ * @param requestMethod 请求方法
|
|
|
+ * @param requestMapping 请求路径
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ public static String buildMappingKey(String requestMethod, String requestMapping) {
|
|
|
+ //TODO 判断 requestMapping 是否已 “/” 开头
|
|
|
+ if (!StringUtils.isEmpty(requestMapping) && !requestMapping.startsWith("/")) {
|
|
|
+ requestMapping = "/" + requestMapping;
|
|
|
+ }
|
|
|
+ return requestMethod.toUpperCase() + ":" + requestMapping;
|
|
|
+ }
|
|
|
+
|
|
|
+ public void setRequestMappingHandlerMapping(RequestMappingHandlerMapping requestMappingHandlerMapping) {
|
|
|
+ this.requestMappingHandlerMapping = requestMappingHandlerMapping;
|
|
|
+ }
|
|
|
+
|
|
|
+ public void setHandler(RequestHandler handler) {
|
|
|
+ this.handler = handler;
|
|
|
+ }
|
|
|
+
|
|
|
+ public void setMagicApiService(ApiServiceProvider magicApiService) {
|
|
|
+ this.magicApiService = magicApiService;
|
|
|
+ }
|
|
|
+
|
|
|
+ public List<ApiInfo> getApiInfos() {
|
|
|
+ return apiInfos;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 注册请求
|
|
|
+ */
|
|
|
+ public void registerAllMapping() {
|
|
|
+ List<ApiInfo> list = magicApiService.listWithScript();
|
|
|
+ if (list != null) {
|
|
|
+ apiInfos.addAll(list);
|
|
|
+ for (ApiInfo info : list) {
|
|
|
+ try {
|
|
|
+ registerMapping(info, false);
|
|
|
+ } catch (Exception e) {
|
|
|
+ logger.error("接口:{}注册失败", info.getName(), e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 根据请求方法和路径获取接口信息
|
|
|
+ *
|
|
|
+ * @param method 请求方法
|
|
|
+ * @param requestMapping 请求路径
|
|
|
+ */
|
|
|
+ public ApiInfo getApiInfo(String method, String requestMapping) {
|
|
|
+ return mappings.get(buildMappingKey(method, requestMapping));
|
|
|
+ }
|
|
|
+
|
|
|
+ public void updateGroupPrefix(String oldGroupName, String newGroupName, String prefix) {
|
|
|
+ for (ApiInfo info : apiInfos) {
|
|
|
+ if (oldGroupName.equals(info.getGroupName())) {
|
|
|
+ unregisterMapping(info.getId(), false);
|
|
|
+ info.setGroupName(newGroupName);
|
|
|
+ info.setGroupPrefix(prefix);
|
|
|
+ registerMapping(info, false);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 判断是否已注册
|
|
|
+ */
|
|
|
+ public boolean hasRegisterMapping(ApiInfo info) {
|
|
|
+ if (info.getId() != null) {
|
|
|
+ ApiInfo oldInfo = mappings.get(info.getId());
|
|
|
+ if (oldInfo != null
|
|
|
+ && Objects.equals(oldInfo.getGroupPrefix(), info.getGroupPrefix())
|
|
|
+ && Objects.equals(oldInfo.getMethod(), info.getMethod())
|
|
|
+ && Objects.equals(oldInfo.getPath(), info.getPath())) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ Map<RequestMappingInfo, HandlerMethod> handlerMethods = this.requestMappingHandlerMapping.getHandlerMethods();
|
|
|
+ if (handlerMethods != null) {
|
|
|
+ return handlerMethods.get(getRequestMapping(info)) != null;
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 注册请求映射
|
|
|
+ */
|
|
|
+ public void registerMapping(ApiInfo info, boolean delete) {
|
|
|
+ // 先判断是否已注册,如果已注册,则先取消注册在进行注册。
|
|
|
+ if (mappings.containsKey(info.getId())) {
|
|
|
+ ApiInfo oldInfo = mappings.get(info.getId());
|
|
|
+ logger.info("取消注册接口:{}", oldInfo.getName());
|
|
|
+ // 取消注册
|
|
|
+ mappings.remove(getMappingKey(info));
|
|
|
+ requestMappingHandlerMapping.unregisterMapping(getRequestMapping(oldInfo));
|
|
|
+ }
|
|
|
+ logger.info("注册接口:{}", info.getName());
|
|
|
+ // 注册
|
|
|
+ RequestMappingInfo requestMapping = getRequestMapping(info);
|
|
|
+ mappings.put(info.getId(), info);
|
|
|
+ mappings.put(getMappingKey(info), info);
|
|
|
+ requestMappingHandlerMapping.registerMapping(requestMapping, handler, method);
|
|
|
+ if (delete) { // 刷新缓存
|
|
|
+ apiInfos.removeIf(i -> i.getId().equalsIgnoreCase(info.getId()));
|
|
|
+ apiInfos.add(info);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 取消注册请求映射
|
|
|
+ */
|
|
|
+ public void unregisterMapping(String id, boolean delete) {
|
|
|
+ ApiInfo info = mappings.remove(id);
|
|
|
+ if (info != null) {
|
|
|
+ logger.info("取消注册接口:{}", info.getName());
|
|
|
+ mappings.remove(getMappingKey(info));
|
|
|
+ requestMappingHandlerMapping.unregisterMapping(getRequestMapping(info));
|
|
|
+ if (delete) { //刷新缓存
|
|
|
+ apiInfos.removeIf(i -> i.getId().equalsIgnoreCase(info.getId()));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 根据接口信息获取绑定map的key
|
|
|
+ */
|
|
|
+ private String getMappingKey(ApiInfo info) {
|
|
|
+ return buildMappingKey(info.getMethod(), getRequestPath(info.getGroupPrefix(), info.getPath()));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 处理前缀
|
|
|
+ *
|
|
|
+ * @param groupPrefix 分组前缀
|
|
|
+ * @param path 请求路径
|
|
|
+ */
|
|
|
+ public String getRequestPath(String groupPrefix, String path) {
|
|
|
+ groupPrefix = groupPrefix == null ? "" : groupPrefix;
|
|
|
+ while (groupPrefix.endsWith("/")) {
|
|
|
+ groupPrefix = groupPrefix.substring(0, groupPrefix.length() - 1);
|
|
|
+ }
|
|
|
+ while (path.startsWith("/")) {
|
|
|
+ path = path.substring(1);
|
|
|
+ }
|
|
|
+ path = groupPrefix + "/" + path;
|
|
|
+ if (prefix != null) {
|
|
|
+ path = prefix + (path.startsWith("/") ? path.substring(1) : path);
|
|
|
+ }
|
|
|
+ return path;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 根据接口信息构建 RequestMappingInfo
|
|
|
+ */
|
|
|
+ private RequestMappingInfo getRequestMapping(ApiInfo info) {
|
|
|
+ return RequestMappingInfo.paths(getRequestPath(info.getGroupPrefix(), info.getPath())).methods(RequestMethod.valueOf(info.getMethod().toUpperCase())).build();
|
|
|
+ }
|
|
|
|
|
|
}
|