写在前面

看到这篇博客时,默认你知道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. LINUX - vim高效操作

    (一)可以为操作的一行添加下划线 set cursorline

  2. Linux网络文件下载

    wget 以网络下载 maven 包为例 wget -c http://mirrors.shu.edu.cn/apache/maven/maven-3/3.5.4/binaries/apache-ma ...

  3. 一些CTF题目--20/9/3

    1. 看源码 POST方法.Extract覆盖. 直接url ?参数不行,因为POST参数不在URL上,GET参数才在 Burpsuite抓包,改成  pass=1&thepassword_1 ...

  4. bnuoj-53073 萌萌哒身高差 【数学】【非原创】

    "清明时节雨纷纷,路上行人欲断魂." 然而wfy同学的心情是愉快的,因为BNU ACM队出去春游啦!并且,嗯... 以下是wfy同学的日记: 昨天,何老师告诉我们:明天我们去春游, ...

  5. Linux 驱动框架---input子系统框架

    前面从具体(Linux 驱动框架---input子系统)的工作过程学习了Linux的input子系统相关的架构知识,但是前面的学习比较实际缺少总结,所以今天就来总结一下输入子系统的架构分层,站到远处来 ...

  6. 高并发之Phaser、ReadWriteLock、StampedLock

    本系列研究总结高并发下的几种同步锁的使用以及之间的区别,分别是:ReentrantLock.CountDownLatch.CyclicBarrier.Phaser.ReadWriteLock.Stam ...

  7. Python_K-means算法

    from sklearn import cluster [centroid, label, inertia] = cluster.k_means(data_to_be_classified, num_ ...

  8. 如何使用 VuePress 搭建一个 element-ui 风格的文档网站

    如何使用 VuePress 搭建一个 element-ui 风格的文档网站 { "devDependencies": { "vuepress": "1 ...

  9. Linux shell script All In One

    Linux shell script All In One refs xgqfrms 2012-2020 www.cnblogs.com 发布文章使用:只允许注册用户才可以访问!

  10. Swift private(set) All In One

    Swift private(set) All In One SwiftUI Getters and Setters https://docs.swift.org/swift-book/Language ...