写在前面

看到这篇博客时,默认你知道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. MySQL 索引的类型

    主键索引(PRIMARY KEY) # 主键 = 唯一键索引 + 非空 + 只能设置一个字段 # 创建表的时候创建主键索引 mysql> create table test(id int not ...

  2. MySQL数据库系列(四)- InnoDB下的共享表空间和独立表空间详解

    一.概念 共享表空间: Innodb的所有数据保存在一个单独的表空间里面,而这个表空间可以由很多个文件组成,一个表可以跨多个文件存在,所以其大小限制不再是文件大小的限制,而是其自身的限制.从Innod ...

  3. 爬虫入门六 总结 资料 与Scrapy实例-bibibili番剧信息

    title: 爬虫入门六 总结 资料 与Scrapy实例-bibibili番剧信息 date: 2020-03-16 20:00:00 categories: python tags: crawler ...

  4. 全局ID生成--雪花算法

    分布式ID常见生成策略: 分布式ID生成策略常见的有如下几种: 数据库自增ID. UUID生成. Redis的原子自增方式. 数据库水平拆分,设置初始值和相同的自增步长. 批量申请自增ID. 雪花算法 ...

  5. C++的继承权限

    原文来自于:https://www.cnblogs.com/2018shawn/p/10648408.html 公式: 继承成员对外的访问属性 = Max{继承方式,父类成员访问级别}: ps;以下成 ...

  6. XSS (跨站脚本攻击) 的原理分析,测试 && 应对措施

    1 1 1 XSS (跨站脚本攻击) 的原理分析,测试 1 demo: <!DOCTYPE html> <html lang="en"> <head& ...

  7. MBP 2018

    MBP 2018 touch pad MacBook Pro 如何调节键盘背光 https://support.apple.com/zh-cn/HT202310 F6 & F5 如何清洁 Ma ...

  8. VSCode & disable telemetry reporting

    VSCode & disable telemetry reporting https://code.visualstudio.com/docs/supporting/faq#_how-to-d ...

  9. c++ winapi 让目标程序(target)调用当前程序(local)的函数

    GameCheat 如果你的目标程序是x86/x64, 那么当前程序也需要编译为x84/x64 #include <iostream> #include <string> #i ...

  10. uniapp 发起网络请求

    推荐下我写的uni-http 创建http-config.js import Vue from 'vue' const BASE_URL = 'http://xxx.com'; if (process ...