心中没有答案也没紧要,由于通过本日的分享,能让你轻松 get 如下几点,绝对收成满满。
1. XML 解析的办法;
2. digester 的用法;
3. Java WEB 框架的实现思路;
4. 从 0 到 1 徒手实现一个迷你 WEB 框架。
1. XML 解析办法在 Java 项目研发过程中,不论项目大小,险些都能见到 XML 配置文件的踪影。利用 XML 可以进行项目配置;也可以作为对接三方 API 时数据封装、报文传输转换,等等很多利用场景。
而 XML 文件该如何解析?则是一个旧调重弹的问题,也是研发中选型常常面临的一个问题。通过思维导图梳理,把问题都扼杀在摇篮里。
如导图所示,DOM 和 SAX 是 XML 常见的两大核心解析办法,两者的紧张差异在于它们解析 XML 文件的办法不同。利用 DOM 解析,XML 文件以 DOM 树形构造加载入内存,而 SAX 采取的是事宜模型。
基于这两大解析办法,衍生了一系列的 API,也便是造出了一大批轮子,到底用哪款轮子呢?下面就叨咕叨咕。
上面罗列的这些,你都知道或者用过吗?为了便于你影象,咱们就聊聊发展历史吧。
首先 JAXP 的涌现是为了填补 JAVA 在 XML 标准制订上的空缺,而制订的一套 JAVA XML 标准 API,是对底层 DOM、SAX 的 API 大略封装;而原始 DOM 对付 Java 开拓者而言较难堪用,于是一批 Java 爱好者为了能让解析 XML 得心应手,码出了 jdom;另一批人在 jdom 的根本上重新努力别辟门户,码出了 dom4j,由于 jdom 性能不抵 dom4j,dom4j 则独占鳌头,很多开源框架都用 dom4j 来解析配置文件。
XStream 本不应该涌如今这里,但是鉴于是履历分享,索性也列了出来,在以往项目中报文转换时用的轻微多些,尤其是支付 API 对接时用的超级多,利用它可以很随意马虎的实现 Java 工具和 XML 文档的互转(感兴趣的可以自行补充一下)。
digester 是采取 SAX 来解析 XML 文件,在 Tomcat 中就用 Digester 来解析配置,在 Struts 等很多开源项目,也都用到了 digester 来解析配置文件,在实际项目研发中,也会用它来做协议解析转换,以是这块有必要深入去说一下,对你看源码该当会有帮助。
2. digester 的用法
弱弱问一句:有没有听过 digester,若没有听过,那势必要好好读本文啦。
如果要对本地的 miniframework-config.xml 文件,采取 digester 的办法进行解析,该当怎么做?(配置文件的内容有似曾相识的觉得没?文末解谜)
<?xml version="1.0" encoding="UTF-8"?><action-mappings> <action path="/doOne" type="org.yyxj.miniframework.action.OneAction"> <forward name="one" path="/one.jsp" redirect="false"/> </action> <action path="/doTwo" type="org.yyxj.miniframework.action.TwoAction"> <forward name="two" path="/two.jsp" redirect="true"/> </action></action-mappings>
digester 进行解析 xml,须要依赖解析规则(便是见告 digester 怎么个解析法)。可以利用 Java 硬编码的办法指定解析规则;也可以采取零配置思想,利用表明的办法来指定解析规则;还可以利用 xml 办法配置解析规则。
为了清晰起见,本次就采取 xml 办法进行配置解析规则,解析规则 rule.xml 内容如下。
<?xml version='1.0' encoding='UTF-8'?><digester-rules> <pattern value="action-mappings"> <!-- value是匹配的xml标签的名字,匹配<action>标签 --> <pattern value="action"> <!--每碰到一个action标签,就创建指定类的工具--> <object-create-rule classname="org.yyxj.miniframework.config.ActionMapping"/> <!-- 工具创建后,调用ActionMappings的addActionMapping()方法, 将其加入它上一级元素所对应的工具ActionMappings中 --> <set-next-rule methodname="addActionMapping"/> <!-- 将action元素的各个属性按照相同的名称 赋值给刚刚创建的ActionMapping工具 --> <set-properties-rule/> <!-- 匹配<forward>标签 --> <pattern value="forward"> <!--每碰到一个forward标签,就创建指定类的工具--> <object-create-rule classname="org.yyxj.miniframework.config.ForwardBean"/> <!-- 工具创建后,调用ActionMapping的addForwardBean()方法, 将其加入它上一级元素所对应的工具ActionMapping中 --> <set-next-rule methodname="addForwardBean"/> <!-- 将forward元素的各个属性按照相同的名称 赋值给刚刚创建的ForwardBean工具 --> <set-properties-rule/> </pattern> </pattern> </pattern></digester-rules>
2.2. 创建规则解析依赖的 Java 类
首先是 ActionMappings 类,要供应 addActionMapping 方法以便添加 ActionMapping 工具,考虑到后面会依据要求路径找 ActionMapping,索性也定义一个 findActionMapping 的方法,代码如下。
package org.yyxj.miniframework.config;import java.util.HashMap;import java.util.Map;/ @author 一猿小讲 /public class ActionMappings { private Map<String, ActionMapping> mappings = new HashMap<String, ActionMapping>(); public void addActionMapping(ActionMapping mapping) { this.mappings.put(mapping.getPath(), mapping); } public ActionMapping findActionMapping(String path) { return this.mappings.get(path); } @Override public String toString() { return mappings.toString(); }}
依据解析规则文件,接下来会匹配到 miniframework-config.xml 文件的 action 标签,要定义对应的 ActionMapping 类,包含要求路径及让谁处理的类路径,当然也要供应 addForwardBean 方法用于添加 ForwardBean 工具,代码定义如下。
package org.yyxj.miniframework.config;import java.util.HashMap;import java.util.Map;/ @author 一猿小讲 /public class ActionMapping { private String path; private String type; private Map<String, ForwardBean> forwards = new HashMap<String, ForwardBean>(); public void addForwardBean(ForwardBean bean) { forwards.put(bean.getName(), bean); } public ForwardBean findForwardBean(String name) { return forwards.get(name); } public String getPath() { return path; } public void setPath(String path) { this.path = path; } public String getType() { return type; } public void setType(String type) { this.type = type; } @Override public String toString() { return path + "==" + type + "==" + this.forwards.toString(); }}
依据解析规则文件,接下来会匹配到 miniframework-config.xml 文件的 forward 标签,那么就要创建与之对应的 ForwardBean 类,并且拥有 name、path、redirect 三个属性,代码定义如下。
package org.yyxj.miniframework.config;/ @author 一猿小讲 /public class ForwardBean { private String name; private String path; private boolean redirect; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPath() { return path; } public void setPath(String path) { this.path = path; } public boolean isRedirect() { return redirect; } public void setRedirect(boolean redirect) { this.redirect = redirect; } @Override public String toString() { return name + "==" + path + "==" + redirect; }}
2.3. 引入依赖包,编写测试类
<dependency> <groupId>commons-digester</groupId> <artifactId>commons-digester</artifactId> <version>2.1</version></dependency>
编写测试类。
package org.yyxj.miniframework.config;import org.apache.commons.digester.Digester;import org.apache.commons.digester.xmlrules.DigesterLoader;import org.xml.sax.SAXException;import java.io.IOException;/ Digester用法测试类 @author 一猿小讲 /public class Test { public static void main(String[] args) throws IOException, SAXException { String rlueFile = "org/yyxj/miniframework/config/rule.xml"; String configFile = "miniframework-config.xml"; Digester digester = DigesterLoader.createDigester( Test.class.getClassLoader().getResource(rlueFile)); ActionMappings mappings = new ActionMappings(); digester.push(mappings); digester.parse(Test.class.getClassLoader().getResource(configFile)); System.out.println(mappings); }}
2.4. 跑起来,看看解析是否 OK?
程序输出如下:{/doOne=/doOne==org.yyxj.miniframework.action.OneAction=={one=one==/one.jsp==false}, /doTwo=/doTwo==org.yyxj.miniframework.action.TwoAction=={two=two==/two.jsp==true}}
到这儿 digester 解析 xml 就算达到了预期效果,digester 解析实在起来很大略,如法泡制撸两遍,就自然而然节制,以是不要被乌央乌央的代码给吓退缩(代码只是方便你施展 CV 大法)。
不过,会用 digester 解析 xml 还不算完事,还想扩展一下思路,站在上面代码的根本之上,去考试测验实现一个迷你版的 WEB 框架。
3. WEB 框架的实现思路
此时请忘却 digester 解析的事情,脑海里只需保留开篇提到的 miniframework-config.xml 文件,怕你忘却,就再贴一遍。
图中赤色圈住部分,实在可以这么理解,当用户要求的 path 为 /doOne 时,会交给 OneAction 去处理,处理完之后的返回结果若是 one,则跳转到 one.jsp,给前端相应。
为了说的更清晰,说清楚思路,还是画一张图吧。
ActionServlet 紧张是吸收用户要求,然后根据要求的 path 去 AcitonMappings中探求对应的 ActionMapping,然后依据 ActionMapping 找到对应的 Action,并调用 Action 完成业务处理,然后把相应视图返回给用户,多少都透漏着 MVC 设计模式中 C 的角色。
Action 紧张是业务掌握器,实在很大略,只需供应抽象的 execute 方法即可,详细怎么实行交给详细的业务实现类去实现吧。
4. 徒手实现迷你版的 WEB 框架
鉴于 ActionMappings、ActionMapping、ForwardBean 已是可复用代码,紧张是完成 miniframework-config.xml 文件的解析,那接下来只需把图中缺失落的类定义一下就 Ok 啦。
4.1. 中心掌握器 ActionServlet
package org.yyxj.miniframework.controller;import org.apache.commons.digester.Digester;import org.apache.commons.digester.xmlrules.DigesterLoader;import org.yyxj.miniframework.config.ActionMapping;import org.yyxj.miniframework.config.ActionMappings;import org.yyxj.miniframework.config.ForwardBean;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;/ 中心掌握器 1. 吸收客户真个要求; 2. 根据要求的 path 找到对应的 Action 来处理业务。 @author 一猿小讲 /public class ActionServlet extends HttpServlet { private static final long serialVersionUID = 1L; private ActionMappings mappings = new ActionMappings(); public static final String RULE_FILE = "org/yyxj/miniframework/config/rule.xml"; public static final String EASY_STRUTS_CONFIG_FILE = "miniframework-config.xml"; @Override public void init() { Digester digester = DigesterLoader.createDigester(ActionServlet.class.getClassLoader().getResource(RULE_FILE)); digester.push(mappings); try { digester.parse(ActionServlet.class.getClassLoader().getResource(EASY_STRUTS_CONFIG_FILE)); } catch (Exception e) { // LOG } } @Override public void service(HttpServletRequest request, HttpServletResponse response) throws IOException { request.setCharacterEncoding("UTF-8"); response.setContentType("text/html;charset=utf-8"); // 获取要求路径 String uri = request.getRequestURI(); String path = uri.substring(uri.lastIndexOf("/"), uri.lastIndexOf(".")); // 1:根据要求路径获取对应的 Action 来处理详细的业务 ActionMapping mapping = mappings.findActionMapping(path); try { Action action = (Action) Class.forName(mapping.getType()).newInstance(); // 2:进行业务处理,并返回实行的结果 String result = action.execute(request, response); // 3:依据实行结果找到对应的 ForwardBean ForwardBean forward = mapping.findForwardBean(result); // 4:相应 if (forward.isRedirect()) { response.sendRedirect(request.getContextPath() + forward.getPath()); } else { request.getRequestDispatcher(forward.getPath()).forward(request, response); } } catch (Exception e) { // LOG System.err.println(String.format("service ex [%s]", e.getMessage())); } }}
4.2. 业务掌握器 Action 及业务实现
package org.yyxj.miniframework.controller;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;/ 业务掌握器 @author 一猿小讲 /public abstract class Action { public abstract String execute(HttpServletRequest request, HttpServletResponse response) throws Exception;}
紧接着就定义详细的业务实现呗。
package org.yyxj.miniframework.action;import org.yyxj.miniframework.controller.Action;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;/ 业务实现 @author 一猿小讲 /public class OneAction extends Action { @Override public String execute(HttpServletRequest request, HttpServletResponse response) throws Exception { return "one"; }}
TwoAction 与 OneAction 一样都是继续了 Action,实现 execute 方法。
package org.yyxj.miniframework.action;import org.yyxj.miniframework.controller.Action;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;/ 业务实现 @author 一猿小讲 /public class TwoAction extends Action { @Override public String execute(HttpServletRequest request, HttpServletResponse response) throws Exception { return "two"; }}
4.3. 配置 web.xml,配置做事启动入口
<?xml version="1.0" encoding="UTF-8"?><web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <servlet> <servlet-name>ActionServlet</servlet-name> <servlet-class>org.yyxj.miniframework.controller.ActionServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>ActionServlet</servlet-name> <url-pattern>.do</url-pattern> </servlet-mapping> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list></web-app>
4.4. 画三个 JSP 页面出来,便于验证
index.jsp 内容如下。
<%@ page pageEncoding="UTF-8"%><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html> <head> <title>My JSP 'index.jsp' starting page</title> </head> <body> <a href="doOne.do">DoOneAction</a><br/> <a href="doTwo.do">DoTwoAction</a><br/> </body></html>
one.jsp 内容如下。
<%@ page pageEncoding="UTF-8"%><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">一猿小讲 say one .... ...
two.jsp 内容如下。
<%@ page pageEncoding="UTF-8"%><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">一猿小讲 say two .... ...
4.5. 支配、启动 WEB 做事
到这一个迷你版的 WEB 框架就完事啦,把项目打成 war 包,放到 tomcat 里跑起来,验证一下。
4.6. 项目构造一览
蓝色圈住部分可以打成 miniframework.jar 包,当做可复用类库,在其它项目中直接引入,只需编写赤色圈住部分的业务 Action 以及页面就好啦。
5. 答疑解谜
本次紧张聊了聊 xml 解析的办法,着重分享了 digester 的用法,并站在 digester 解析 xml 的根本之上,徒手仿照了一个 WEB 的迷你版的框架。
如果你研究过 Tomcat 的源码或者利用过 Struts 的话,本日的分享该当很随意马虎节制,由于它们都用到了 digester 进行解析配置文件。
鉴于目前据我知道的很多公司的老项目,技能栈还勾留在 Struts 上,以是有必要进行一次老技能新谈。
坊间这么说「只要会 XML 解析,搞懂反射,熟习 Servlet,口试问到什么框架都不怕,由于打通了任督二脉,框架看一眼就基本知道事理啦」。
不过,技能更新确实快,稍有不慎就 out,不过在追逐新技能的同时,老技能的思想理念也别全抛在脑后,如果真能打通任督二脉,做到交融贯通那就最好啦。
好了,本次的分享就到这里,希望你们喜好。
欢迎关注「一猿小讲」,理解更多精彩。