All Downloads are FREE. Search and download functionalities are using the official Maven repository.

cn.herodotus.engine.web.scan.processor.RequestMappingScanner Maven / Gradle / Ivy

Go to download

基于 Spring Authorization Server 的 Eurynome Cloud 基础核心组件模块

There is a newer version: 3.0.4.0
Show newest version
/*
 * Copyright (c) 2020-2030 ZHENGGENGWEI(码匠君)
 *
 * Dante Engine Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Dante Engine 采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
 *
 * 1.请不要删除和修改根目录下的LICENSE文件。
 * 2.请不要删除和修改 Dante Engine 源码头部的版权声明。
 * 3.请保留源码和相关描述文件的项目出处,作者声明等。
 * 4.分发源码时候,请注明软件出处 https://gitee.com/herodotus/dante-engine
 * 5.在修改包名,模块名称,项目代码等时,请注明软件出处 https://gitee.com/herodotus/dante-engine
 * 6.若您的项目无法满足以上几点,可申请商业授权
 */

package cn.herodotus.engine.web.scan.processor;

import cn.herodotus.engine.assistant.core.definition.constants.BaseConstants;
import cn.herodotus.engine.assistant.core.definition.constants.SymbolConstants;
import cn.herodotus.engine.web.core.definition.RequestMappingScanManager;
import cn.herodotus.engine.web.core.support.WebPropertyFinder;
import cn.herodotus.engine.web.core.domain.RequestMapping;
import cn.herodotus.engine.web.scan.properties.ScanProperties;
import cn.hutool.core.util.HashUtil;
import cn.hutool.crypto.SecureUtil;
import io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.annotations.Operation;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.condition.PathPatternsRequestCondition;
import org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 

Description: RequestMapping扫描器

* * @author : gengwei.zheng * @date : 2020/6/2 19:52 */ public class RequestMappingScanner implements ApplicationListener { private static final Logger log = LoggerFactory.getLogger(RequestMappingScanner.class); private final ScanProperties restProperties; private final RequestMappingScanManager requestMappingScanManager; public RequestMappingScanner(ScanProperties restProperties, RequestMappingScanManager requestMappingScanManager) { this.restProperties = restProperties; this.requestMappingScanManager = requestMappingScanManager; } @Override public void onApplicationEvent(ApplicationReadyEvent event) { ApplicationContext applicationContext = event.getApplicationContext(); log.debug("[Herodotus] |- [1] Application is READY, start to scan request mapping!"); onApplicationEvent(applicationContext); } public void onApplicationEvent(ApplicationContext applicationContext) { // 1、获取服务ID:该服务ID对于微服务是必需的。 String serviceId = WebPropertyFinder.getApplicationName(applicationContext.getEnvironment()); // 2、只针对有EnableResourceServer注解的微服务进行扫描。如果变为单体架构目前不会用到EnableResourceServer所以增加的了一个Architecture判断 if (!requestMappingScanManager.isPerformScan()) { // 只扫描资源服务器 log.warn("[Herodotus] |- Can not found scan annotation in Service [{}], Skip!", serviceId); return; } // 3、获取所有接口映射 Map mappings = applicationContext.getBeansOfType(RequestMappingHandlerMapping.class); // 4、 获取url与类和方法的对应信息 List resources = new ArrayList<>(); for (RequestMappingHandlerMapping mapping : mappings.values()) { Map handlerMethods = mapping.getHandlerMethods(); if (MapUtils.isNotEmpty(handlerMethods)) { for (Map.Entry entry : handlerMethods.entrySet()) { RequestMappingInfo requestMappingInfo = entry.getKey(); HandlerMethod handlerMethod = entry.getValue(); // 4.1、如果是被排除的requestMapping,那么就进行不扫描 if (isExcludedRequestMapping(handlerMethod)) { continue; } // 4.2、拼装扫描信息 RequestMapping requestMapping = createRequestMapping(serviceId, requestMappingInfo, handlerMethod); if (ObjectUtils.isEmpty(requestMapping)) { continue; } resources.add(requestMapping); } } } if (CollectionUtils.isNotEmpty(resources)) { log.debug("[Herodotus] |- [2] Request mapping scan found [{}] resources in service [{}], go to next stage!", serviceId, resources.size()); requestMappingScanManager.process(resources); } else { log.debug("[Herodotus] |- [2] Request mapping scan can not find any resources in service [{}]!", serviceId); } log.info("[Herodotus] |- Request Mapping Scan for Service: [{}] FINISHED!", serviceId); } /** * 检测RequestMapping是否需要被排除 * * @param handlerMethod HandlerMethod * @return boolean */ private boolean isExcludedRequestMapping(HandlerMethod handlerMethod) { if (!isSpringAnnotationMatched(handlerMethod)) { return true; } return !isSwaggerAnnotationMatched(handlerMethod); } /** * 如果开启isJustScanRestController,那么就只扫描RestController * * @param handlerMethod HandlerMethod * @return boolean */ private boolean isSpringAnnotationMatched(HandlerMethod handlerMethod) { if (restProperties.isJustScanRestController()) { return handlerMethod.getMethod().getDeclaringClass().getAnnotation(RestController.class) != null; } return true; } /** * 有ApiIgnore注解的方法不扫描, 没有ApiOperation注解不扫描 * * @param handlerMethod HandlerMethod * @return boolean */ private boolean isSwaggerAnnotationMatched(HandlerMethod handlerMethod) { if (handlerMethod.getMethodAnnotation(Hidden.class) != null) { return false; } Operation operation = handlerMethod.getMethodAnnotation(Operation.class); return ObjectUtils.isNotEmpty(operation) && !operation.hidden(); } /** * 如果当前class的groupId在GroupId列表中,那么就进行扫描,否则就排除 * * @param className 当前扫描的controller类名 * @return Boolean */ private boolean isLegalGroup(String className) { if (StringUtils.isNotEmpty(className)) { List groupIds = restProperties.getScanGroupIds(); List result = groupIds.stream().filter(groupId -> StringUtils.contains(className, groupId)).collect(Collectors.toList()); return !CollectionUtils.sizeIsEmpty(result); } else { return false; } } private RequestMapping createRequestMapping(String serviceId, RequestMappingInfo info, HandlerMethod method) { // 4.2.1、获取类名 // method.getMethod().getDeclaringClass().getName() 取到的是注解实际所在类的名字,比如注解在父类叫BaseController,那么拿到的就是BaseController // method.getBeanType().getName() 取到的是注解实际Bean的名字,比如注解在在父类叫BaseController,而实际类是SysUserController,那么拿到的就是SysUserController String className = method.getBeanType().getName(); // 4.2.2、检测该类是否在GroupIds列表中 if (!isLegalGroup(className)) { return null; } // 5.2.3、获取不包含包路径的类名 String classSimpleName = method.getBeanType().getSimpleName(); // 4.2.4、获取RequestMapping注解对应的方法名 String methodName = method.getMethod().getName(); // 5.2.5、获取注解对应的请求类型 RequestMethodsRequestCondition requestMethodsRequestCondition = info.getMethodsCondition(); String requestMethods = StringUtils.join(requestMethodsRequestCondition.getMethods(), SymbolConstants.COMMA); // 5.2.6、获取主机对应的请求路径 PathPatternsRequestCondition pathPatternsCondition = info.getPathPatternsCondition(); Set patternValues = pathPatternsCondition.getPatternValues(); if (CollectionUtils.isEmpty(patternValues)) { return null; } String urls = StringUtils.join(patternValues, SymbolConstants.COMMA); // 对于单体架构路径一般都是menu,还是手动设置吧。 // if (!isDistributedArchitecture()) { // if (StringUtils.contains(urls, "index")) { // return null; // } // } // 5.2.7、微服务范围更加粗放, 单体架构应用通过classSimpleName进行细化 // String identifyingCode = isDistributedArchitecture() ? serviceId : classSimpleName; // 5.2.8、根据serviceId, requestMethods, urls生成的MD5值,作为自定义主键 String flag = serviceId + SymbolConstants.DASH + requestMethods + SymbolConstants.DASH + urls; String id = SecureUtil.md5(flag); int code = HashUtil.fnvHash(flag); // 5.2.9、组装对象 RequestMapping requestMapping = new RequestMapping(); requestMapping.setMetadataId(id); requestMapping.setMetadataCode(BaseConstants.AUTHORITY_PREFIX + code); // 微服务需要明确ServiceId,同时也知道ParentId,Hammer有办法,但是太繁琐,还是生成数据后,配置一把好点。 // if (isDistributedArchitecture()) { // requestMapping.setServiceId(identifyingCode); // requestMapping.setParentId(identifyingCode); // } requestMapping.setServiceId(serviceId); requestMapping.setParentId(serviceId); Operation apiOperation = method.getMethodAnnotation(Operation.class); if (ObjectUtils.isNotEmpty(apiOperation)) { requestMapping.setMetadataName(apiOperation.summary()); // requestMapping.setDescription(apiOperation.description()); } requestMapping.setRequestMethod(requestMethods); requestMapping.setUrl(urls); requestMapping.setClassName(className); requestMapping.setMethodName(methodName); return requestMapping; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy