自定义Spring-MVC HandlerMapping Demo

15 篇文章 0 订阅
13 篇文章 0 订阅

自定义Spring-MVC HandlerMapping Demo

  1. 这里就不介绍Spring-MVC 的流程了,不懂的可以百度;
  2. 你都知道什么是HandlerMapping了,应该是知道什么Spring-MVC的工作流程
  3. 接下来就是就是demo示例了。

我们先分析一下Spring-MVC的自带RequestMappingHandlerMapping映射器

这是RequestMappingHandlerMapping 的继承树
在这里插入图片描述

顾名思义这个就是解析我们@RequestMapping()的映射器,我后面就模仿一个@ZkqMapping()

Spring Boot 再启动的时会自动调用该方法,把注入的Spring bean进行验证当前bean是否支持该映射器
在这里插入图片描述

这里会循环说有HandlerMapping 映射器,只要不为空就表示匹配成功!这个映射器就能解析我们的请求(如果出现多个可以解析的HandlerMapping ,那么谁在前面就用谁,源码中可以看出。不为空就直接返回,不在继续循环)
在这里插入图片描述
getHandler 有调用的当前getHandlerInternal 这个是确定当HandlerMapping 有没有能处理该请求的方法
在这里插入图片描述
getHandlerInternal 内部查找有没有能处理该请求的方法内有就返回null ,然后就出栈了,上面就会循环下一个HandlerMapping 直到找到为止(所以我们常用的RequestMappingHandlerMapping一般都在第一个提高效率)
在这里插入图片描述
到此我们大概了解的我们常用的RequestMappingHandlerMapping 的工作流程,接下老我们模仿一个。。。

2. 自定义HandlerMapping代码实现

2.1 编写说明

这里我把Spring的 RequestMappingHandlerMapping和RequestMappingInfoHandlerMapping和二为一。

当然也可以直接继承RequestMappingInfoHandlerMapping 示例:https://www.cnblogs.com/fjrgg/p/14523883.html

这是我的继承树
在这里插入图片描述

2.2 代码编写

我们先定义个类似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.*;

/**
 * @Description Description
 * @Author 张凯强
 * @Date Created in 2021/9/24
 * @E-mail 862166318@qq.com
 */

@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;

/**
 * @Description Description
 * @Author 张凯强
 * @Date Created in 2021/9/17
 * @E-mail 862166318@qq.com
 */

// 把当前类注入spring IOC 容器,不然Spring 无法添加当前映射器
@Component
public class ZkqHandlerMapping  extends AbstractHandlerMethodMapping<RequestMappingInfo> implements 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<String> getMappingPathPatterns(RequestMappingInfo requestMappingInfo) {
        return requestMappingInfo.getPatternsCondition().getPatterns();
    }

    // 检查一个映射是否与当前请求匹配,并返回一个与当前请求相关的条件的(可能是新的)映射。
    @Override
    protected RequestMappingInfo getMatchingMapping(RequestMappingInfo requestMappingInfo, HttpServletRequest httpServletRequest) {
        return requestMappingInfo.getMatchingCondition(httpServletRequest);
    }

    // 返回一个比较器,用于对匹配的映射进行排序。 返回的比较器应该将“更好”的匹配排序得更高。
    @Override
    protected Comparator<RequestMappingInfo> 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<String> 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

第二次循环才匹配,因为第一个是我们自定义的
在这里插入图片描述
响应没问题
在这里插入图片描述

3.2 自定义请求

只定义请求地址: http://localhost:8080/yue

第一次就匹配成功了
在这里插入图片描述
响应也正常
在这里插入图片描述
到此结束,通过此案例我们大概了解了RequestMappingHandlerMapping的映射流程,以及实现了一个简单的demo。有感兴趣的可以了解其他的HandlerMapping。(欢迎交流技术,大神勿喷!)

要在Eclipse中扩展Spring MVC,您需要执行以下步骤: 1. 安装Spring IDE插件 打开Eclipse并转到“Help”菜单。选择“Eclipse Marketplace”选项。在搜索栏中输入“Spring IDE”并按Enter键。在搜索结果中选择“Spring IDE”并单击“Install”按钮。跟随安装向导完成安装。 2. 创建Spring MVC项目 转到“File”菜单并选择“New”>“Spring Legacy Project”>“Spring MVC Project”。在“New Spring MVC Project”对话框中,输入您的项目名称并单击“Next”按钮。在“New Spring MVC Project”对话框中,选择“MVC Project”并单击“Next”按钮。在“New Spring MVC Project”对话框中,选择“Spring MVC Version”和“MVC Project Template”并单击“Finish”按钮。您的Spring MVC项目现在已创建。 3. 创建控制器类 在项目中创建一个新的Java类。这将是您的控制器类。在类中添加一个@RequestMapping注释,并为其指定一个URL映射。例如,@RequestMapping(“/hello”)。 4. 配置Spring MVC 在项目中创建一个新的XML文件并命名为“servlet-context.xml”。在此文件中,定义您的控制器类,并为其指定一个处理程序映射器和处理程序适配器。例如: ```xml <bean name="/hello" class="com.example.HelloController"/> <bean id="handlerMapping" class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/> <bean id="handlerAdapter" class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/> ``` 5. 运行应用程序 转到“Run”菜单并选择“Run As”>“Run on Server”。选择您喜欢的服务器并单击“Finish”按钮。您的应用程序现在应在服务器上运行,并且可以通过浏览器访问您在控制器类中指定的URL映射。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值