- 这里就不介绍Spring-MVC 的流程了,不懂的可以百度;
- 你都知道什么是HandlerMapping了,应该是知道什么Spring-MVC的工作流程
- 接下来就是就是demo示例了。
这是RequestMappingHandlerMapping 的继承树
顾名思义这个就是解析我们@RequestMapping()的映射器,我后面就模仿一个@ZkqMapping()
Spring Boot 再启动的时会自动调用该方法,把注入的Spring bean进行验证当前bean是否支持该映射器
这里会循环说有HandlerMapping 映射器,只要不为空就表示匹配成功!这个映射器就能解析我们的请求(如果出现多个可以解析的HandlerMapping ,那么谁在前面就用谁,源码中可以看出。不为空就直接返回,不在继续循环)
getHandler 有调用的当前getHandlerInternal 这个是确定当HandlerMapping 有没有能处理该请求的方法
getHandlerInternal 内部查找有没有能处理该请求的方法内有就返回null ,然后就出栈了,上面就会循环下一个HandlerMapping 直到找到为止(所以我们常用的RequestMappingHandlerMapping一般都在第一个提高效率)
到此我们大概了解的我们常用的RequestMappingHandlerMapping 的工作流程,接下老我们模仿一个。。。
这里我把Spring的 RequestMappingHandlerMapping和RequestMappingInfoHandlerMapping和二为一。
当然也可以直接继承RequestMappingInfoHandlerMapping 示例:https://www.cnblogs.com/fjrgg/p/14523883.html
这是我的继承树
我们先定义个类似SpringMVC中的@RequestMapping 注解 名字为 @ZkqMapping
import org.springframework.core.annotation.AliasFor;
import org.springframework.web.bind.annotation.Mapping;
import org.springframework.web.bind.annotation.RequestMethod;
import java.lang.annotation.*;
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@documented
@Mapping
public @interface ZkqMapping {
String name() default "";
@AliasFor("path")
String[] value() default {};
@AliasFor("value")
String[] path() default {};
RequestMethod[] method() default {};
String[] params() default {};
String[] headers() default {};
String[] consumes() default {};
String[] produces() default {};
}
我们编写映射器 ,有很多都是直接复制RequestMappingInfoHandlerMapping 的代码,我要只要是定义isHandler()、getMappingForMethod(),这连个方法即可
import org.springframework.context.EmbeddedValueResolverAware; import org.springframework.core.Ordered; import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.stereotype.Component; import org.springframework.stereotype.Controller; import org.springframework.util.StringValueResolver; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.handler.AbstractHandlerMethodMapping; import org.springframework.web.servlet.handler.MatchableHandlerMapping; import org.springframework.web.servlet.handler.RequestMatchResult; import org.springframework.web.servlet.mvc.condition.ProducesRequestCondition; import org.springframework.web.servlet.mvc.method.RequestMappingInfo; import javax.servlet.http.HttpServletRequest; import java.lang.reflect.Method; import java.util.Comparator; import java.util.Set; // 把当前类注入spring IOC 容器,不然Spring 无法添加当前映射器 @Component public class ZkqHandlerMapping extends AbstractHandlerMethodMappingimplements MatchableHandlerMapping, EmbeddedValueResolverAware { private RequestMappingInfo.BuilderConfiguration config = new RequestMappingInfo.BuilderConfiguration(); private StringValueResolver embeddedValueResolver; // 这个是提升我们的HandlerMapping等级(重写了父类的方法还有其他方式这里不在演示) int HIGHEST_PRECEDENCE = Integer.MIN_VALUE; @Override public int getOrder() { return Ordered.HIGHEST_PRECEDENCE; } // 判断这个类的公共方法上是否有@ZkqMapping 注解,有表示该映射器支持当前类的映射关系,否则不支持 @Override protected boolean isHandler(Class> aClass) { return (AnnotatedElementUtils.hasAnnotation(aClass, Controller.class) || AnnotatedElementUtils.hasAnnotation(aClass, ZkqMapping.class)); } // 返回映射的方法 @Override protected RequestMappingInfo getMappingForMethod(Method method, Class> aClass) { ZkqMapping zkqMapping = method.getAnnotation(ZkqMapping.class); return zkqMapping==null?null:RequestMappingInfo .paths(zkqMapping.value()) .methods(zkqMapping.method()) .params(zkqMapping.params()) .headers(zkqMapping.headers()) .consumes(zkqMapping.consumes()) .produces(zkqMapping.produces()) .mappingName(zkqMapping.name()) .options(config) .build(); } // 返回所提供映射中包含的URL路径。 @Override protected Set getMappingPathPatterns(RequestMappingInfo requestMappingInfo) { return requestMappingInfo.getPatternsCondition().getPatterns(); } // 检查一个映射是否与当前请求匹配,并返回一个与当前请求相关的条件的(可能是新的)映射。 @Override protected RequestMappingInfo getMatchingMapping(RequestMappingInfo requestMappingInfo, HttpServletRequest httpServletRequest) { return requestMappingInfo.getMatchingCondition(httpServletRequest); } // 返回一个比较器,用于对匹配的映射进行排序。 返回的比较器应该将“更好”的匹配排序得更高。 @Override protected Comparator getMappingComparator(final HttpServletRequest httpServletRequest) { return (info1, info2) -> info1.compareTo(info2, httpServletRequest); } @Override public void setEmbeddedValueResolver(StringValueResolver resolver) { this.embeddedValueResolver = resolver; } // 这里匹配成功,可以给request里面放值,在想要的环节可以取出 @Override protected void handleMatch(RequestMappingInfo mapping, String lookupPath, HttpServletRequest request) { System.out.println(lookupPath); // super.handleMatch(mapping, lookupPath, request); } @Override protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception { request.removeAttribute(PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE); try { return super.getHandlerInternal(request); } finally { ProducesRequestCondition.clearMediaTypesAttribute(request); } } @Override public RequestMatchResult match(HttpServletRequest request, String pattern) { RequestMappingInfo info = RequestMappingInfo.paths(pattern).options(this.config).build(); RequestMappingInfo matchingInfo = info.getMatchingCondition(request); if (matchingInfo == null) { return null; } Set patterns = matchingInfo.getPatternsCondition().getPatterns(); String lookupPath = getUrlPathHelper().getLookupPathForRequest(request, LOOKUP_PATH); return new RequestMatchResult(patterns.iterator().next(), lookupPath, getPathMatcher()); }
启动类和Controller的编写
import com.zkq.springdemo.config.ZkqMapping;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@SpringBootApplication
public class SpringDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringDemoApplication.class, args);
}
// 内部类 (不想在创建包,毕竟只是demo)
@RestController
public class TestController{
// SpringMVC的原生映射
@GetMapping("test")
public String test(){
return "zkq";
}
// 我们自己的映射
@ZkqMapping("zkq")
public String zkq(){
return "自定义";
}
@ZkqMapping("yue")
public String yue(){
return "月光之上";
}
}
}
3. 测试代码
3.1 原生请求
我们先访问原生的映射 http://localhost:8080/test
第二次循环才匹配,因为第一个是我们自定义的
响应没问题
只定义请求地址: http://localhost:8080/yue
第一次就匹配成功了
响应也正常
到此结束,通过此案例我们大概了解了RequestMappingHandlerMapping的映射流程,以及实现了一个简单的demo。有感兴趣的可以了解其他的HandlerMapping。(欢迎交流技术,大神勿喷!)



