01-DispatchServlet和RequestMapping

本文详细介绍了如何在SpringMVC中使用@Configuration、@ComponentScan等注解配置内嵌Web服务器,自定义参数和返回值注解,以及创建自定义的参数解析器和返回值处理器。通过示例展示了如何使用MockHttpServletRequest进行测试。
摘要由CSDN通过智能技术生成

SpringMVC启动

server.port=8090
spring.mvc.servlet.load-on-startup=1
@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;
    }

}

public static void main(String[] args) {
    AnnotationConfigServletWebServerApplicationContext applicationContext =
            new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);

}

HandlerMethod

准备自定义注解

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

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

准备控制器

@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

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

测试

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

/**
 * @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

@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

@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()));
}

获取所有参数解析器

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

自定义入参解析器

/**
 * @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

@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()));
}

自定义返回值处理器

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

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

}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

层巅余落日

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值