阅读 143

SpringMVC自定义兼容性HandlerMapping​

SpringMVC自定义兼容性HandlerMapping

写在前面

看到这篇博客时,默认你知道Spring MVC中HandlerMapping的作用,及前台请求到响应的的流转。
感谢网上其他大佬博客给我的借鉴,博客地址这里忘记了。
大家可以直接点击右上角进入我的SpringBoot项目查看源码,有用的话帮我点亮下呗。

自定义Handler

我有时候会考虑是否可以自定义HandlerMapping,可以参考RequestMappingHandlerMapping继承的父类,并且重写部分方法,以下为我的实现。

首先,需要新建一个注解,这个注解的作用同@RequestMapping.

package com.example.feng.annotation;import org.springframework.web.bind.annotation.RequestMethod;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;/**
 * @author fengjirong
 * @date 2021/3/11 14:20
 */@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public @interface FengRequestMapping {    String value() default "";

    RequestMethod[] method() default {};
}

接下来是自定义handler的代码,需要实现多个方法,用于指定自定义注解修饰的方法使用当前handler,设置handler对象的url等参数,各方法的作用请查看对应方法注释。需要注意的是,需将自定义handler的优先级设置为order(0),否则会出现异常。

package com.example.feng.handler;import com.example.feng.annotation.FengRequestMapping;import com.example.feng.utils.ApplicaitonFactory;import org.springframework.context.ConfigurableApplicationContext;import org.springframework.context.EmbeddedValueResolverAware;import org.springframework.core.annotation.AnnotationUtils;import org.springframework.lang.Nullable;import org.springframework.stereotype.Component;import org.springframework.util.Assert;import org.springframework.util.StringValueResolver;import org.springframework.web.method.HandlerMethod;import org.springframework.web.servlet.handler.MatchableHandlerMapping;import org.springframework.web.servlet.handler.RequestMatchResult;import org.springframework.web.servlet.mvc.condition.RequestCondition;import org.springframework.web.servlet.mvc.method.RequestMappingInfo;import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping;import org.springframework.web.util.UrlPathHelper;import javax.servlet.ServletException;import javax.servlet.http.HttpServletRequest;import java.lang.reflect.Method;import java.util.Set;/**
 * @author fengjirong
 * @date 2021/3/11 15:51
 */@Componentclass FengRequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping
        implements MatchableHandlerMapping, EmbeddedValueResolverAware {    private RequestMappingInfo.BuilderConfiguration config = new RequestMappingInfo.BuilderConfiguration();    @Nullable
    private StringValueResolver embeddedValueResolver;    /**
     * handler是否含有@FengRequestMapping注解
     *
     * @param beanType
     * @return boolean
     * @Author fengjirong
     * @Date 2021/3/11 14:35
     */
    @Override
    protected boolean isHandler(Class<?> beanType) {
        Method[] methods = beanType.getDeclaredMethods();        for (Method method : methods) {            if (AnnotationUtils.findAnnotation(method, FengRequestMapping.class) != null) {                return true;
            }
        }        return false;

    }    /**
     * description: 使用方法级别的@ {@FengRequestMapping}注释创建RequestMappingInfo。
     *
     * @param method  handlerType
     * @param handlerType  handlerType
     * @return RequestMappingInfo
     * @Author fengjirong
     * @Date   2021/3/12 11:24
     */
    @Override
    protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
        RequestMappingInfo info = null;
        FengRequestMapping mapping = method.getAnnotation(FengRequestMapping.class);        if (mapping != null){
            RequestCondition<?> condition = getCustomMethodCondition(method);
            info = createRequestMappingInfo(mapping, condition);
        }        return info;
    }    /**
     * description: 匹配操作
     *
     * @param info
     * @return
     * @Author fengjirong
     * @Date   2021/3/12 11:26
     */
    @Override
    protected void handleMatch(RequestMappingInfo info, String lookupPath, HttpServletRequest request) {
        request.setAttribute("isMongo", true);
        request.setAttribute("handledTime", System.nanoTime());
    }    /**
     * description: 不匹配url处理
     * 
     * @param infos 
     * @param lookupPath 
     * @param request 
     * @return HandlerMethod
     * @Author fengjirong
     * @Date   2021/3/12 11:37
     */
    @Override
    protected HandlerMethod handleNoMatch(Set<RequestMappingInfo> infos, String lookupPath, HttpServletRequest request) throws ServletException {        return null;
    }    /**
     * description: 从注解中获得请求路径、请求类型等创建RequestMappingInfo对象方法
     *
     * @param requestMapping
     * @param customCondition
     * @return RequestMappingInfo
     * @Author fengjirong
     * @Date   2021/3/12 11:28
     */
    private RequestMappingInfo createRequestMappingInfo(
            FengRequestMapping requestMapping, @Nullable RequestCondition<?> customCondition) {
        ConfigurableApplicationContext context = ApplicaitonFactory.getContext();
        RequestMappingInfo.Builder builder = RequestMappingInfo
                .paths(resolveEmbeddedValuesInPatterns(new String[]{requestMapping.value()}))
                .methods(requestMapping.method())
                .params(new String[]{})
                .headers(new String[]{})
                .consumes(new String[]{})
                .produces(new String[]{})
                .mappingName("");        if (customCondition != null) {
            builder.customCondition(customCondition);
        }        return builder.options(this.config).build();
    }    /**
     * 属性设置
     */
    @Override
    public void afterPropertiesSet() {        // 提升当前 HandlerMapping 的在映射处理器列表中的顺序
        super.setOrder(0);        super.afterPropertiesSet();
    }    @Override
    public void setEmbeddedValueResolver(StringValueResolver resolver) {        this.embeddedValueResolver = resolver;
    }    @Override
    public RequestMatchResult match(HttpServletRequest request, String pattern) {
        Assert.isNull(getPatternParser(), "This HandlerMapping requires a PathPattern");
        RequestMappingInfo info = RequestMappingInfo.paths(pattern).options(this.config).build();
        RequestMappingInfo match = info.getMatchingCondition(request);        return (match != null && match.getPatternsCondition() != null ?                new RequestMatchResult(
                        match.getPatternsCondition().getPatterns().iterator().next(),
                        UrlPathHelper.getResolvedLookupPath(request),
                        getPathMatcher()) : null);
    }    /**
     * Resolve placeholder values in the given array of patterns.
     * @return a new array with updated patterns
     */
    protected String[] resolveEmbeddedValuesInPatterns(String[] patterns) {        if (this.embeddedValueResolver == null) {            return patterns;
        }        else {
            String[] resolvedPatterns = new String[patterns.length];            for (int i = 0; i < patterns.length; i++) {
                resolvedPatterns[i] = this.embeddedValueResolver.resolveStringValue(patterns[i]);
            }            return resolvedPatterns;
        }
    }    @Nullable
    protected RequestCondition<?> getCustomMethodCondition(Method method) {        return null;
    }
}

接下来是测试controller,测试@FengRequestMapping与@RequestMapping的兼容性。

package com.example.feng.student;import com.example.feng.annotation.FengRequestMapping;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.DeleteMapping;import org.springframework.web.bind.annotation.GetMapping;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;import org.springframework.web.bind.annotation.ResponseBody;import org.springframework.web.bind.annotation.RestController;/**
 * @author fengjirong
 * @date 2021/3/10 11:11
 */@Controllerpublic class StudentController {    @ResponseBody
    @FengRequestMapping(value = "/student", method = RequestMethod.GET)
    public String get(){        return "get submit";
    }    @ResponseBody
    @FengRequestMapping(value = "/student", method = RequestMethod.POST)
    public String post(){        return "post submit";
    }    @ResponseBody
    @FengRequestMapping(value = "/student", method = RequestMethod.PUT)
    public String put(){        return "put submit";
    }    @ResponseBody
    //@FengRequestMapping(value = "/student", method = RequestMethod.DELETE)
    @DeleteMapping(value = "/student")
    public String delete(){        return "delete submit";
    }
}

前台页面使用rest风格表单提交的index.html。

<!DOCTYPE html><html lang="en"><head>
    <meta charset="UTF-8">
    <title>rest风格controller测试</title>
    <script src="https://code.jquery.com/jquery-3.0.0.min.js"></script>
    <script type="text/javascript">
        function doButton() {
            $.ajax({                type: "DELETE",                url: "/student",                async:false,                success:function (data) {
                    alert(data)
                }
            });
        }    </script></head><body><form action="/student" method="GET">
    <input type="submit" value="get"></form><form action="/student" method="POST">
    <input type="submit" value="post"></form><form action="/student" method="POST">
    <input name="_method" value="put" type="hidden">
    <input name="_m" value="put" type="hidden">
    <input type="submit" value="put"></form><form action="/student" method="POST">
    <input name="_method" value="delete" type="hidden">
    <input name="_m" value="delete" type="hidden">
    <input type="submit" value="delete"></form><button name="button1" onclick="doButton()">
    确认</button></body></html>

SpringBoot开启Rest风格controller需要在配置文件中手动开启,添加以下配置项

spring:
  mvc:
    hiddenmethod:
      filter:
      #启用rest风格请求
        enabled: true

看效果

查看自定义handler中handler注册详情。

而使用@RequestMapping标注的handler注册在RequestMappingHandlerMapping组件中。

由上两张图,我们可以看到即使多个注解在一个conroller中,也能够得到很好的映射,这样提高了自定义handler的兼容性。
由于我测试的时候构建的是Spring Boot项目,访问http://localhost:8004/跳转到index.html,点击按钮,前台提交表单,可以得到对应的响应。


文章分类
后端
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐