自己实现spring(二) —— mvc框架的实现

  1. DispatchServlet初始化会调用initStrategies()方法
protected void initStrategies(ApplicationContext context) {
        //初始化多媒体解析器
        initMultipartResolver(context);
        //初始化语言支持
        initLocaleResolver(context);
        //初始化主题
        initThemeResolver(context);
        //初始化url-->Handler
        initHandlerMappings(context);
        //初始化HandlerAdapter具体的匹配调用过程
        initHandlerAdapters(context);
        //初始化调用异常处理器
        initHandlerExceptionResolvers(context);
        initRequestToViewNameTranslator(context);
        //初始化视图解析器
        initViewResolvers(context);
        initFlashMapManager(context);
  1. 请求来的时候会调用doService(HttpServletRequest request, HttpServletResponse response)方法,然后调用doDispatch(request, response);
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
    ...
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
    ...
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

根据url正则匹配到mappedHandler 然后再拿到HandlerAdapter ha 通过ha反射调用具体对应的方法

下面开始撸我们自己的mvc框架,也按照springmvc的套路来

现在web.xml配置我们的servlet

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:javaee="http://java.sun.com/xml/ns/javaee"
    xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
    version="2.4">
    <servlet>
        <servlet-name>SpringMVC</servlet-name>
        <servlet-class>com.fanday.mvc.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>applicationContext.properties</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>SpringMVC</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
</web-app>

然后定义我们自己的Adapter和HandlerAdapter类

package com.fanday.mvc;

import java.lang.reflect.Method;
import java.util.regex.Pattern;

public class Handler {

    //调用对应的具体controller对象
    private Object controller;
    //和url匹配的方法
    private Method method;
    //对应RequestMapping的url正则
    private Pattern pattern;

    public Handler(Object controller, Method method, Pattern pattern) {
        this.controller = controller;
        this.method = method;
        this.pattern = pattern;
    }

    public Object getController() {
        return controller;
    }

    public void setController(Object controller) {
        this.controller = controller;
    }

    public Method getMethod() {
        return method;
    }

    public void setMethod(Method method) {
        this.method = method;
    }

    public Pattern getPattern() {
        return pattern;
    }

    public void setPattern(Pattern pattern) {
        this.pattern = pattern;
    }
}
package com.fanday.mvc;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
import java.util.Map;

public class HandlerAdapter {
    //保存对应的RequestParam  value==>参数的位置
    //或者HttpRequestServlet.getName()===>index
    private Map<String, Integer> paramType;

    public HandlerAdapter(Map<String, Integer> paramType) {
        this.paramType = paramType;
    }

    /**
     * 具体调用的方法
     * @param req
     * @param resp
     * @param handler  和url匹配的handler
     * @throws Exception
     */
    public void handle(HttpServletRequest req, HttpServletResponse resp, Handler handler) throws Exception {
        //获取要调用方法的全部参数类型
        Class<?>[] parameterTypes = handler.getMethod().getParameterTypes();
        //创建一个反射调用需要的参数值得数组,数组长度和参数长度一样
        Object[] paramValues = new Object[parameterTypes.length];
        /**
         * 如果参数类型-->index  map里面有HttpServletRequest
         * 就在这个index下的数组赋值req
         */
        if (paramType.containsKey(HttpServletRequest.class.getName())) {
            paramValues[paramType.get(HttpServletRequest.class.getName())] = req;
        }
        if (paramType.containsKey(HttpServletResponse.class.getName())) {
            paramValues[paramType.get(HttpServletResponse.class.getName())] = resp;
        }

        /**
         * 循环遍历RequestParam  value==>index
         * 如果拿到的value在请求参数里面有
         * 那么就从req中取出来赋值给数组
         * 
         */
        for (Map.Entry<String, Integer> entry : paramType.entrySet()) {
            String paramName = entry.getKey();
            Integer index = entry.getValue();
            //拿到请求name对应的value
            String[] values = req.getParameterValues(paramName);
            if (values != null && values.length != 0){
                String value = Arrays.toString(values).replaceAll("\\[|\\]", "").replaceAll(",\\s", ",");
                //赋值给参数数组,并且把取出来的string类型转成我们参数的类型
                paramValues[index] = castValueType(value,parameterTypes[index]);
            }
        }
        //最后反射调用Controller的method方法
        handler.getMethod().invoke(handler.getController(),paramValues);
    }

    private Object castValueType(String value, Class<?> clazz) {
        if(clazz == String.class){
            return value;
        }else if(clazz == Integer.class){
            return Integer.valueOf(value);
        }else if(clazz == int.class){
            return Integer.valueOf(value).intValue();
        }else{
            return null;
        }
    }

    public Map<String, Integer> getParamType() {
        return paramType;
    }

    public void setParamType(Map<String, Integer> paramType) {
        this.paramType = paramType;
    }
}

接下来开始写我们的DisPatchServlet

package com.fanday.mvc.servlet;

import com.fanday.ioc.annotation.Controller;
import com.fanday.ioc.annotation.RequestMapping;
import com.fanday.ioc.annotation.RequestParam;
import com.fanday.ioc.support.AnnotationApplicationContext;
import com.fanday.mvc.Handler;
import com.fanday.mvc.HandlerAdapter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;

public class DispatcherServlet extends HttpServlet {

    public static final String CONTEXT_CONFIG_LOCATION = "contextConfigLocation";
    private List<Handler> handlerMapping = new ArrayList<Handler>();
    private Map<Handler, HandlerAdapter> adapterMapping = new ConcurrentHashMap<Handler, HandlerAdapter>();

    @Override
    public void init() throws ServletException {
        //取出来web.xml中配置的param参数
        String location = getInitParameter(CONTEXT_CONFIG_LOCATION);
        //创建ApplicationContext上下文,启动bean的解析  创建  注入等过程
        AnnotationApplicationContext context = new AnnotationApplicationContext(location);

        //请求解析
        initMultipartResolver(context);
        //多语言、国际化
        initLocaleResolver(context);
        //主题View层的
        initThemeResolver(context);

        //解析url和Method的关联关系
        initHandlerMappings(context);
        //适配器(匹配的过程)
        initHandlerAdapters(context);

        //异常解析
        initHandlerExceptionResolvers(context);
        //视图转发(根据视图名字匹配到一个具体模板)
        initRequestToViewNameTranslator(context);

        //解析模板中的内容(拿到服务器传过来的数据,生成HTML代码)
        initViewResolvers(context);

        initFlashMapManager(context);

        System.out.println("GPSpring MVC is init.");
    }

    private void initFlashMapManager(AnnotationApplicationContext context) {
    }

    private void initViewResolvers(AnnotationApplicationContext context) {
    }

    private void initRequestToViewNameTranslator(AnnotationApplicationContext context) {
    }

    private void initHandlerExceptionResolvers(AnnotationApplicationContext context) {
    }

    private void initHandlerAdapters(AnnotationApplicationContext context) {
        if (handlerMapping.isEmpty()) return;
        //遍历所有的handlerMapping
        for (Handler handler : handlerMapping) {
            Method method = handler.getMethod();
            //创建一个保存RequestParam 注解的value(即参数名)==>index(参数位置索引)
            Map<String, Integer> paramType = new HashMap<String, Integer>();
            //获取所有的参数类型数组
            Class<?>[] parameterTypes = method.getParameterTypes();
            for (int i = 0; i < parameterTypes.length; i++) {
                Class<?> type = parameterTypes[i];
                //如果有HttpServletRequest类型就往map中保存 类型名==>index
                if (type == HttpServletRequest.class || type == HttpServletResponse.class) {
                    paramType.put(type.getName(),i);
                }
            }

            //获取所有的参数注解,之所以返回二维数组,是因为每个参数可能有
            //多个注解修饰
            Annotation[][] pas = method.getParameterAnnotations();
            for (int i = 0; i < pas.length; i++) {
                //获取第i个参数的修饰注解数组
                Annotation[] pa = pas[i];
                //遍历每个参数的修饰注解
                for (Annotation a:pa){
                    if(a instanceof RequestParam){
                        String paramName = ((RequestParam) a).value();
                        if(!"".equals(paramName)){
                            //如果注解属于@RequestParam
                            //把注解参数 name==>index保存map
                            paramType.put(paramName,i);
                        }
                    }
                }
            }
            adapterMapping.put(handler,new HandlerAdapter(paramType));
        }

    }

    /**
     * 初始化handlerMappings
     * @param context
     */
    private void initHandlerMappings(AnnotationApplicationContext context) {
        //获取context中所有的bean
        Map<String, Object> beans = context.getBeans();
        if (beans.isEmpty()) return;
        for (Map.Entry<String, Object> entry : beans.entrySet()) {
            //只对controller修饰的类做解析
            if (!entry.getValue().getClass().isAnnotationPresent(Controller.class)) return;
            String url = "";
            Class<?> clazz = entry.getValue().getClass();
            if (clazz.isAnnotationPresent(RequestMapping.class)) {
                //先取类上面的url
                url = clazz.getAnnotation(RequestMapping.class).value();
            }

            Method[] methods = clazz.getMethods();
            //再取对应方法上的url
            for (Method m : methods) {
                if (!m.isAnnotationPresent(RequestMapping.class)) continue;
                String subUrl = m.getAnnotation(RequestMapping.class).value();
                String regex = (url + subUrl).replaceAll("/+", "/");
                Pattern pattern = Pattern.compile(regex);
                //添加到handlerMapping中去
                handlerMapping.add(new Handler(entry.getValue(), m, pattern));
            }

        }
    }

    private void initThemeResolver(AnnotationApplicationContext context) {
    }

    private void initLocaleResolver(AnnotationApplicationContext context) {
    }

    private void initMultipartResolver(AnnotationApplicationContext context) {
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req, resp);

    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        try {
            doDispatch(req,resp);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        //取出匹配的handler
        Handler handler = getHandler(req);

        //根据handler取出HandlerAdapter
        HandlerAdapter ha = getHandlerAdapter(handler);

        //调用handle方法处理请求,暂时未做ModalAndView处理
        ha.handle(req,resp,handler);
    }

    private HandlerAdapter getHandlerAdapter(Handler handler) {
        if(adapterMapping.isEmpty())return null;
        return adapterMapping.get(handler);
    }

    private Handler getHandler(HttpServletRequest req) {
        if(handlerMapping.isEmpty())return null;
        String contextPath = req.getContextPath();
        String url = req.getRequestURI();
        //获取请求的url  除去contextPath剩余的
        url = url.replace(contextPath,"").replaceAll("/+","/");
        for (Handler handler:handlerMapping){
            if(handler.getPattern().matcher(url).matches()){
                //匹配到就把handler返回
                return handler;
            }
        }
        return null;
    }
}

测试我们的mvc框架

这里写图片描述

至此mvc框架也完成了,剩余的就是模板引擎的处理了,说一下模板的大致思想:

  1. 调用ha.handle(req,resp,handler);方法之后会返回ModalAndView
  2. 判断handler的method方法返回值类型,如果是ModalAndView,就把method.invoke()返回的值强转成ModalAndView
  3. 取出来view就是对应模板的name,把模板读出来,正则找到我们定义的例如:@{name}这种,用modal中值进行替换,resp输出
  4. 如果返回的是string,就判断method上面的注解有没有@ResponseBody,有的话resp输出json数据

代码下载传送门

猜你喜欢

转载自blog.csdn.net/u011702633/article/details/81101969
今日推荐