• 手写 简易 Spring MVC v2(增加对annotation的支持)


    增加对annotation的支持,查找@Controller下的@RequestMapping
    代码:https://github.com/kuotian/springmvc_me

    1. 注解

    @Controller

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Controller {    
        //Controller(value="")    
        String value() default "";
    }
    

    @RequestMapping

    @Target({ ElementType.TYPE, ElementType.METHOD })
    @Retention(RetentionPolicy.RUNTIME)
    public @interface RequestMapping {    
        // RequestMapping(value="")    
        String value() default "";
    }
    

    @ResponseBody

    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface ResponseBody {    
        // RequestMapping(value="")    
        String value() default "";
    }
    

    @Target@Retention:元注解,作用就是给注解进行解释的
    @Target:最终我们这个注解是要作用在什么地方(类、方法或者字段)
    ElementType.TYPE:类
    ElementType.METHOD:方法
    Retention:最终我们这个注解是要保留到哪个阶段(源代码、class文件、运行时环境)

    2. UserController 新的Handler

    使用注解方式去编写的Handler,一个handler类中可以处理N个请求,和HttpRequestHandler是有很大的进步的。

    UserController

    • 类上加@Controller注解
    • 方法上加@RequestMapping注解(便于HandlerMapping建立注解方式下的请求映射关系)
      方法参数需要知道请求中的参数有哪些,我们就可以一一对应的写到我们Controller方法参数中去获取这些参数
    package com.hdu.springmvc.controller;
    
    //使用@Controller注解的类,就表示是一个Handler类
    @Controller
    public class UserController {
        // http请求:http://localhost/queryUser4?id=10&name=messi
        @RequestMapping("/queryUser4")
        @ResponseBody
        public Map<String, Object> queryUser(Integer id, String name) {
            Map<String, Object> map = new HashMap<String, Object>();
            map.put("id", id);
            map.put("name", name);
            return map;
        }
    
        // http请求:http://localhost/queryUser4?id=10&name=messi
        @RequestMapping("/addUser4")
        @ResponseBody
        public String addUser() {
            return "addUser4";
        }
    }
    

    3. 编写新的HandlerAdapter

    RequestMappingHandlerAdapter

    package com.hdu.springmvc.handleradapter;
    
    public class RequestMappingHandlerAdapter implements HandlerAdapter {
        @Override
        public ModelAndView handleRequest(Object handler, HttpServletRequest request, HttpServletResponse response) throws Exception {
            // 1.将Object类型的handler强转为HandlerMethod类型
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            // 2.取出Method对象和Controller对象
            Method method = handlerMethod.getMethod();
            Object controller = handlerMethod.getHandler();
            // 3.要处理方法参数的绑定(请求URL中的参数是字符串类型,而Controller方法的参数类型是多种多样的)
            Object[] args = getParameter(request, method);
            // 4.通过反射调用对应的方法,并得到返回值(就是调用Controller类中被RequestMapping注解所对应的方法)
            Object returnValue = method.invoke(controller, args);
            // 5.处理返回值
            handleReturnValue(response, method, returnValue);
            return null;
        }
    
        private Object[] getParameter(HttpServletRequest request, Method method) {
            List<Object> parameterList = new ArrayList<Object>();
            // 将请求URL中的参数,获取到一个Map集合中
            Map<String, String[]> parameterMap = request.getParameterMap();
            // 获取Controller方法的参数
            Parameter[] parameters = method.getParameters();
            for (Parameter parameter : parameters) {
                // 获取参数名称
                String name = parameter.getName();
                // 需要根据方法中的参数名称去获取parameterMap中对应key的值
                String[] stringValue = parameterMap.get(name);
    
                // 获取目标方法参数类型
                Class<?> type = parameter.getType();
                // 类型转换
                Object value = convertValue(stringValue, type);
                parameterList.add(value);
            }
            return parameterList.toArray();
        }
    
        private Object convertValue(String[] stringValue, Class<?> type) {
            if (stringValue == null || stringValue.length == 0) {
                return null;
            }
            // TODO 使用策略模式去优化
            if (type == List.class) {
                return stringValue;
            } else if (type == Integer.class) {
                return Integer.parseInt(stringValue[0]);
            } else if (type == String.class) {
                return stringValue[0];
            }
            return null;
        }
    
        private void handleReturnValue(HttpServletResponse response, Method method, Object returnValue) throws Exception {
            // 是否带有@ResponseBody注解
            if (method.isAnnotationPresent(ResponseBody.class)) {
                if (returnValue instanceof String) {
                    response.setContentType("text/plain;charset=utf8");
                    response.getWriter().write(returnValue.toString());
                } else if (returnValue instanceof Map) {
                    response.setContentType("application/json;charset=utf8");
                    response.getWriter().write(JsonUtils.object2Json(returnValue));
                } // ....
            } else {
                // 页面跳转处理
            }
        }
    
        @Override
        public boolean supports(Object handler) {
            return (handler instanceof HandlerMethod);
        }
    }
    

    4. 编写新的HandlerMapping

    ​ RequestMapping注解中的请求URL和它修饰的方法之间建立了映射关系。需要针对这种映射关系进行解析,然后放入Map集合中。
    ​ 按理说一个请求URL应该对应一个Handler类。而现在我们对应的是一个Controller方法。

    ​ 在注解方式编写的Controller类这种实现,Controller类不是真正的Handler类,真正的Handler类是Controller类+对应的方法

    ​ 对于这个组合,我们提供了一个对象去封装他们,这个对象HandlerMethod对象(【Object handler,Method method】)
    ​ 最终我们得出结论:请求URLHandlerMethod建立映射关系,HandlerMethod才是DispatcherServlet类中需要的Handler类,而Controller不是Handler类。

    RequestMappingHandlerMapping

    package com.hdu.springmvc.handlermapping;
    
    // 对于注解方式的处理器建立映射关系
    public class RequestMappingHandlerMapping implements HandlerMapping, BeanFactoryAware {
    
        private DefaultListableBeanFactory beanFactory;
        // uri和处理器对象的映射集合
        private Map<String, Object> mappings = new HashMap<>();
    
        public void init() {
            try {
                // 1.要获取所有的BeanDefinition,获取Class对象
                String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames();
                for (String beanDefinitionName : beanDefinitionNames) {
                    BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanDefinitionName);
                    String beanClassName = beanDefinition.getBeanClassName();
                    Class<?> clazz = Class.forName(beanClassName);
                    // 2. 查找带有Controller注解的类
                    if(isHandler(clazz)){
                        Method[] methods = clazz.getDeclaredMethods();
                        for (Method method : methods) {
                            // 3.获取该Controller类中所有带@RequestMapping注解的方法
                            if (method.isAnnotationPresent(RequestMapping.class)) {
                                // 4.取出@RequestMapping注解中的value值(请求URL)。
                                RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
                                String url = requestMapping.value();
                                // 5.获取@RequestMapping注解对应的方法的Method对象
                                // 6.根据BeanDefinition中的beanName从ioc容器中获取Controller对象
                                Object controller = beanFactory.getBean(beanDefinitionName);
                                // 7.讲Method对象和Controller对象封装到HandlerMethod对象中
                                HandlerMethod handlerMethod = new HandlerMethod(controller, method);
                                // 8.建立映射关系
                                mappings.put(url, handlerMethod);
                            }
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        private boolean isHandler(Class<?> clazz) {
            return (clazz.isAnnotationPresent(Controller.class) || clazz.isAnnotationPresent(RequestMapping.class));
        }
    
        @Override
        public Object getHandler(HttpServletRequest request) throws Exception {
            String uri = request.getRequestURI();
            return mappings.get(uri);
        }
    
        @Override
        public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
            this.beanFactory =  (DefaultListableBeanFactory) beanFactory;
        }
    }
    

    HandlerMethod

    Method对象和Controller对象封装到HandlerMethod对象中。

    package com.hdu.springmvc.handlermapping;
    
    public class HandlerMethod {
        private Object handler; // Controller
        private Method method; // Method
    
        public Object getHandler() {
            return handler;
        }
    
        public void setHandler(Object handler) {
            this.handler = handler;
        }
    
        public Method getMethod() {
            return method;
        }
    
        public void setMethod(Method method) {
            this.method = method;
        }
    
        public HandlerMethod(Object handler, Method method) {
            super();
            this.handler = handler;
            this.method = method;
        }
    }
    

    5.springmvc.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
                            http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <!-- 首先配置handler,交给spring容器去管理该bean -->
        <!-- 其次配置name属性为uri,便于建立映射关系 -->
        <bean name="/addUser2"
              class="com.hdu.springmvc.handler.AddUserHandler"></bean>
        <bean name="/queryUser2"
              class="com.hdu.springmvc.handler.QueryUserHandler"></bean>
    
        <!-- 配置处理器映射器 -->
        <bean class="com.hdu.springmvc.handlermapping.BeanNameUrlHandlerMapping"
              init-method="init"></bean>
        <bean class="com.hdu.springmvc.handlermapping.SimpleUrlHandlerMapping"
              init-method="init"></bean>
        <bean class="com.hdu.springmvc.handlermapping.RequestMappingHandlerMapping"
              init-method="init"></bean>
    
        <!-- 配置注解方式的处理器 -->
        <bean class="com.hdu.springmvc.controller.UserController"></bean>
    
        <!-- 配置处理器适配器 -->
        <bean class="com.hdu.springmvc.handleradapter.HttpRequestHandlerAdapter"></bean>
        <bean class="com.hdu.springmvc.handleradapter.RequestMappingHandlerAdapter"></bean>
    </beans>
    

    6.web.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
             version="4.0">
        <servlet>
            <servlet-name>AddUserServlet</servlet-name>
            <servlet-class>com.hdu.springmvc.servlet.AddUserServlet</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>AddUserServlet</servlet-name>
            <url-pattern>/addUser</url-pattern>
        </servlet-mapping>
    
        <servlet>
            <servlet-name>QueryUserServlet</servlet-name>
            <servlet-class>com.hdu.springmvc.servlet.QueryUserServlet</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>QueryUserServlet</servlet-name>
            <url-pattern>/queryUser</url-pattern>
        </servlet-mapping>
    
        <servlet>
            <servlet-name>DispatcherServlet</servlet-name>
            <servlet-class>com.hdu.springmvc.servlet.DispatcherServlet</servlet-class>
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>springmvc.xml</param-value>
            </init-param>
        </servlet>
        <servlet-mapping>
            <servlet-name>DispatcherServlet</servlet-name>
            <url-pattern>/</url-pattern>
        </servlet-mapping>
    </web-app>
    

    测试处理请求流程

    • localhostaddUser 直接编写Servlet处理
    • localhostaddUser2 --->DispatcherServlet --->BeanNameUrlHandlerMapping--->HttpRequestHandlerAdapter---> HttpRequestHandler 的 AddUserHandler
    • localhostaddUser3 --->DispatcherServlet --->SimpleUrlHandlerMapping---> AddUserHandler
    • localhostaddUser4 --->DispatcherServlet --->RequestMappingHandlerMapping---> RequestMappingHandlerAdapter--->UserController
  • 相关阅读:
    mysql max_allowed_packet设置及问题
    mybatis之foreach用法
    uniapp编译小程序分包配置
    鼠标点击页面显示文字
    element ui el-date-picker 判断所选时间是否交叉
    前端处理跨域的几种方式
    uniapp的ajax封装请求
    js获取两个时间差
    vue文件下载功能
    nextTick使用
  • 原文地址:https://www.cnblogs.com/kuotian/p/13159477.html
Copyright © 2020-2023  润新知