写在前面

看到这篇博客时,默认你知道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. 容器技术学习系列(一)-Docker基础知识学习

    一.概述 1.Docker是什么? 1)Docker是世界领先的软件容器化平台 2)Docker公司开发,开源,托管在github 3)跨平台,支持Windows.MacOS.Linux 2.Dock ...

  2. IFIX 5.9 历史数据 曲线 (非SQL模式)

    装完 ifix 5.9 默认是没有Hist 开头的 历史数据源的,没存,至少我装的版本是这样. 那个Historian 也没有安装包,好像还要授权,自己研究不了. 1 先把数据存本地 在你的安装包里 ...

  3. Spring(三) Spring IOC

    Spring 核心之 IOC 容器 再谈 IOC 与 DI IOC(Inversion of Control)控制反转:所谓控制反转,就是把原先我们代码里面需要实现的对象创 建.依赖的代码,反转给容器 ...

  4. 51nod 1073约瑟夫环 递归公式法

    约瑟夫环问题的原来描述为,设有编号为1,2,--,n的n(n>0)个人围成一个圈,从第1个人开始报数,报到m时停止报数,报m的人出圈,再从他的下一个人起重新报数,报到m时停止报数,报m的出圈,- ...

  5. C#通过NI-VISA操作Tektronix TBS 2000B系列示波器

    一.概述 本文描述采用C#语言访问控制Tektronix TBS 2000B 系列示波器.接口协议采用NI-VISA. 最近一个项目需要和一款示波器进行通信,需要对示波器进行一些简单控制并获取到波形数 ...

  6. React.createClass vs. ES6 Class Components

    1 1 1 https://www.fullstackreact.com/articles/react-create-class-vs-es6-class-components/ React.crea ...

  7. JavaScript & Error Types

    JavaScript & Error Types JavaScript提供了8个错误对象,这些错误对象会根据错误类型在try / catch表达式中引发: Error EvalError Ra ...

  8. Microsoft Solitaire Collection

    Microsoft Solitaire Collection game https://zone.msn.com/gameplayer/gameplayerHTML.aspx?game=mssolit ...

  9. js & h5 & app & object storage

    js & h5 & app & object storage API https://developer.mozilla.org/en-US/docs/Web/API Stor ...

  10. HTTP/1.1 & HTTP/2 & webpack

    HTTP/1.1 & HTTP/2 & webpack Bundling your application is especially powerful for HTTP/1.1 cl ...