标签搜索

目 录CONTENT

文章目录

手写框架之SpringMVC

陈铭
2021-07-11 / 0 评论 / 0 点赞 / 212 阅读 / 3,772 字 / 正在检测是否收录...

执行流程

示意图

就像JavaWeb的通常开发会在web.xml配置DispatcherServlet一样,手写框架也是在内嵌的Tomcat里面绑定我自己手写的DispatcherServlet。此时这个Tomcat容器里面的仅有我手写的DispatcherServlet。当请求来的时候,DispatcherServlet父类FrameworkServlet重写了doGet和doPost方法,这两个方法会调用抽象方法doService,DispatcherServlet重写了这个方法。因此和SpringMVC一样,所有的请求最终会进入DispatcherServlet的doService方法。

doService方法会调用doDispatch方法,doDispatch就是找寻对应controller方法并执行方法的核心部分。首先会根据请求路径获取HandlerExecutionChain,里面封装了处理请求的handler;接着获取对应请求类型的处理器HandlerAdapter去处理HandlerExecutionChain封装的HandlerMapping,并获得controller执行结果。根据有无@ResponseBody决定是否要继续生成视图。在有视图的情况下,会找到对应的视图解析器(本项目中就是返回了一个html静态资源,所以用的View是InternalResourceView)。因此后续会得到一个物理视图(也就是html页面),最终并返回响应。
image

关键类

Tomcat

因为是手写SpringMVC,模拟DispatcherServlet的运行流程,那就不能直接用本地的Tomcat跑了,所以这里用了内嵌的Tomcat。主要就是标识了静态资源的位置和加载了自定义的Servlet。也就是我们后面手写的DispatcherServlet

public class Tomcat {
    public void start(String port, String hostName){
        org.apache.catalina.startup.Tomcat tomcat = new org.apache.catalina.startup.Tomcat();

        Server server = tomcat.getServer();
        Service service = server.findService("Tomcat");

        Connector connector = new Connector();
        connector.setPort(Integer.parseInt(port));

        Engine engine = new StandardEngine();
        engine.setDefaultHost(hostName);


        Host host = new StandardHost();
        host.setName(hostName);


        String contextPath="";
        String htmlPath = this.getClass().getResource("/").getFile();
        htmlPath = new File(htmlPath+"static").getAbsolutePath();
        Context context = tomcat.addWebapp(contextPath, htmlPath);
        context.addLifecycleListener(new org.apache.catalina.startup.Tomcat.FixContextListener());

        //维护起层级关系
        host.addChild(context);
        engine.addChild(host);
        service.setContainer(engine);
        service.addConnector(connector);

        tomcat.addServlet(contextPath,"dispatcher",new DispatcherServlet());
        context.addServletMappingDecoded("/test","dispatcher");

        try {
            tomcat.start();
            tomcat.getServer().await();
        } catch (Exception e) {
            //因为没有设置jsp的servlet,这里会报java.lang.ClassNotFoundException: org.apache.jasper.servlet.JspServlet
            //我直接忽略了。。
        }
    }
}

FrameworkServlet

因为符合Servlet的规范,所以顶层的自定义Servlet应该继承HttpServlet,该类也是SpringMVC有的类,主要作用是重写doGet和doPost方法,将所有的请求都传给抽象方法doService,而子类DispatcherServlet正好实现该方法,这样就把所有请求都转到了DispatcherServlet里面去了。

public abstract class FrameworkServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.processRequest(req, resp);
    }

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

    private void processRequest(HttpServletRequest req, HttpServletResponse resp) {
        doService(req,resp);
    }

    protected abstract void doService(HttpServletRequest req, HttpServletResponse resp);
}

DispatcherServlet

该类继承FrameworkServlet,所有请求都会执行到该类的doService方法,进而执行到doDispatch方法。doDispatch方法也是分发请求的核心方法,也就是说根据请求路径将请求交给其中一个handler处理(controller)。并且处理完还有交由对应的视图解析器去封装响应。具体大家看源码。

public class DispatcherServlet extends FrameworkServlet {

    //请求路径和Controller的Method映射
    private List<HandlerMapping> handlerMappings;
    //执行对应请求方法的执行器
    private List<HandlerAdapter> handlerAdapters;
    //请求中是否带文件
    private boolean multipartRequestParsed=false;
    //因为没有Spring的Context,这里直接把Method的Mapping设置静态属性,好传参
    public static AbstractHandlerMethodMapping abstractHandlerMethodMapping = new AbstractHandlerMethodMapping();
    //从请求路径中获取视图名
    private RequestToViewNameTranslator viewNameTranslator;
    //视图解析器,用来处理返回哪个html等视图
    private List<ViewResolver> viewResolvers;

    @Override
    public void init() throws ServletException {
        super.init();
        initServletBean();
    }


    private void initServletBean() {
        initStrategies();
    }

    public void doService(HttpServletRequest req, HttpServletResponse resp) {
        doDispatch(req, resp);
    }

    /**
     * 分发请求至对应的controller
     *
     * @param request
     * @param response
     */
    private void doDispatch(HttpServletRequest request, HttpServletResponse response) {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;

        ModelAndView mv = null;


        //判断是否携带文件
        multipartRequestParsed = this.checkMultipart(request);
        //获取对应请求的handler,也就是controller
        mappedHandler = this.getHandler(processedRequest);
        if (mappedHandler == null) {
            this.noHandlerFound(processedRequest, response);
            return;
        }

        //获取HandlerAdapter,用来处理请求返回视图
        HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
        String method = request.getMethod();
        mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

        this.processDispatchResult(processedRequest, response, mappedHandler, mv);
    }


    private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, HandlerExecutionChain mappedHandler, ModelAndView mv) {
        this.render(mv, request, response);
    }

    private void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) {
        String viewName = mv.getViewName();
        View view=null;
        if (viewName != null) {
            view = this.resolveViewName(viewName, mv.getModelInternal(), request);
            if (view == null) {
                throw new RuntimeException("Could not resolve view with name '" + mv.getViewName() + "' in servlet with name '" + this.getServletName() + "'");
            }
        }
        view.render(mv.getModelInternal(), request, response);
    }

    private View resolveViewName(String viewName, Object modelInternal, HttpServletRequest request) {
        if (this.viewResolvers != null) {
            Iterator var5 = this.viewResolvers.iterator();

            while(var5.hasNext()) {
                ViewResolver viewResolver = (ViewResolver)var5.next();
                View view = viewResolver.resolveViewName(viewName);
                //前后缀补全
                if (view instanceof InternalResourceView){
                    String url = ((InternalResourceView) view).getUrl();
                    if (viewNameTranslator instanceof DefaultRequestToViewNameTranslator){
                        String prefix = ((DefaultRequestToViewNameTranslator) viewNameTranslator).getPrefix();
                        String suffix = ((DefaultRequestToViewNameTranslator) viewNameTranslator).getSuffix();
                        ((InternalResourceView) view).setUrl(prefix+url+suffix);
                    }

                }
                if (view != null) {
                    return view;
                }
            }
        }

        return null;
    }

    private HandlerAdapter getHandlerAdapter(Object handler) {
        if (this.handlerAdapters.get(0).support(handler)) {
            return this.handlerAdapters.get(0);
        }
        throw new RuntimeException("can not find handlerAdapters");
    }

    /**
     * 找不到处理链,也就是说找不到对应的Controller的方法,就404
     *
     * @param processedRequest
     * @param response
     */
    private void noHandlerFound(HttpServletRequest processedRequest, HttpServletResponse response) {
        try {
            response.sendError(404);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取执行该请求的处理链
     *
     * @param processedRequest
     * @return
     */
    private HandlerExecutionChain getHandler(HttpServletRequest processedRequest) {
        HandlerExecutionChain handlerExecutionChain = null;
        try {
            handlerExecutionChain = handlerMappings.get(0).getHandler(processedRequest);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return handlerExecutionChain;
    }

    /**
     * 判断请求是否带文件
     *
     * @param request
     * @return
     */
    private boolean checkMultipart(HttpServletRequest request) {
        String contentType = request.getContentType();
        if (contentType == null) {
            return false;
        }
        return request.getContentType().startsWith("multipart/");
    }

    /**
     * 初始化给映射,解析器等等
     */
    protected void initStrategies() {
        this.initHandlerMappings();
        this.initHandlerAdapters();
        this.initRequestToViewNameTranslator();
        this.initViewResolvers();
    }

    private void initViewResolvers() {
        viewResolvers=Collections.singletonList(new ContentNegotiatingViewResolver());
    }

    private void initRequestToViewNameTranslator() {
        viewNameTranslator=new DefaultRequestToViewNameTranslator();
    }

    /**
     * 源码中是搜索整个项目支持的controller类型(声明一个controller不仅仅可以用@Controller)
     * 不同的controller声明方式,会导致HandlerMapping的对应实现类不同
     * RequestMappingHandlerAdapter是@Controller的对应形式(简化书写,就放了一个)
     */
    private void initHandlerAdapters() {
        handlerAdapters = Collections.singletonList(new RequestMappingHandlerAdapter());
    }

    /**
     * 源码中是搜索整个项目支持的controller类型(声明一个controller不仅仅可以用@Controller)
     * 不同的controller声明方式,会导致HandlerMapping的对应实现类不同
     * RequestMappingHandlerMapping是@RequestMapping的对应形式(简化书写,就放了一个,并且扫描的任务也写在这里)
     */
    private void initHandlerMappings() {
        handlerMappings = Collections.singletonList(new RequestMappingHandlerMapping());
        AbstractHandlerMethodMapping.MappingRegistry mappingRegistry = abstractHandlerMethodMapping.getMappingRegistry();
        findController(mappingRegistry);
    }

    private void findController(AbstractHandlerMethodMapping.MappingRegistry mappingRegistry) {
        String path = this.getClass().getResource("/").getFile();
        File file = new File(path);
        treeFile(mappingRegistry, path, file);
    }

    private void treeFile(AbstractHandlerMethodMapping.MappingRegistry mappingRegistry, String path, File file) {
        for (File listFile : file.listFiles()) {
            if (listFile.isDirectory()) {
                if (listFile.listFiles().length > 0) {
                    treeFile(mappingRegistry, path, listFile);
                }
            } else {
                String filePath = listFile.toString();
                filePath = filePath.substring(path.length() - 1).replace("\\", ".");
                if (filePath.endsWith(".class")) {
                    filePath = filePath.substring(0, filePath.length() - 6);
                    try {
                        Class<?> aClass = this.getClass().getClassLoader().loadClass(filePath);
                        if (aClass.isAnnotationPresent(Controller.class)) {
                            for (Method method : aClass.getDeclaredMethods()) {
                                if (method.isAnnotationPresent(RequestMapping.class)) {
                                    MethodParameter[] methodParameters = new MethodParameter[method.getParameterTypes().length];
                                    for (int i = 0; i < method.getParameterTypes().length; i++) {
                                        Class<?> parameterType = method.getParameterTypes()[i];
                                        //因为反射是获取不到方法参数的参数名的,只能获取参数类型
                                        //获取参数名需要进行反编译,这里简略了,参数名都设置为""
                                        MethodParameter methodParameter = new MethodParameter(i, parameterType, "");
                                        methodParameters[i] = methodParameter;
                                    }
                                    HandlerMethod handlerMethod = new HandlerMethod(aClass, method, methodParameters);
                                    String value = ((RequestMapping) method.getAnnotation(RequestMapping.class)).value();
                                    mappingRegistry.getMappings().put(value, handlerMethod);
                                }
                            }
                        }
                    } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

}

HandlerExecutionChain、AbstractHandlerMethodMapping和HandlerMethod

HandlerExecutionChain是处理链类,如果一个请求需要被多个handler处理,就像过滤器那样,这个类就会封装多个handler。而里面的成员属性handler本质上是HandlerMethod类,下文会讲,是一个封装Method封装类,这个Method也就是Controller类里面定义的类。

public class HandlerExecutionChain {
    private Object handler;

    public HandlerExecutionChain(Object handler) {
        if (handler instanceof HandlerMethod){
            this.handler = handler;
        }else {
            throw new RuntimeException("handler not instanceof HandlerMethod");
        }
    }

    public Object getHandler() {

        return this.handler;
    }

}

而AbstractHandlerMethodMapping类就是存放HandlerMethod的地方,上述的HandlerExecutionChain就是在这个类里面获取对应请求的handler对象。AbstractHandlerMethodMapping中有个内部类MappingRegistry,这个内部类维护了一个Map,存放的就是HandlerMethod

public class AbstractHandlerMethodMapping {
    //路径和方法的映射存放位置
    private  AbstractHandlerMethodMapping.MappingRegistry mappingRegistry = new AbstractHandlerMethodMapping.MappingRegistry();

    public MappingRegistry getMappingRegistry() {
        return mappingRegistry;
    }

    public HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception{
        String lookupPath = request.getRequestURI();
        HandlerMethod handlerMethod = this.lookupHandlerMethod(lookupPath, request);
        return handlerMethod;
    }

    private HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) {
        List<Match> matches = new ArrayList();
        this.addMatchingMappings(lookupPath, matches, request);
        return matches.get(0).handlerMethod;
    }

    private void addMatchingMappings(String lookupPath, List<Match> matches, HttpServletRequest request) {
        matches.add(new Match(lookupPath,(HandlerMethod)this.mappingRegistry.getMappings().get(lookupPath)));
    }

    private class Match {
        private final String mapping;
        private final HandlerMethod handlerMethod;

        public Match(String mapping, HandlerMethod handlerMethod) {
            this.mapping = mapping;
            this.handlerMethod = handlerMethod;
        }

        public String getMapping() {
            return mapping;
        }

        public HandlerMethod getHandlerMethod() {
            return handlerMethod;
        }

        public String toString() {
            return this.mapping.toString();
        }
    }
    public class MappingRegistry {
        private final Map<String, HandlerMethod> mappingLookup = new LinkedHashMap();

        MappingRegistry() {
        }

        public Map<String, HandlerMethod> getMappings() {
            return this.mappingLookup;
        }
    }
}

最后咱们看看HandlerMethod,这个类成员属性比较简单,beanType是Method所在的controller类的类对象;method是执行请求的对应Method对象;parameters是MethodParameter数组,这个MethodParameter就不做赘述了,用来封装Method对象的参数类型和参数名,但由于参数名只能用反编译获取(SpringMVC也是这么做的),我也不会就没写了

public class HandlerMethod {
    private Class<?> beanType;
    private Method method;
    private MethodParameter[] parameters;

    public HandlerMethod(Class<?> beanType, Method method, MethodParameter[] parameters) {
        this.beanType = beanType;
        this.method = method;
        this.parameters = parameters;
    }

    public Class<?> getBeanType() {
        return beanType;
    }

    public void setBeanType(Class<?> beanType) {
        this.beanType = beanType;
    }

    public Method getMethod() {
        return method;
    }

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

    public MethodParameter[] getParameters() {
        return parameters;
    }

    public void setParameters(MethodParameter[] parameters) {
        this.parameters = parameters;
    }

    public Object invoke(Object[] args) {
        Object result = null;
        try {
            Object bean = beanType.newInstance();
            if (method.getParameterTypes().length>0){
                //不支持传参哦,我不会在这里用反编译获取参数名
                throw new RuntimeException("不支持传参哦,我不会在这里用反编译获取参数名");
            }else {
                result = method.invoke(bean);
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
        return result;
    }
}

HandlerMapping及其实现类

HandlerMapping是个接口,和SpringMVC定义的一样,它的实现类用来获取映射请求路径的handler的,实现类的关键方法就是用来获取HandlerExecutionChain

public class RequestMappingHandlerMapping implements HandlerMapping {

    public HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        Object handler = this.getHandlerInternal(request);
        HandlerExecutionChain executionChain = this.getHandlerExecutionChain(handler, request);
        return executionChain;
        }

    private HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
        return new HandlerExecutionChain(handler);
    }



    private Object getHandlerInternal(HttpServletRequest request) {
        AbstractHandlerMethodMapping abstractHandlerMethodMapping = DispatcherServlet.abstractHandlerMethodMapping;
        HandlerMethod handlerMethod=null;
        try {
            handlerMethod = abstractHandlerMethodMapping.getHandlerInternal(request);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return handlerMethod;
    }
}

HandlerAdapter及其实现类

HandlerAdapter也是个接口,和SpringMVC定义的一样,它的实现类是用来执行获取到的handler,本质上是取出Method对象,反射执行。最终会获得ModlerAndView。

/**
 * @Controller的对应映射的执行器
 */
public class RequestMappingHandlerAdapter implements HandlerAdapter {
    @Override
    public boolean support(Object handler) {
        return handler instanceof HandlerMethod && this.supportsInternal((HandlerMethod) handler);
    }

    @Override
    public ModelAndView handle(HttpServletRequest processedRequest, HttpServletResponse response, Object handler) {
        return this.handleInternal(processedRequest, response, (HandlerMethod) handler);
    }

    private ModelAndView handleInternal(HttpServletRequest processedRequest, HttpServletResponse response, HandlerMethod handler) {

        ModelAndView mav = this.invokeHandlerMethod(processedRequest, response, handler);
        return mav;
    }

    private ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) {

        ServletInvocableHandlerMethod invocableMethod = this.createInvocableHandlerMethod(handlerMethod);
        ModelAndViewContainer mavContainer = new ModelAndViewContainer();
        invocableMethod.invokeAndHandle(request,response, mavContainer, new Object[0]);
        ModelAndView modelAndView = this.getModelAndView(mavContainer, request,response);
        return modelAndView;

    }

    private ModelAndView getModelAndView(ModelAndViewContainer mavContainer, HttpServletRequest request, HttpServletResponse response) {

        ModelMap model = mavContainer.getModel();
        ModelAndView mav=null;
        if (mavContainer.getViewName()!=null){
            mav = new ModelAndView(mavContainer.getViewName(), model);
        }
        return mav;
    }

    private ServletInvocableHandlerMethod createInvocableHandlerMethod(HandlerMethod handlerMethod) {
        return new ServletInvocableHandlerMethod(handlerMethod);
    }

    /**
     * RequestMappingHandlerAdapter就是处理@Controller类型的请求的
     *
     * @param handler
     * @return
     */
    private boolean supportsInternal(HandlerMethod handler) {
        return true;
    }
}

除此之外还有两个和RequestMappingHandlerAdapter相关的关键类:ServletInvocableHandlerMethod、HandlerMethodReturnValueHandlerComposite。RequestMappingHandlerAdapter在执行方法是是让ServletInvocableHandlerMethod通过反射去执行的,执行回来的结果会在传给HandlerMethodReturnValueHandlerComposite去判断是否要设置视图的相关信息(因为存在@ResponseBody就不需要接下来进入视图解析的流程了)

public class ServletInvocableHandlerMethod {
    private HandlerMethod handlerMethod;

    private HandlerMethodReturnValueHandlerComposite returnValueHandlers = new HandlerMethodReturnValueHandlerComposite();

    public ServletInvocableHandlerMethod(HandlerMethod handlerMethod) {
        this.handlerMethod = handlerMethod;
    }

    public void invokeAndHandle(HttpServletRequest request, HttpServletResponse response, ModelAndViewContainer mavContainer, Object... objects) {
        Object returnValue = this.invokeForRequest(request,response, mavContainer, objects);
        if (returnValue == null) {
            throw new RuntimeException("method erro");
        }
        //将返回的结果封装在视图容器中
        this.returnValueHandlers.handleReturnValue(returnValue,  mavContainer, request,response);
    }


    private Object invokeForRequest(HttpServletRequest request, HttpServletResponse response, ModelAndViewContainer mavContainer, Object... providedArgs) {
        Object[] args = this.getMethodArgumentValues(request, mavContainer, providedArgs);
        return this.doInvoke(request,args);
    }

    private Object doInvoke(HttpServletRequest request, Object[] args) {
        HandlerMethod handlerMethod = DispatcherServlet.abstractHandlerMethodMapping.getMappingRegistry().getMappings().get(request.getRequestURI());
        return handlerMethod.invoke(args);
    }

    /**
     * 传参,因为绑定参数要获取方法的参数名,需要用到反编译技术,所以这里先不写
     * @param request
     * @param mavContainer
     * @param providedArgs
     * @return
     */
    private Object[] getMethodArgumentValues(HttpServletRequest request, ModelAndViewContainer mavContainer, Object[] providedArgs) {
        return providedArgs;
    }
}


public class HandlerMethodReturnValueHandlerComposite {

    public void handleReturnValue(Object returnValue, ModelAndViewContainer mavContainer, HttpServletRequest request, HttpServletResponse response) {
        if (returnValue instanceof CharSequence) {
            String viewName = returnValue.toString();
            HandlerMethod handlerMethod = DispatcherServlet.abstractHandlerMethodMapping.getMappingRegistry().getMappings().get(request.getRequestURI());
            //这里就没写那么深了,@RequestMapping的请求直接在这里传回去了
            if (handlerMethod.getMethod().isAnnotationPresent(ResponseBody.class)) {
                response.addHeader("Content-Type","text/html;charset=UTF-8");
                ServletOutputStream outputStream=null;
                try {
                    outputStream = response.getOutputStream();
                    outputStream.write(viewName.getBytes());
                } catch (IOException e) {
                    e.printStackTrace();
                }finally {
                    if (outputStream!=null){
                        try {
                            outputStream.flush();
                            outputStream.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }else {
                mavContainer.setViewName(viewName);
            }
        }
    }
}

视图类

ModelAndView是个逻辑视图,里面我就写了两个属性,view是视图名,model是模型。ModelAndViewContainer是一个视图容器,handler执行返回视图名后会设置在容器中,而ModelAndView就是从容器中获取的。View是一个视图的接口,在SpringMVC中有很多实现类,其中物理视图的实现类是InternalResourceView,主要封装了静态文件的url以及转发请求至具体资源的方法。

public class ModelAndView {
    private Object view;
    private ModelMap model;

    public ModelAndView(Object view, ModelMap model) {
        this.view = view;
        this.model = model;
    }

    public String getViewName() {
        return view.toString();
    }

    public Object getModelInternal() {
        return model;
    }
}


public class ModelAndViewContainer {

    private Object view;
    //model封装类
    private ModelMap defaultModel;
    //重定向的model封装类
    private ModelMap redirectModel;


    public ModelAndViewContainer() {
    }

    public void setViewName(String viewName) {
        this.view = viewName;
    }

    public ModelMap getModel() {
        return this.defaultModel;
    }

    public Object getViewName() {
        return this.view;
    }
}


public class InternalResourceView implements View {

    private String url;
    private String contentType = "text/html;charset=ISO-8859-1";

    public InternalResourceView(String viewName) {
        url=viewName;
    }

    @Override
    public void render(Object modelInternal, HttpServletRequest request, HttpServletResponse response) {
        this.renderMergedOutputModel(request,response);
    }

    private void renderMergedOutputModel(HttpServletRequest request, HttpServletResponse response) {

        String dispatcherPath = this.prepareForRendering(request, response);
        RequestDispatcher rd = this.getRequestDispatcher(request, dispatcherPath);
        try {
            rd.forward(request, response);
        } catch (ServletException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private RequestDispatcher getRequestDispatcher(HttpServletRequest request, String dispatcherPath) {
        return request.getRequestDispatcher(dispatcherPath);
    }

    private String prepareForRendering(HttpServletRequest request, HttpServletResponse response) {
        if (url.equals(request.getRequestURI())){
            throw new RuntimeException("erro loop!");
        }
        return url;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }
}

视图解析类

ViewResolver是个接口,其实现类用来解析视图,返回具体的物理视图,也就是InternalResourceView。而RequestToViewNameTranslator是一个视图名转换类接口,实现类封装了视图的前后缀,ViewResolver接受的视图名都是经过RequestToViewNameTranslator处理后的视图名。

public class DefaultRequestToViewNameTranslator implements RequestToViewNameTranslator {
    private String prefix = "";
    private String suffix = "";
    private String separator = "/";

    @Override
    public String getViewName(HttpServletRequest var1) {
        return null;
    }

    public String getPrefix() {
        return prefix;
    }

    public String getSuffix() {
        return suffix;
    }

    public String getSeparator() {
        return separator;
    }
}


public class ContentNegotiatingViewResolver implements ViewResolver {
    @Override
    public View resolveViewName(String viewName) {
        View bestView = this.getBestView(viewName);
        return bestView;
    }

    private View getBestView(String viewName) {
        return new InternalResourceView(viewName);
    }
}

Main方法及其测试结果

Main方法

就一行代码,开启内嵌的Tomcat

public class Main {
    public static void main(String[] args) {
        new Tomcat().start("8080","localhost");
    }
}

测试结果(不加@ResponseBody)

应该返回物理视图,也就是html页面。

@Controller
public class MyController{

    //手写框架目前不支持传参,因为需要用到反编译技术
    //支持@ResponseBody或者返回页面
    @RequestMapping("/test")
    public String test(){
        return "test.html";
    }
}

image

测试结果(加@ResponseBody)

应该返回模型数据,当然也会被浏览器解析出来。

@Controller
public class MyController{

    //手写框架目前不支持传参,因为需要用到反编译技术
    //支持@ResponseBody或者返回页面
    @ResponseBody
    @RequestMapping("/test")
    public String test(){
        return "test.html";
    }
}

image

Gitee源码

源码

0

评论区