01-DispatchServlet和RequestMapping

SpringMVC启动

properties 复制代码
server.port=8090
spring.mvc.servlet.load-on-startup=1
java 复制代码
@Configuration
@ComponentScan
//  引入配置
@PropertySource("classpath:application.properties")
@EnableConfigurationProperties({ServerProperties.class, WebMvcProperties.class})
public class WebConfig {

    /**
     * 内嵌web容器工厂
     */
    @Bean
    public TomcatServletWebServerFactory tomcatServletWebServerFactory(ServerProperties serverProperties) {
        return new TomcatServletWebServerFactory(serverProperties.getPort());
    }

    @Bean
    public DispatcherServlet dispatcherServlet() {
        // initStrategies 初始化逻辑
        return new DispatcherServlet();
    }

    /**
     * 注册DispatchServlet  springmvc入口
     */
    @Bean
    public DispatcherServletRegistrationBean dispatcherServletRegistrationBean(
        DispatcherServlet dispatcherServlet,
        WebMvcProperties webMvcProperties
    ) {
        DispatcherServletRegistrationBean registrationBean = new DispatcherServletRegistrationBean(dispatcherServlet, "/");
        // 默认DispatcherServlet是在第一次访问应用时初始化的  这里优先级设置为1  让DispatcherServlet启动时初始化
        registrationBean.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
        return registrationBean;
    }

}
java 复制代码
public static void main(String[] args) {
    AnnotationConfigServletWebServerApplicationContext applicationContext =
            new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);

}

HandlerMethod

准备自定义注解

java 复制代码
/**
 * 自定义参数注解:用于将请求头中的token参数解析出来并放入方法入参(参数解析器)
 */
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Token {
}

/**
 * 自定义方法返回值注解  用户测试自定义方法返回值处理器
 */
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Yml {
}

准备控制器

java 复制代码
@Slf4j
@Controller
public class TestController {


    /**
     * 测试get请求
     *
     * @return
     */
    @GetMapping("/test1")
    public ModelAndView test1() {
        log.debug("test1");
        return null;
    }

    /**
     * 测试带入参注解的post请求
     *
     * @param name
     * @return
     */
    @PostMapping("/test2")
    public ModelAndView test2(@RequestParam String name) {
        log.debug("test2({})", name);
        return null;
    }

    /**
     * 测试带自定义注解的入参处理器   put请求
     *
     * @param token
     * @return
     */
    @PutMapping("/test3")
    public ModelAndView test3(@Token String token) {
        log.debug("test3({})", token);
        return null;
    }

    /**
     * 不定义请求类型  自定义返回类型处理器
     *
     * @return
     */
    @RequestMapping("/test4")
    @Yml
    public User test4() {
        log.debug("test4()");
        return new User("张三", 30);
    }

}

@Data
@AllArgsConstructor
class User {
    private String name;
    private int age;

    public static void main(String[] args) {
        String userYaml = new Yaml().dump(new User("张三", 30));
        System.out.println(userYaml);
    }

}

准备RequestMappingHandlerMapping

java 复制代码
/**
 * 如果使用DispatcherServlet.properties配置文件中的默认RequestMappingHandlerMapping,其不会作为spring的bean
 * 不方便后续测试  这里new一个RequestMappingHandlerMapping并存入spring容器  方便后续测试
 */
@Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
    return new RequestMappingHandlerMapping();
}

测试

java 复制代码
public static void testHandlerMethod(ApplicationContext applicationContext) {
    // 作用: 解析@RequestMapping以及其派生注解  生成请求路径与处理方法的映射关系  在初始化时就生成了
    RequestMappingHandlerMapping handlerMapping = applicationContext.getBean(RequestMappingHandlerMapping.class);
    Map<RequestMappingInfo, HandlerMethod> handlerMethods = handlerMapping.getHandlerMethods();

    handlerMethods.forEach((k, v) -> {
        System.out.println(k + "=" + v);
    });

    /**
     * {GET [/test1]}=com.you.meet.nice.test.web.springmvc.dispatchservlet.TestController#test1()
     * {POST [/test2]}=com.you.meet.nice.test.web.springmvc.dispatchservlet.TestController#test2(String)
     * { [/test4]}=com.you.meet.nice.test.web.springmvc.dispatchservlet.TestController#test4()
     * {PUT [/test3]}=com.you.meet.nice.test.web.springmvc.dispatchservlet.TestController#test3(String)
     */

}

RequestMappingHandlerAdapter

准备自定义RequestMappingHandlerAdapter Bean

java 复制代码
/**
 * @author zhoujunlin
 * @date 2024/3/14 22:12
 * @desc 继承RequestMappingHandlerAdapter,重新invokeHandlerMethod方法
 * 因为此方法是protected的,外部无法调用  继承后修改为public便于测试
 */
public class MyRequestMappingHandlerAdapter extends RequestMappingHandlerAdapter {

    @Override
    public ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
        return super.invokeHandlerMethod(request, response, handlerMethod);
    }
}


/**
 * 继承RequestMappingHandlerAdapter,并放入Bean容器  替换DispatcherServlet.properties配置中的默认RequestMappingHandlerAdapter
 */
@Bean
public MyRequestMappingHandlerAdapter myRequestMappingHandlerAdapter() {
    MyRequestMappingHandlerAdapter handlerAdapter = new MyRequestMappingHandlerAdapter();
    return handlerAdapter;
}

test1

java 复制代码
@SneakyThrows
public static void test1(ApplicationContext applicationContext) {
    RequestMappingHandlerMapping handlerMapping = applicationContext.getBean(RequestMappingHandlerMapping.class);
    MockHttpServletRequest request = new MockHttpServletRequest("GET", "/test1");
    MockHttpServletResponse response = new MockHttpServletResponse();
    // 获取Mock请求对应的处理器方法  放回执行器链对象
    HandlerExecutionChain handlerExecutionChain = handlerMapping.getHandler(request);
    System.out.println("handlerExecutionChain = " + handlerExecutionChain);
    /**
     * 对应一个处理器方法和0个拦截器
     * HandlerExecutionChain with [com.you.meet.nice.test.web.springmvc.dispatchservlet.TestController#test1()] and 0 interceptors
     */
    System.out.println("========>");
    MyRequestMappingHandlerAdapter handlerAdapter = applicationContext.getBean(MyRequestMappingHandlerAdapter.class);
    handlerAdapter.invokeHandlerMethod(request, response, ((HandlerMethod) handlerExecutionChain.getHandler()));
}

test2

java 复制代码
@SneakyThrows
public static void test2(ApplicationContext applicationContext) {
    RequestMappingHandlerMapping handlerMapping = applicationContext.getBean(RequestMappingHandlerMapping.class);
    MockHttpServletRequest request = new MockHttpServletRequest("POST", "/test2");
    request.addParameter("name", "zhou");
    MockHttpServletResponse response = new MockHttpServletResponse();
    HandlerExecutionChain handlerExecutionChain = handlerMapping.getHandler(request);
    System.out.println("========>");
    MyRequestMappingHandlerAdapter handlerAdapter = applicationContext.getBean(MyRequestMappingHandlerAdapter.class);
    handlerAdapter.invokeHandlerMethod(request, response, ((HandlerMethod) handlerExecutionChain.getHandler()));
}

获取所有参数解析器

java 复制代码
public static void testAllArgumentResolver(ApplicationContext applicationContext) {
    // 获取所有参数解析器
    MyRequestMappingHandlerAdapter handlerAdapter = applicationContext.getBean(MyRequestMappingHandlerAdapter.class);
    handlerAdapter.getArgumentResolvers().forEach(System.out::println);
}

自定义入参解析器

java 复制代码
/**
 * @author zhoujunlin
 * @date 2024/3/14 22:23
 * @desc 自定义Token注解对应的入参解析器
 */
public class TokenArgumentResolver implements HandlerMethodArgumentResolver {

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        // 只有入参中有Token注解才支持解析
        Token token = parameter.getParameterAnnotation(Token.class);
        return Objects.nonNull(token);
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        return webRequest.getHeader("token");
    }

}


/**
 * 继承RequestMappingHandlerAdapter,并放入Bean容器  替换DispatcherServlet.properties配置中的默认RequestMappingHandlerAdapter
 */
@Bean
public MyRequestMappingHandlerAdapter myRequestMappingHandlerAdapter() {
    MyRequestMappingHandlerAdapter handlerAdapter = new MyRequestMappingHandlerAdapter();

    TokenArgumentResolver tokenArgumentResolver = new TokenArgumentResolver();
    handlerAdapter.setCustomArgumentResolvers(CollUtil.newArrayList(tokenArgumentResolver));

    return handlerAdapter;
}

test3

java 复制代码
@SneakyThrows
public static void test3(ApplicationContext applicationContext) {
    RequestMappingHandlerMapping handlerMapping = applicationContext.getBean(RequestMappingHandlerMapping.class);
    MockHttpServletRequest request = new MockHttpServletRequest("PUT", "/test3");
    request.addParameter("token", "混淆视听");
    request.addHeader("token", "某个token");
    MockHttpServletResponse response = new MockHttpServletResponse();
    HandlerExecutionChain handlerExecutionChain = handlerMapping.getHandler(request);
    System.out.println("========>");
    MyRequestMappingHandlerAdapter handlerAdapter = applicationContext.getBean(MyRequestMappingHandlerAdapter.class);
    handlerAdapter.invokeHandlerMethod(request, response, ((HandlerMethod) handlerExecutionChain.getHandler()));
}

自定义返回值处理器

java 复制代码
public class YmlReturnValueHandler implements HandlerMethodReturnValueHandler {

    @Override
    public boolean supportsReturnType(MethodParameter returnType) {
        Yml yml = returnType.getMethodAnnotation(Yml.class);
        // 只有方法上存在Yml注解才解析
        return Objects.nonNull(yml);
    }

    @Override
    public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
        // 1. 对象转yml
        String ret = new Yaml().dump(returnValue);

        // 2. 将结果写入响应
        HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
        response.setContentType("text/plain;charset=utf-8");
        response.getWriter().print(ret);

        // 3. 设置请求已处理完毕
        mavContainer.setRequestHandled(true);

    }
}

/**
 * 继承RequestMappingHandlerAdapter,并放入Bean容器  替换DispatcherServlet.properties配置中的默认RequestMappingHandlerAdapter
 */
@Bean
public MyRequestMappingHandlerAdapter myRequestMappingHandlerAdapter() {
    MyRequestMappingHandlerAdapter handlerAdapter = new MyRequestMappingHandlerAdapter();

    TokenArgumentResolver tokenArgumentResolver = new TokenArgumentResolver();
    handlerAdapter.setCustomArgumentResolvers(CollUtil.newArrayList(tokenArgumentResolver));

    YmlReturnValueHandler ymlReturnValueHandler = new YmlReturnValueHandler();
    handlerAdapter.setReturnValueHandlers(CollUtil.newArrayList(ymlReturnValueHandler));

    return handlerAdapter;
}

test4

java 复制代码
@SneakyThrows
public static void test4(ApplicationContext applicationContext) {
    RequestMappingHandlerMapping handlerMapping = applicationContext.getBean(RequestMappingHandlerMapping.class);
    MockHttpServletRequest request = new MockHttpServletRequest("DELETE", "/test4");
    request.addParameter("token", "混淆视听");
    request.addHeader("token", "某个token");
    MockHttpServletResponse response = new MockHttpServletResponse();
    HandlerExecutionChain handlerExecutionChain = handlerMapping.getHandler(request);
    System.out.println("========>");
    MyRequestMappingHandlerAdapter handlerAdapter = applicationContext.getBean(MyRequestMappingHandlerAdapter.class);
    handlerAdapter.invokeHandlerMethod(request, response, ((HandlerMethod) handlerExecutionChain.getHandler()));

    byte[] ret = response.getContentAsByteArray();
    System.out.println(new String(ret, StandardCharsets.UTF_8));

}
相关推荐
书中自有妍如玉5 分钟前
.net 使用MQTT订阅消息
java·前端·.net
风铃儿~31 分钟前
Spring AI 入门:Java 开发者的生成式 AI 实践之路
java·人工智能·spring
斯普信专业组36 分钟前
Tomcat全方位监控实施方案指南
java·tomcat
忆雾屿1 小时前
云原生时代 Kafka 深度实践:06原理剖析与源码解读
java·后端·云原生·kafka
武昌库里写JAVA1 小时前
iview Switch Tabs TabPane 使用提示Maximum call stack size exceeded堆栈溢出
java·开发语言·spring boot·学习·课程设计
gaoliheng0061 小时前
Redis看门狗机制
java·数据库·redis
我是唐青枫1 小时前
.NET AOT 详解
java·服务器·.net
Su米苏2 小时前
Axios请求超时重发机制
java
本郡主是喵3 小时前
并发编程 - go版
java·服务器·开发语言