写在前面

看到这篇博客时,默认你知道Spring MVC中handler的作用,及前台请求到响应的的流转。

感谢网上其他大佬博客给我的借鉴,博客地址这里忘记了。

自定义Handler

我有时候会考虑是否可以自定义handler,可以参考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
*/
@Component
class 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
*/
@Controller
public 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>

看效果

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

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



由上两张图,我们可以看到即使多个注解在一个conroller中,也能够得到很好的映射,这样提高了自定义handler的兼容性。

由于我测试的时候构建的是Spring Boot项目,访问http://localhost:8004/跳转到index.html,点击按钮,前台提交表单,可以得到对应的响应。

SpringMVC自定义兼容性Handler的更多相关文章

  1. C++进阶--自定义new handler

    //############################################################################ // 自定义new handler /* ...

  2. SpringMVC自定义视图Excel视图和PDF视图

    SpringMVC自定义视图 Excel视图和PDF视图 SpringMVC杂记(十一) 使用Excel视图 Spring MVC 视图解析器(ViewResolver ) java实现导出excel ...

  3. SpringMVC自定义类型转换器

    SpringMVC 自定义类型转换器  我们在使用SpringMVC时,常常需要把表单中的参数映射到我们对象的属性中,我们可以在默认的spring-servlet.xml加上如下的配置即可做到普通数据 ...

  4. Python 日志打印之自定义logger handler

    日志打印之自定义logger handler By:授客 QQ:1033553122 #实践环境 WIN 10 Python 3.6.5 #实践代码 handler.py #!/usr/bin/env ...

  5. SpringMVC 自定义一个拦截器

    自定义一个拦截器方法,实现HandlerInterceptor方法 public class FirstInterceptor implements HandlerInterceptor{ /** * ...

  6. SpringMVC——自定义拦截器、异常处理以及父子容器配置

    自定义拦截器: 一.若想实现自定义拦截器,需要实现 org.springframework.web.servlet.HandlerInterceptor 接口. 二.HandlerIntercepto ...

  7. springMVC自定义注解实现用户行为验证

    最近在进行项目开发的时候需要对接口做Session验证 1.自定义一个注解@AuthCheckAnnotation @Documented @Target(ElementType.METHOD) @I ...

  8. SpringMVC 自定义类型转换器

    先准备一个JavaBean(Employee) 一个Handler(SpringMVCTest) 一个converters(EmployeeConverter) 要实现的输入一个字符串转换成一个emp ...

  9. springMVC自定义全局异常

    SpringMVC通过HandlerExceptionResolver处理程序异常,包括Handler映射,数据绑定以及目标方法执行时所发生的异常. SpringMVC中默认是没有加装载Handler ...

随机推荐

  1. 在kubernetes集群里集成Apollo配置中心(1)之交付Apollo-configservice至Kubernetes集群

    1.Apollo简介 Apollo(阿波罗)是携程框架部门研发的分布式配置中心,能够集中化管理应用不同环境.不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限.流程治理等特性,适用于微 ...

  2. K8S(08)交付实战-交付jenkins到k8s集群

    k8s交付实战-交付jenkins到k8s集群 目录 k8s交付实战-交付jenkins到k8s集群 1 准备jenkins镜像 1.1 下载官方镜像 1.2 修改官方镜像 1.2.1 创建目录 1. ...

  3. 【非原创】codeforces 1025D - Recovering BST【区间dp+二叉搜索树】

    题目:戳这里 题意:给一个不下降序列,有n个数.问能否构造一个二叉搜索树,满足父亲和儿子之间的gcd>1. 解题思路:其实这题就是构造个二叉搜索树,只不过多了个条件.主要得了解二叉搜索树的性质, ...

  4. 地址解析协议ARP与逆地址解析协议RARP

    IP地址是用来通信的,但是和硬件地址是有区别的.物理地址是数据链路层和物理层使用的地址,IP地址是网络层及以上各层使用的地址. 发送数据时,数据从高层向下层传输,使用IP地址的IP数据报交给下层的数据 ...

  5. Bootstrap页头

    页头组件能够为 h1 标签增加适当的空间,并且与页面的其他部分形成一定的分隔.它支持 h1 标签内内嵌 small 元素的默认效果,还支持大部分其他组件(需要增加一些额外的样式). <div c ...

  6. 错误记录:MQJE001: 完成代码为 '2',原因为 '2035'。

    在windows server 2008上安装websphere mq7.5 服务端,建立队列.通过java client向我的机器的队列发送消息和接收消息. mq安装成功,队列管理器.队列.通道也都 ...

  7. 101道Numpy、Pandas练习题

    无论是数据分析还是机器学习,数据的预处理必不可少. 其中最常用.最基础的Python库非numpy和pandas莫属,很多初学者可能看了很多教程,但是很快就把用法忘光了. 光看不练假把式,今天向大家推 ...

  8. html5 image>usemap (attribute)

    # html5   image>usemap  (attribute) https://caniuse.com/#search=usemap http://www.w3.org/TR/html5 ...

  9. React Hooks: useCallback All In One

    React Hooks: useCallback All In One useCallback https://reactjs.org/docs/hooks-reference.html#usecal ...

  10. fibonacci all in one

    fibonacci all in one fibonacci sequence https://www.mathsisfun.com/numbers/fibonacci-sequence.html f ...