Today Web Mvc 设计思想

发布于: 2019年08月14日 09:10:58 | 分类: 轮子工厂 | 浏览: 117

简介

该框架起初是仅仅只有280行。随着学习的深入框架经历了数次重大重构。学习过Spring在使用上和她也很像,但是他们有着本质的区别。边框架根本目的在于学习。

Talk is cheap. Show me the code.

代码如下

package com.yhj.core.web.servlet;

import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.springframework.web.context.ContextLoader;

import com.yhj.ext.utils.Tools;

public final class ViewsDispatcher extends HttpServlet {

    private static final long serialVersionUID = 1L;

    private static final Map<String, RequestMapping> requestMapping = new HashMap<>();
    private static String contextPath = null;
    private static String extension = "action";

    @Override
    public void destroy() {

    }

    public String getExtension() {
        return extension;
    }

    /**
     * viewsConfigLocation 加载配置文件
     */
    @Override
    public void init(ServletConfig config) throws ServletException {
        super.init(config);
        contextPath = config.getServletContext().getContextPath();
        String con = config.getInitParameter("viewsConfigLocation");
        System.out.println("Initializing ViewsDispatcher from [" + this.getServletContext().getRealPath(con) + "]");
        setConfiguration(this.getServletContext().getRealPath(con));
        this.getServletContext().setAttribute("extension", extension);
        this.getServletContext().setAttribute("contextPath", contextPath);
    }

    /**
     * 解析url
     * 
     * @param request
     * @param response
     * @throws ServletException
     * @throws IOException
     */
    @Override
    protected final void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        response.setCharacterEncoding("UTF-8");

        response.setContentType("text/html;charset=UTF-8");
        String requestURI = request.getRequestURI();

        if (requestMapping.containsKey(requestURI)) {

            RequestMapping mapping = requestMapping.get(requestURI);
            // 执行页面转向前的方法
            if (mapping.hasMethod()) {
                // 从Spring获得
                try {
                    Object obj = mapping.getMethod().invoke(ContextLoader.getCurrentWebApplicationContext().getBean(mapping.getClazz()),
                            request, response);
                    if (obj != null) {
                        response.getWriter().print(obj.toString());
                        return;
                    }
                }
                catch (Exception e) {
                    response.sendError(500);
                }
            }
            // 转到相应页面
            if (!response.isCommitted()) {
                switch (mapping.getReturnType())
                {
                    case "dispatcher" :
                        if (Tools.isAjax(request)) {
                            request.getRequestDispatcher(mapping.getAssetsPath().replace(".jsp", "_content.jsp")).forward(request,
                                    response);
                            break;
                        }
                        request.getRequestDispatcher(mapping.getAssetsPath()).forward(request, response);
                        break;
                    case "redirectAction" :
                        response.sendRedirect(contextPath + "/" + mapping.getAssetsPath() + "." + extension);
                        break;
                    case "redirect" :
                        response.sendRedirect(contextPath + "/" + mapping.getAssetsPath());
                        break;
                    case "json" :
                        response.setContentType("application/json;charset=UTF-8");

                        break;
                    default:
                        break;
                }
            }
        }
        else {
            response.sendError(404);
        }
    }

    /**
     * 从xml文件中得到配置信息
     * 
     * @param element
     */
    @SuppressWarnings("unchecked")
    private static void getConfiguration(Element element) {
        RequestMapping mapping = new RequestMapping();
        if (!"constant".equals("")) {
            if (element.attribute("view.extension") != null) {
                extension = element.attribute("view.extension").getValue();
            }
        }
        if ("view".equals(element.getName())) {
            try {
                mapping.setClazz(Class.forName(element.getParent().attribute("class").getValue()));
            }
            catch (ClassNotFoundException e) {
                System.err.println(element.getParent().attribute("class").getValue() + "-类定义错误! 请仔细检查 [" + e.getLocalizedMessage() + "]");
            }
            mapping.setAssetsPath(element.getParent().attribute("baseDir").getValue() + element.attribute("jsp").getValue());
            mapping.setRequestUri(contextPath + "/" + element.attribute("name").getValue() + "." + extension);
            mapping.setReturnType(element.attribute("type").getValue());

            requestMapping.put(contextPath + "/" + element.attribute("name").getValue() + "." + extension, mapping);

            System.out.println("View 映射:" + requestMapping.get(contextPath + "/" + element.attribute("name").getValue() + "." + extension));

        }
        else if ("action".equals(element.getName())) {
            if (element.attribute("jsp") != null) {
                mapping.setAssetsPath(element.getParent().attribute("baseDir").getValue() + element.attribute("jsp").getValue());
            }
            mapping.setReturnType(element.attribute("type").getValue());
            mapping.setRequestUri(contextPath + "/" + element.attribute("name").getValue() + "." + extension);
            try {
                mapping.setClazz(Class.forName(element.getParent().attribute("class").getValue()));
            }
            catch (ClassNotFoundException e) {
                System.err.println(element.getParent().attribute("class").getValue() + "-类定义错误! 请仔细检查 [" + e.getLocalizedMessage() + "]");
            }
            try {
                mapping.setMethod(mapping.getClazz().getMethod(element.attribute("method").getValue(),
                        HttpServletRequest.class, HttpServletResponse.class));
            }
            catch (NoSuchMethodException e) {
                System.err.println(element.getParent().attribute("class").getValue() + "类中未找到此方法(" + e.getLocalizedMessage() + ")");
            }
            catch (SecurityException e) {
                System.err.println(e.getMessage());
            }

            requestMapping.put(contextPath + "/" + element.attribute("name").getValue() + "." + extension, mapping);

            System.out.println("Action 映射:" + requestMapping.get(contextPath + "/" + element.attribute(
                    "name").getValue() + "." + extension));
        }
        // 使用递归
        Iterator<Element> iterator = element.elementIterator();
        while (iterator.hasNext()) {
            getConfiguration(iterator.next());
        }
    }

    /**
     * 读取配置文件
     * 
     * @param configFilePath
     */
    private static void setConfiguration(String configFilePath) {
        SAXReader reader = new SAXReader();
        // 读取文件 转换成Document
        Document document = null;
        try {
            document = reader.read(new File(configFilePath));
        }
        catch (DocumentException e) {
            System.err.println(e.getLocalizedMessage());
        }
        Element element = document.getRootElement();
        getConfiguration(element);
    }

}

final class RequestMapping implements Serializable {

    private static final long serialVersionUID = -8130348090936881368L;
    /** 返回类型 */
    private String returnType = null;
    /** 资源路径 */
    private String assetsPath = "";
    /** 请求路径 */
    private String requestUri = "";
    /** 前置方法 */
    private transient Method method = null;
    /** 方法所在类 */
    private transient Class<?> clazz = null;

    public String getAssetsPath() {
        return assetsPath;
    }

    public Class<?> getClazz() {
        return clazz;
    }

    public Method getMethod() {
        return method;
    }

    public String getRequestUri() {
        return requestUri;
    }

    public String getReturnType() {
        return returnType;
    }

    public boolean hasMethod() {
        return method != null;
    }

    public RequestMapping setAssetsPath(String assetsPath) {
        this.assetsPath = assetsPath;
        return this;
    }

    public void setClazz(Class<?> clazz) {
        this.clazz = clazz;
    }

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

    public RequestMapping setRequestUri(String requestUri) {
        this.requestUri = requestUri;
        return this;
    }

    public RequestMapping setReturnType(String returnType) {
        this.returnType = returnType;
        return this;
    }

    @Override
    public String toString() {
        return " {\n\t\"returnType\":\"" + returnType + "\",\n\t\"assetsPath\":\"" + assetsPath + "\",\n\t\"requestUri\":\"" + requestUri + "\",\n\t\"method\":\"" + method + "\",\n\t\"clazz\":\"" + clazz + "\"\n}";
    }
}

该系列博文讲述整个web框架设计思想。将围绕所有Action请求入口: DispatcherServlet#service()方法,分为多个章节讲述整个框架。

定义及命名

  1. 本框架认为一个类(class)叫做控制器(Controller),类方法被定义为Handler或Action。
  2. 类似@ActionMapping,@Controller,@ControllerAdvice,@RequestParam等被叫做根注解Root Annotation)。所谓根注解即是可以自定义注解,只要自定义注解上含有该根注解即可拥有与根注解相同功能。例如:@Controller含有根注解@Component即可标识一个组件,(该注解标识一个单例Bean)。
@Component
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Controller {

    /**
     * Controller name or a bean name
     */
    String[] value() default {};
}

Action请求入口: DispatcherServlet#service()

@Override
public void service(final ServletRequest req, final ServletResponse res) //
        throws ServletException, IOException //
{
    // Lookup handler mapping
    final HandlerMapping mapping = lookupHandlerMapping((HttpServletRequest) req);

    if (mapping == null) {
        ((HttpServletResponse) res).sendError(404);
        return;
    }

    final RequestContext context = prepareContext(req, res);
    try {

        final Object result;
        // Handler Method
        final HandlerMethod method;// = requestMapping.getHandlerMethod();
        if (mapping.hasInterceptor()) {
            // get intercepter s
            final int[] its = mapping.getInterceptors();
            // invoke intercepter
            final HandlerInterceptorRegistry registry = getHandlerInterceptorRegistry();
            for (final int i : its) {
                if (!registry.get(i).beforeProcess(context, mapping)) {
                    if (log.isDebugEnabled()) {
                        log.debug("Interceptor: [{}] return false", registry.get(i));
                    }
                    return;
                }
            }
            result = invokeHandler(context, method = mapping.getHandlerMethod(), mapping);
            for (final int i : its) {
                registry.get(i).afterProcess(context, mapping, result);
            }
        }
        else {
            result = invokeHandler(context, method = mapping.getHandlerMethod(), mapping);
        }

        method.resolveResult(context, result);
    }
    catch (Throwable e) {
        ResultUtils.resolveException(context, exceptionResolver, mapping, e);
    }
}

系列博文

  1. Today Web Mvc 路由设计
  2. Today Web Mvc 参数处理器设计

未完

版权声明:本文为作者原创文章,转载时请务必声明出处并添加指向此页面的链接。
分享:
发表评论

目前您尚未登录,请 登录 后进行评论

评论信息