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

}
相关推荐
Bug退退退12328 分钟前
RabbitMQ 高级特性之重试机制
java·分布式·spring·rabbitmq
小皮侠29 分钟前
nginx的使用
java·运维·服务器·前端·git·nginx·github
Zz_waiting.41 分钟前
Javaweb - 10.4 ServletConfig 和 ServletContext
java·开发语言·前端·servlet·servletconfig·servletcontext·域对象
全栈凯哥41 分钟前
02.SpringBoot常用Utils工具类详解
java·spring boot·后端
兮动人1 小时前
获取终端外网IP地址
java·网络·网络协议·tcp/ip·获取终端外网ip地址
呆呆的小鳄鱼1 小时前
cin,cin.get()等异同点[面试题系列]
java·算法·面试
独立开阀者_FwtCoder1 小时前
"页面白屏了?别慌!前端工程师必备的排查技巧和面试攻略"
java·前端·javascript
Touper.1 小时前
JavaSE -- 泛型详细介绍
java·开发语言·算法
静若繁花_jingjing1 小时前
Redis线程模型
java·数据库·redis
hello早上好2 小时前
CGLIB代理核心原理
java·spring