spring自定义RequestMappingHandlerMapping


import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.service.annotation.HttpExchange;
import org.springframework.web.servlet.mvc.condition.RequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.Set;


public class CustomRequestMappingHandlerMapping extends RequestMappingHandlerMapping {


    @Override
    public void afterPropertiesSet() {
        super.setOrder(1);
        super.afterPropertiesSet();


    }

    @Nullable
    private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
        RequestCondition customCondition = (element instanceof Class clazz ?
                getCustomTypeCondition(clazz) : getCustomMethodCondition((Method) element));

        RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
        if (requestMapping != null) {
            return createRequestMappingInfo(requestMapping, customCondition);
        }

        HttpExchange httpExchange = AnnotatedElementUtils.findMergedAnnotation(element, HttpExchange.class);
        if (httpExchange != null) {
            return createRequestMappingInfo(httpExchange, customCondition);
        }

        return null;
    }

    @Override
    protected boolean isHandler(Class beanType) {
        return AnnotatedElementUtils.hasAnnotation(beanType, FeignClient.class);
    }

    @Override
    protected void handleMatch(RequestMappingInfo info, String lookupPath, HttpServletRequest request) {

    }

    @Override
    protected HandlerMethod handleNoMatch(Set infos, String lookupPath, HttpServletRequest request) throws ServletException {
        return null;
    }


    protected RequestMappingInfo getMappingForMethod(Method method, Class handlerType) {
        FeignClient annotation = AnnotatedElementUtils.findMergedAnnotation(handlerType, FeignClient.class);
        RequestMappingInfo info = null;
        if (annotation != null) {
            RequestCondition customMethodCondition = getCustomTypeCondition(handlerType);
            Class[] interfaces = handlerType.getInterfaces();
            info = createRequestMappingInfo(annotation, customMethodCondition, method, interfaces[0]);
        }
        return info;
    }


    protected RequestMappingInfo createRequestMappingInfo(FeignClient requestMapping, RequestCondition customCondition, Method method, Class superInterface) {
        RequestMapping mergedAnnotation = AnnotatedElementUtils.findMergedAnnotation(method, RequestMapping.class);
        RequestMethod[] requestMethods = null;
        if (mergedAnnotation != null) {
            requestMethods = mergedAnnotation.method();
        } else {
            requestMethods = new RequestMethod[]{};
        }

        String substring = superInterface.getSimpleName().substring(0, 1).toLowerCase() + superInterface.getSimpleName().substring(1);

        String path = StringUtils.hasText(requestMapping.path()) ? requestMapping.path() : StringUtils.hasText(requestMapping.contextId()) ? requestMapping.contextId() :
                StringUtils.hasText(requestMapping.name()) ? requestMapping.name() : substring;

        RequestMappingInfo.Builder builder = RequestMappingInfo
                .paths(resolveEmbeddedValuesInPatterns(new String[]{path + "/" + method.getName()}))
                .methods(requestMethods)
                .params(new String[]{})
                .headers(new String[]{})
                .consumes(new String[]{})
                .produces(new String[]{})
                .mappingName(requestMapping.contextId());


        return builder.options(getBuilderConfiguration()).build();
    }


}

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.core.Conventions;
import org.springframework.core.MethodParameter;
import org.springframework.core.ResolvableType;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.http.HttpStatusCode;
import org.springframework.http.ProblemDetail;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.lang.Nullable;
import org.springframework.validation.BindingResult;
import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor;

import java.io.IOException;
import java.lang.reflect.Type;
import java.net.URI;
import java.util.List;


public class CustomMessageConverterMethodProcessor extends AbstractMessageConverterMethodProcessor {


    protected CustomMessageConverterMethodProcessor(List> converters) {
        super(converters);
    }

    @Override
    public boolean supportsReturnType(MethodParameter returnType) {

        return AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), FeignClient.class);
    }


    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        Class[] interfaces = parameter.getContainingClass().getInterfaces();
        for (Class anInterface : interfaces) {
            FeignClient annotation = anInterface.getAnnotation(FeignClient.class);
            if (annotation != null) {
                return true;
            }
        }
        return false;
    }



    @Override
    public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
                                  NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

        parameter = parameter.nestedIfOptional();
        Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());

        if (binderFactory != null) {
            String name = Conventions.getVariableNameForParameter(parameter);
            ResolvableType type = ResolvableType.forMethodParameter(parameter);
            WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name, type);
            if (arg != null) {
                validateIfApplicable(binder, parameter);
                if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
                    throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
                }
            }
            if (mavContainer != null) {
                mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
            }
        }

        return adaptArgumentIfNecessary(arg, parameter);
    }

    @Override
    protected  Object readWithMessageConverters(NativeWebRequest webRequest, MethodParameter parameter,
                                                   Type paramType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {

        ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
        Object arg = readWithMessageConverters(inputMessage, parameter, paramType);
        if (arg == null && checkRequired(parameter)) {
            throw new HttpMessageNotReadableException("Required request body is missing: " +
                    parameter.getExecutable().toGenericString(), inputMessage);
        }
        return arg;
    }

    protected boolean checkRequired(MethodParameter parameter) {
        RequestBody requestBody = parameter.getParameterAnnotation(RequestBody.class);
        return (requestBody != null && requestBody.required() && !parameter.isOptional());
    }

    @Override
    public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
                                  ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
            throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {

        mavContainer.setRequestHandled(true);
        ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
        ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);

        if (returnValue instanceof ProblemDetail detail) {
            outputMessage.setStatusCode(HttpStatusCode.valueOf(detail.getStatus()));
            if (detail.getInstance() == null) {
                URI path = URI.create(inputMessage.getServletRequest().getRequestURI());
                detail.setInstance(path);
            }
        }

        // Try even with null return value. ResponseBodyAdvice could get involved.
        writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
    }
}

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;

import java.util.ArrayList;
import java.util.List;

@Configuration
public class CustomBeanPostProcessor implements BeanPostProcessor {


    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof RequestMappingHandlerAdapter requestMappingHandlerAdapter) {
            List> messageConverters = requestMappingHandlerAdapter.getMessageConverters();
            List returnValueHandlers = requestMappingHandlerAdapter.getReturnValueHandlers();
            ArrayList handlerMethodReturnValueHandlers = new ArrayList<>(returnValueHandlers);
            CustomMessageConverterMethodProcessor customMessageConverterMethodProcessor = new CustomMessageConverterMethodProcessor(messageConverters);
            handlerMethodReturnValueHandlers.addFirst(customMessageConverterMethodProcessor);
            requestMappingHandlerAdapter.setReturnValueHandlers(handlerMethodReturnValueHandlers);
            List argumentResolvers = requestMappingHandlerAdapter.getArgumentResolvers();
            ArrayList handlerMethodArgumentResolvers = new ArrayList<>(argumentResolvers);
            handlerMethodArgumentResolvers.addFirst(customMessageConverterMethodProcessor);
            requestMappingHandlerAdapter.setArgumentResolvers(handlerMethodArgumentResolvers);
        }
        return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
    }
}

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.support.FormattingConversionService;
import org.springframework.web.accept.ContentNegotiationManager;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import org.springframework.web.servlet.resource.ResourceUrlProvider;



@Configuration
public class CustomWebMvcConfigurer {

    @Bean
    @ConditionalOnClass(WebMvcConfigurationSupport.class)
    @SuppressWarnings("deprecation")
    public RequestMappingHandlerMapping customRequestMappingHandlerMapping(
            @Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
            @Qualifier("mvcConversionService") FormattingConversionService conversionService,
            @Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider
    ) {

        RequestMappingHandlerMapping mapping = new CustomRequestMappingHandlerMapping();
        mapping.setOrder(1);
        mapping.setContentNegotiationManager(contentNegotiationManager);
        return mapping;
    }

}

你可能感兴趣的:(spring,java,后端)