摘要:前言在從零開(kāi)始實(shí)現(xiàn)一個(gè)簡(jiǎn)易的框架七實(shí)現(xiàn)中實(shí)現(xiàn)了框架的的功能,不過(guò)最后指出代碼的邏輯不是很好,在這一章節(jié)就將這一部分代碼進(jìn)行優(yōu)化。
前言
在從零開(kāi)始實(shí)現(xiàn)一個(gè)簡(jiǎn)易的Java MVC框架(七)--實(shí)現(xiàn)MVC中實(shí)現(xiàn)了doodle框架的MVC的功能,不過(guò)最后指出代碼的邏輯不是很好,在這一章節(jié)就將這一部分代碼進(jìn)行優(yōu)化。
優(yōu)化的目標(biāo)是1.去除DispatcherServlet請(qǐng)求分發(fā)器中的http邏輯代碼;2.將ControllerHandler和ResultRender中代碼按功能細(xì)分出來(lái),使其各司其職。
修改DispatcherServlet 創(chuàng)建接口先在com.zbw.mvc包下創(chuàng)建兩個(gè)包handler和render,分別用于放ControllerHandler和ResultRender拆分出來(lái)的功能類(lèi)。
再在這兩個(gè)包下創(chuàng)建兩個(gè)接口,以便后面的功能類(lèi)都按這個(gè)接口規(guī)范。
package com.zbw.mvc.handler; import com.zbw.mvc.RequestHandlerChain; /** * 請(qǐng)求執(zhí)行器 Handler */ public interface Handler { /** * 請(qǐng)求的執(zhí)行器 */ boolean handle(final RequestHandlerChain handlerChain) throws Exception; }
package com.zbw.mvc.render; import com.zbw.mvc.RequestHandlerChain; /** * 渲染請(qǐng)求結(jié)果 interface */ public interface Render { /** * 執(zhí)行渲染 */ void render(RequestHandlerChain handlerChain) throws Exception; }實(shí)現(xiàn)RequestHandlerChain
上面兩個(gè)接口都有個(gè)參數(shù)RequestHandlerChain,這個(gè)類(lèi)是整個(gè)請(qǐng)求的執(zhí)行鏈,用于存儲(chǔ)整個(gè)請(qǐng)求需要保存的一些屬性和串聯(lián)整個(gè)請(qǐng)求。
在com.zbw.mvc下創(chuàng)建這個(gè)類(lèi)
package com.zbw.mvc; import ... /** * http請(qǐng)求處理鏈 */ @Data @Slf4j public class RequestHandlerChain { /** * Handler迭代器 */ private IteratorhandlerIt; /** * 請(qǐng)求request */ private HttpServletRequest request; /** * 請(qǐng)求response */ private HttpServletResponse response; /** * 請(qǐng)求http方法 */ private String requestMethod; /** * 請(qǐng)求http路徑 */ private String requestPath; /** * 請(qǐng)求狀態(tài)碼 */ private int responseStatus; /** * 請(qǐng)求結(jié)果處理器 */ private Render render; public RequestHandlerChain(Iterator handlerIt, HttpServletRequest request, HttpServletResponse response) { this.handlerIt = handlerIt; this.request = request; this.response = response; this.requestMethod = request.getMethod(); this.requestPath = request.getPathInfo(); this.responseStatus = HttpServletResponse.SC_OK; } /** * 執(zhí)行請(qǐng)求鏈 */ public void doHandlerChain() { try { while (handlerIt.hasNext()) { if (!handlerIt.next().handle(this)) { break; } } } catch (Exception e) { log.error("doHandlerChain error", e); render = new InternalErrorRender(); } } /** * 執(zhí)行處理器 */ public void doRender() { if (null == render) { render = new DefaultRender(); } try { render.render(this); } catch (Exception e) { log.error("doRender", e); throw new RuntimeException(e); } } }
在這個(gè)類(lèi)中除了存儲(chǔ)http請(qǐng)求信息以外,還有Handler迭代器handlerIt和請(qǐng)求結(jié)果處理器Render。
doHandlerChain()方法就會(huì)迭代執(zhí)行handlerIt中的Handler的handle()方法,并且會(huì)根據(jù)每個(gè)Handler返回的值來(lái)判斷是否繼續(xù)往下執(zhí)行下一個(gè)Handler。
doRender()方法用于調(diào)用Render中的render()方法。
更改DispatcherServlet接下來(lái)就可以修改DispatcherServlet請(qǐng)求轉(zhuǎn)發(fā)器了。
package com.zbw.mvc; import ... /** * DispatcherServlet 所有http請(qǐng)求都由此Servlet轉(zhuǎn)發(fā) */ @Slf4j public class DispatcherServlet extends HttpServlet { /** * 請(qǐng)求執(zhí)行鏈 */ private final ListHANDLER = new ArrayList<>(); /** * 執(zhí)行請(qǐng)求 */ @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { RequestHandlerChain handlerChain = new RequestHandlerChain(HANDLER.iterator(), req, resp); handlerChain.doHandlerChain(); handlerChain.doRender(); } }
可以看到現(xiàn)在DispatcherServlet已經(jīng)很簡(jiǎn)潔了,把請(qǐng)求的邏輯代碼交給RequestHandlerChain處理,自己沒(méi)有多余的http邏輯代碼。
實(shí)現(xiàn)幾種Handler上面只創(chuàng)建了Handler的接口沒(méi)有實(shí)現(xiàn)類(lèi),現(xiàn)在就實(shí)現(xiàn)幾個(gè)Handler的實(shí)現(xiàn)類(lèi)。這些實(shí)現(xiàn)類(lèi)只實(shí)現(xiàn)了簡(jiǎn)單的一些http請(qǐng)求的功能,大家可以自己根據(jù)情況開(kāi)發(fā)更多的實(shí)現(xiàn)類(lèi)。
PreRequestHandler首先是PreRequestHandler,用于預(yù)處理http的一些信息,比如設(shè)置http編碼,處理請(qǐng)求url,打印一些信息等。
package com.zbw.mvc.handler; import ... /** * 請(qǐng)求預(yù)處理 */ @Slf4j public class PreRequestHandler implements Handler { @Override public boolean handle(final RequestHandlerChain handlerChain) throws Exception { // 設(shè)置請(qǐng)求編碼方式 handlerChain.getRequest().setCharacterEncoding("UTF-8"); String requestPath = handlerChain.getRequestPath(); if (requestPath.length() > 1 && requestPath.endsWith("/")) { handlerChain.setRequestPath(requestPath.substring(0, requestPath.length() - 1)); } log.info("[Doodle] {} {}", handlerChain.getRequestMethod(), handlerChain.getRequestPath()); return true; } }SimpleUrlHandler
接下來(lái)是SimpleUrlHandler,用于處理靜態(tài)資源,當(dāng)碰到資源是靜態(tài)資源時(shí)就直接轉(zhuǎn)發(fā)請(qǐng)求到Tomcat默認(rèn)的servlet去。
package com.zbw.mvc.handler; import ... /** * 普通url請(qǐng)求執(zhí)行 * 主要處理靜態(tài)資源 */ @Slf4j public class SimpleUrlHandler implements Handler { /** * tomcat默認(rèn)RequestDispatcher的名稱(chēng) * TODO: 其他服務(wù)器默認(rèn)的RequestDispatcher.如WebLogic為FileServlet */ private static final String TOMCAT_DEFAULT_SERVLET = "default"; /** * 默認(rèn)的RequestDispatcher,處理靜態(tài)資源 */ private RequestDispatcher defaultServlet; public SimpleUrlHandler(ServletContext servletContext) { defaultServlet = servletContext.getNamedDispatcher(TOMCAT_DEFAULT_SERVLET); if (null == defaultServlet) { throw new RuntimeException("沒(méi)有默認(rèn)的Servlet"); } log.info("The default servlet for serving static resource is [{}]", TOMCAT_DEFAULT_SERVLET); } @Override public boolean handle(final RequestHandlerChain handlerChain) throws Exception { if (isStaticResource(handlerChain.getRequestPath())) { defaultServlet.forward(handlerChain.getRequest(), handlerChain.getResponse()); return false; } return true; } /** * 是否為靜態(tài)資源 */ private boolean isStaticResource(String url) { return url.startsWith(Doodle.getConfiguration().getAssetPath()); } }JspHandler
然后是處理jsp頁(yè)面的實(shí)現(xiàn)類(lèi)JspHandler,當(dāng)碰到資源是jsp頁(yè)面時(shí)就直接轉(zhuǎn)發(fā)請(qǐng)求到Tomcat的jsp的servlet去。
package com.zbw.mvc.handler; import ... /** * jsp請(qǐng)求處理 * 主要負(fù)責(zé)jsp資源請(qǐng)求 */ public class JspHandler implements Handler { /** * jsp請(qǐng)求的RequestDispatcher的名稱(chēng) */ private static final String JSP_SERVLET = "jsp"; /** * jsp的RequestDispatcher,處理jsp資源 */ private RequestDispatcher jspServlet; public JspHandler(ServletContext servletContext) { jspServlet = servletContext.getNamedDispatcher(JSP_SERVLET); if (null == jspServlet) { throw new RuntimeException("沒(méi)有jsp Servlet"); } } @Override public boolean handle(final RequestHandlerChain handlerChain) throws Exception { if (isPageView(handlerChain.getRequestPath())) { jspServlet.forward(handlerChain.getRequest(), handlerChain.getResponse()); return false; } return true; } /** * 是否為jsp資源 */ private boolean isPageView(String url) { return url.startsWith(Doodle.getConfiguration().getViewPath()); } }ControllerHandler
最后就是ControllerHandler,這個(gè)和從零開(kāi)始實(shí)現(xiàn)一個(gè)簡(jiǎn)易的Java MVC框架(七)--實(shí)現(xiàn)MVC中的ControllerHandler功能一樣,用于處理請(qǐng)求中數(shù)據(jù)和controller對(duì)應(yīng)的關(guān)系。
package com.zbw.mvc.handler; import ... /** * Controller請(qǐng)求處理 */ @Slf4j public class ControllerHandler implements Handler { /** * 請(qǐng)求信息和controller信息關(guān)系map */ private Map初始化HANDLER列表和去除TomcatServer的多余代碼pathControllerMap = new ConcurrentHashMap<>(); /** * bean容器 */ private BeanContainer beanContainer; public ControllerHandler() { beanContainer = BeanContainer.getInstance(); Set > mappingSet = beanContainer.getClassesByAnnotation(RequestMapping.class); this.initPathControllerMap(mappingSet); } @Override public boolean handle(final RequestHandlerChain handlerChain) throws Exception { String method = handlerChain.getRequestMethod(); String path = handlerChain.getRequestPath(); ControllerInfo controllerInfo = pathControllerMap.get(new PathInfo(method, path)); if (null == controllerInfo) { handlerChain.setRender(new NotFoundRender()); return false; } Object result = invokeController(controllerInfo, handlerChain.getRequest()); setRender(result, controllerInfo, handlerChain); return true; } /** * 執(zhí)行controller方法 */ private Object invokeController(ControllerInfo controllerInfo, HttpServletRequest request) { Map requestParams = getRequestParams(request); List
剛才實(shí)現(xiàn)的幾個(gè)HANDLER還需要初始化,就在DispatcherServlet的init()方法中初始化。注意初始化的順序會(huì)決定其在RequestHandlerChain執(zhí)行鏈中執(zhí)行的先后。
... @Slf4j public class DispatcherServlet extends HttpServlet { ... /** * 初始化Servlet */ @Override public void init() throws ServletException { HANDLER.add(new PreRequestHandler()); HANDLER.add(new SimpleUrlHandler(getServletContext())); HANDLER.add(new JspHandler(getServletContext())); HANDLER.add(new ControllerHandler()); } ... }
然后去除TomcatServer中JspServlet和DefaultServlet兩個(gè)servlet的初始化,因?yàn)橐呀?jīng)在 JspHandler和SimpleUrlHandler中初始化了這兩個(gè)servlet。
... @Slf4j public class TomcatServer implements Server { ... public TomcatServer(Configuration configuration) { try { this.tomcat = new Tomcat(); tomcat.setBaseDir(configuration.getDocBase()); tomcat.setPort(configuration.getServerPort()); File root = getRootFolder(); File webContentFolder = new File(root.getAbsolutePath(), configuration.getResourcePath()); if (!webContentFolder.exists()) { webContentFolder = Files.createTempDirectory("default-doc-base").toFile(); } log.info("Tomcat:configuring app with basedir: [{}]", webContentFolder.getAbsolutePath()); StandardContext ctx = (StandardContext) tomcat.addWebapp(configuration.getContextPath(), webContentFolder.getAbsolutePath()); ctx.setParentClassLoader(this.getClass().getClassLoader()); WebResourceRoot resources = new StandardRoot(ctx); ctx.setResources(resources); // 去除了JspHandler和SimpleUrlHandler這兩個(gè)servlet的注冊(cè) tomcat.addServlet(configuration.getContextPath(), "dispatcherServlet", new DispatcherServlet()).setLoadOnStartup(0); ctx.addServletMappingDecoded("/*", "dispatcherServlet"); } catch (Exception e) { log.error("初始化Tomcat失敗", e); throw new RuntimeException(e); } } }實(shí)現(xiàn)幾種Render
上面創(chuàng)建的Render接口也需要一些實(shí)現(xiàn)類(lèi)。同樣的,這些Render也只是實(shí)現(xiàn)基本的功能 ,大家可以自己根據(jù)情況開(kāi)發(fā)更多。
DefaultRender這個(gè)是默認(rèn)的Render,設(shè)置HttpServletResponse中的status為RequestHandlerChain中StatusCode。
package com.zbw.mvc.render; import ... /** * 默認(rèn)渲染 200 */ public class DefaultRender implements Render { @Override public void render(RequestHandlerChain handlerChain) throws Exception { int status = handlerChain.getResponseStatus(); handlerChain.getResponse().setStatus(status); } }InternalErrorRender
這個(gè)Render返回StatusCode為500
package com.zbw.mvc.render; import ... /** * 渲染500 */ public class InternalErrorRender implements Render { @Override public void render(RequestHandlerChain handlerChain) throws Exception { handlerChain.getResponse().sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); } }NotFoundRender
這個(gè)Render返回StatusCode為404
package com.zbw.mvc.render; import ... /** * 渲染404 */ public class NotFoundRender implements Render { @Override public void render(RequestHandlerChain handlerChain) throws Exception { handlerChain.getResponse().sendError(HttpServletResponse.SC_NOT_FOUND); } }JsonRender
這個(gè)Render返回json數(shù)據(jù),當(dāng)Handler請(qǐng)求發(fā)現(xiàn)返回?cái)?shù)據(jù)為json格式時(shí),就用這個(gè)Render
package com.zbw.mvc.render; import ... /** * 渲染json */ @Slf4j public class JsonRender implements Render { private Object jsonData; public JsonRender(Object jsonData) { this.jsonData = jsonData; } @Override public void render(RequestHandlerChain handlerChain) throws Exception { // 設(shè)置響應(yīng)頭 handlerChain.getResponse().setContentType("application/json"); handlerChain.getResponse().setCharacterEncoding("UTF-8"); // 向響應(yīng)中寫(xiě)入數(shù)據(jù) try (PrintWriter writer = handlerChain.getResponse().getWriter()) { writer.write(JSON.toJSONString(jsonData)); writer.flush(); } } }ViewRender
這個(gè)Render跳轉(zhuǎn)到頁(yè)面,將ModelAndView中的信息存到HttpServletRequest中并跳轉(zhuǎn)到對(duì)應(yīng)頁(yè)面
package com.zbw.mvc.render; import ... /** * 渲染頁(yè)面 */ @Slf4j public class ViewRender implements Render { private ModelAndView mv; public ViewRender(Object mv) { if (mv instanceof ModelAndView) { this.mv = (ModelAndView) mv; } else if (mv instanceof String) { this.mv = new ModelAndView().setView((String) mv); } else { throw new RuntimeException("返回類(lèi)型不合法"); } } @Override public void render(RequestHandlerChain handlerChain) throws Exception { HttpServletRequest req = handlerChain.getRequest(); HttpServletResponse resp = handlerChain.getResponse(); String path = mv.getView(); Map結(jié)語(yǔ)model = mv.getModel(); model.forEach(req::setAttribute); req.getRequestDispatcher(Doodle.getConfiguration().getViewPath() + path).forward(req, resp); } }
至此,MVC的優(yōu)化完成了,同時(shí)整個(gè)doodle框架的代碼也算是完成了。
雖然doodle早已完成,但是講解的文章托托延延到現(xiàn)在才完成。
在剛完成doodle時(shí)感覺(jué)整個(gè)框架已經(jīng)成型了,但是在寫(xiě)這個(gè)系列文章的過(guò)程中才真正發(fā)現(xiàn)欠缺的還有非常非常多,甚至覺(jué)得把它稱(chēng)為框架都有些抬舉它了呢。
只能說(shuō)在實(shí)現(xiàn)它然后再寫(xiě)這個(gè)系列的文章之后對(duì)spring的崇拜之心更加深了,其被javaer廣泛使用和拜讀果然是有原因的。
另外也感謝大家閱讀這個(gè)系列的文章,如果對(duì)大家有所幫助的話(huà)可以去給我的項(xiàng)目加個(gè)star,有什么問(wèn)題和建議也可以提出來(lái)交流交流。
這個(gè)系列的所有文章我都放在我的博客上了:http://zzzzbw.cn/
從零開(kāi)始實(shí)現(xiàn)一個(gè)簡(jiǎn)易的Java MVC框架(一)--前言
從零開(kāi)始實(shí)現(xiàn)一個(gè)簡(jiǎn)易的Java MVC框架(二)--實(shí)現(xiàn)Bean容器
從零開(kāi)始實(shí)現(xiàn)一個(gè)簡(jiǎn)易的Java MVC框架(三)--實(shí)現(xiàn)IOC
從零開(kāi)始實(shí)現(xiàn)一個(gè)簡(jiǎn)易的Java MVC框架(四)--實(shí)現(xiàn)AOP
從零開(kāi)始實(shí)現(xiàn)一個(gè)簡(jiǎn)易的Java MVC框架(五)--引入aspectj實(shí)現(xiàn)AOP切點(diǎn)
從零開(kāi)始實(shí)現(xiàn)一個(gè)簡(jiǎn)易的Java MVC框架(六)--加強(qiáng)AOP功能
從零開(kāi)始實(shí)現(xiàn)一個(gè)簡(jiǎn)易的Java MVC框架(七)--實(shí)現(xiàn)MVC
從零開(kāi)始實(shí)現(xiàn)一個(gè)簡(jiǎn)易的Java MVC框架(八)--制作Starter
從零開(kāi)始實(shí)現(xiàn)一個(gè)簡(jiǎn)易的Java MVC框架(九)--優(yōu)化MVC代碼
源碼地址:doodle
原文地址:從零開(kāi)始實(shí)現(xiàn)一個(gè)簡(jiǎn)易的Java MVC框架(九)--優(yōu)化MVC代碼
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/76715.html
摘要:不過(guò)仔細(xì)了解了一段時(shí)候發(fā)現(xiàn),其實(shí)他的原理是很簡(jiǎn)單的,所以想要自己也動(dòng)手實(shí)現(xiàn)一個(gè)功能類(lèi)似的框架。原文地址從零開(kāi)始實(shí)現(xiàn)一個(gè)簡(jiǎn)易的框架 前言 最近在看spring-boot框架的源碼,看了源碼之后更是讓我感受到了spring-boot功能的強(qiáng)大。而且使用了很多的設(shè)計(jì)模式,讓人在看的時(shí)候覺(jué)得有點(diǎn)難以下手。 不過(guò)仔細(xì)了解了一段時(shí)候發(fā)現(xiàn),其實(shí)他的原理是很簡(jiǎn)單的,所以想要自己也動(dòng)手實(shí)現(xiàn)一個(gè)功能類(lèi)似的...
摘要:接下來(lái)就可以把這個(gè)切點(diǎn)類(lèi)加入到我們之前實(shí)現(xiàn)的功能中了。實(shí)現(xiàn)的切點(diǎn)功能首先改裝注解,把之前改成來(lái)存儲(chǔ)表達(dá)式。測(cè)試用例在上一篇文章從零開(kāi)始實(shí)現(xiàn)一個(gè)簡(jiǎn)易的框架四實(shí)現(xiàn)中的測(cè)試用例的基礎(chǔ)上修改測(cè)試用例。 前言 在上一節(jié)從零開(kāi)始實(shí)現(xiàn)一個(gè)簡(jiǎn)易的Java MVC框架(四)--實(shí)現(xiàn)AOP中我們實(shí)現(xiàn)了AOP的功能,已經(jīng)可以生成對(duì)應(yīng)的代理類(lèi)了,但是對(duì)于代理對(duì)象的選擇只能通過(guò)指定的類(lèi),這樣確實(shí)不方便也不合理。...
摘要:服務(wù)器相關(guān)配置啟動(dòng)類(lèi)資源目錄目錄靜態(tài)文件目錄端口號(hào)目錄目錄實(shí)現(xiàn)內(nèi)嵌服務(wù)器在上一章文章從零開(kāi)始實(shí)現(xiàn)一個(gè)簡(jiǎn)易的框架七實(shí)現(xiàn)已經(jīng)在文件中引入了依賴(lài),所以這里就不用引用了。 spring-boot的Starter 一個(gè)項(xiàng)目總是要有一個(gè)啟動(dòng)的地方,當(dāng)項(xiàng)目部署在tomcat中的時(shí)候,經(jīng)常就會(huì)用tomcat的startup.sh(startup.bat)的啟動(dòng)腳本來(lái)啟動(dòng)web項(xiàng)目 而在spring-b...
摘要:在前面的文章中實(shí)現(xiàn)的功能時(shí),目標(biāo)類(lèi)都只能被一個(gè)切面代理,如果想要生成第二個(gè)代理類(lèi),就會(huì)把之前的代理類(lèi)覆蓋。改裝原有功能現(xiàn)在要改裝原來(lái)的的實(shí)現(xiàn)代碼,讓的功能加入到框架中為了讓切面能夠排序,先添加一個(gè)注解,用于標(biāo)記排序。 前言 在前面從零開(kāi)始實(shí)現(xiàn)一個(gè)簡(jiǎn)易的Java MVC框架(四)--實(shí)現(xiàn)AOP和從零開(kāi)始實(shí)現(xiàn)一個(gè)簡(jiǎn)易的Java MVC框架(五)--引入aspectj實(shí)現(xiàn)AOP切點(diǎn)這兩節(jié)文章...
摘要:容器實(shí)際上就是存放所有的地方,即以及相關(guān)信息對(duì)應(yīng)其實(shí)體的容器,為什么稱(chēng)之為呢,因?yàn)樵谥?,定義信息和實(shí)例的東西叫。了解到這個(gè)以后接下來(lái)就可以開(kāi)始編寫(xiě)容器了,在包下創(chuàng)建一個(gè)類(lèi)叫。獲取容器實(shí)例至此,這個(gè)容器就完成了。 項(xiàng)目準(zhǔn)備 首先確保你擁有以下環(huán)境或者工具 idea java 8 maven 3.3.X lombok插件 然后我們創(chuàng)建一個(gè)maven工程,編寫(xiě)pom.xml引入一些需要的...
閱讀 1592·2021-10-18 13:35
閱讀 2371·2021-10-09 09:44
閱讀 829·2021-10-08 10:05
閱讀 2724·2021-09-26 09:47
閱讀 3585·2021-09-22 15:22
閱讀 443·2019-08-29 12:24
閱讀 2009·2019-08-29 11:06
閱讀 2866·2019-08-26 12:23