首页 > 编程语言 > 详细

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

时间:2020-06-18 20:22:52      阅读:55      评论:0      收藏:0      [点我收藏+]

增加对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>

测试处理请求流程

  • localhost\addUser 直接编写Servlet处理
  • localhost\addUser2 --->DispatcherServlet --->BeanNameUrlHandlerMapping--->HttpRequestHandlerAdapter---> HttpRequestHandler 的 AddUserHandler
  • localhost\addUser3 --->DispatcherServlet --->SimpleUrlHandlerMapping---> AddUserHandler
  • localhost\addUser4 --->DispatcherServlet --->RequestMappingHandlerMapping---> RequestMappingHandlerAdapter--->UserController

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

原文:https://www.cnblogs.com/kuotian/p/13159477.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!