1.xml建模的目的及分析过程
假如有一个xml文件的内容如下:
<config>
<action path="/regAction" type="test.RegAction">
<forward name="failed" path="/reg.jsp" redirect="false" />
<forward name="success" path="/login.jsp" redirect="true" />
</action>
<action path="/loginAction" type="test.LoginAction">
<forward name="failed" path="/login.jsp" redirect="false" />
<forward name="success" path="/main.jsp" redirect="true" />
</action>
</config>
需求1:程序员张三需要通过/loginAction获取type=test.LoginAction,下面我们来分析一下这个过程:
步骤一:加载资源文件xxx..class.getResourceAsStream("类名.xml");
步骤二:通过xpath解析获取到所有的action元素标签列表
步骤三:遍历,如果action标签的path属性等于/loginAction,那么就是我们要找的action标签
步骤四:找到的action,我们就可以获取到对应的action标签的type属性值
现在呢,又有一个叫李四的程序员有这样的需求:程序员李四需要通过/regAction获取type=test.RegAction,下面我们来分析一下这个过程:
步骤一:加载资源文件xxx..class.getResourceAsStream("类名.xml");
步骤二:通过xpath解析获取到所有的action元素标签列表
步骤三:遍历,如果action标签的path属性等于/regAction,那么就是我们要找的action标签
步骤四:找到的action,我们就可以获取到对应的action标签的type属性值
通过上面两个需求以及分析的过程来看,我们可以很清楚看到两个需求的步骤基本上是相同的,只需要修改条件就OK,假如还有一个王五也想要一个通过/regAction
获取type=test.RegAction这样的需求,这个过程与张三李四都是重复的,那么我们是不是可以写活用
的方法去调用方法,那么是不是很方便,所以我们要学习xml建模,xml建模不仅是一个知识
也是一种解决方案
,如果每一个需求都需要去解析一次
,那必定会重复
很多的代码,这比较繁琐
,所以我们要学习建模来简化
xml解析的过程
附上xml解析的分析
过程图
对上述进行图解
建模案例1
下面我们来分析一下 怎样进行xml建模呢?
xml建模的核心思想
就是利用java面向对象
的特性,用操作对象的方式来操作xml
还是与上述一样,假如有一个xml文件的内容如下:
<config>
<action path="/regAction" type="test.RegAction">
<forward name="failed" path="/reg.jsp" redirect="false" />
<forward name="success" path="/login.jsp" redirect="true" />
</action>
<action path="/loginAction" type="test.LoginAction">
<forward name="failed" path="/login.jsp" redirect="false" />
<forward name="success" path="/main.jsp" redirect="true" />
</action>
</config>
我们可以把config、action 、forward 看作是一个对象,及一个标签对应一个对象,所以这个xml中我们可以分析出有三个对象(对象也是类,比如猫,狗呀):
下面我们来看一下分析xml建模的思路图(附):
既然我们分析完了,接下来我们来从下往上开始动手写代码(建模)
写代码时我们需要注意的事项:
1、建模必须从里到外
开始建模
既然是从里到外建模,首先我们先建ForwardModel类(forward标签)
package com.xiaoqing.model;
public class ForwardModel {
// <forward name="failed" path="/login.jsp" redirect="false" />
private String name;
private String path;
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;
}
}
接下来我们建ActionModel类(action标签):
package com.xiaoqing.model;
import java.util.HashMap;
import java.util.Map;
public class ActionModel {
/**
* <action path="/loginAction" type="test.LoginAction">
*
*/
private String path;
private String type;
/**
* 因为action标签下有多个子标签,所以我们用map集合来存储
* @return
*/
private Map<String, ForwardModel> foMap=new HashMap<>();
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;
}
/**
* 两个行为
*/
/**
* 将指定的forwardModel压入当前actionModel对象中
* 把<forward/>放入<action></action>
* @param forwardModel
*/
public void push(ForwardModel forwardModel) {
foMap.put(forwardModel.getName(),forwardModel);
}
}
再接下来建ConfigModel类(config标签,最外层):
package com.xiaoqing.model;
import java.util.HashMap;
import java.util.Map;
public class ConfigModel {
private Map<String, ActionModel> acMap=new HashMap<String, ActionModel>();
/**
* 新增Action对象的行为:
* 将指定的actionModel放入当前ConfigModel对象中
* 把<action></action>放入<config></config>
* @param actionModel
*/
public void push(ActionModel actionModel) {
acMap.put(actionModel.getPath(),actionModel);
}
/**
* 通过action对象name属性查找对应的Forward对象的行为
* @param name
* @return
*/
public ActionModel pop(String path) {
return acMap.get(path);
}
}
xml中有三个标签,我们现在也将这三个标签建模建好了,现在我们要把他们集合到一起,所以最后我们要建一个工厂类把他们封装起来,相当于吃汉堡那个案例,前面在自定义MVC时思维导图中的xml建模那部分有说到过
工厂模式解决的问题
:将代码封装易用,提高代码的复用性(类比汉堡的获取方式:肯德基直接购买,原材料自己制作)
一般工厂类一定会有一个方法,就是生产指定模型对象的方法
package com.xiaoqing.model;
import java.io.InputStream;
import java.util.List;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
/**
* 23 种设计模式之工厂模式
*
*
* 工厂模式解决的问题,将代码封装,提高代码的复用性
* 类比汉堡的获取方式(肯德基直接购买,原材料自己制作)
*
*
* 一般工厂类一定会有一个方法,就是生产指定模型对象的方法
* 一般方法名为:build newInstance
*
*
* 注意:
* 一般在工厂类中会有两个以上的构建方法,一个是默认框架路径的模型对象构建方法(无参的方法,是固定的),
* 还有一个是动态读取任意位置下的框架配置文件(有参的方法,是活用的)
* @author 晴sister
*
* https://i.csdn.net/#/uc/profile
*/
public class ConfigModelFactory {
/**
* 通过资源文件构建对应的模型对象
* @param path 具体的资源文件路径
* @return
* @throws Exception
*/
public static ConfigModel build(String path) throws Exception {
//path=/config.xml
InputStream in = ConfigModelFactory.class.getResourceAsStream(path);
SAXReader reader=new SAXReader();
//config.xml里的内容
Document doc = reader.read(in);
//接下来就做一件事情--->把内容填充到configModel对象中(doc.asXML--->configModel)
ConfigModel configModel=new ConfigModel();
ActionModel actionModel=null;
ForwardModel forwardModel=null;
List<Element> actionEles = doc.selectNodes("/config/action");
for (Element actionEle : actionEles) {
actionModel=new ActionModel();
actionModel.setPath(actionEle.attributeValue("path"));
actionModel.setType(actionEle.attributeValue("type"));
//给actionModel中放入ForwardModel对象??
//拿到forward标签内容
List<Element> forwardEles = actionEle.selectNodes("forward");
for (Element forwardEle : forwardEles) {
forwardModel=new ForwardModel();
forwardModel.setName(forwardEle.attributeValue("name"));
forwardModel.setPath(forwardEle.attributeValue("path"));
//redirect属性是Boolean的
//需求:只有config.xml中redirect属性值填写雷亮false,它才代表转发(不填或者填其他的都代表重定向)
//forwardEle.attributeValue("redirect")获取到config中的redirect属性值,那个字符串
forwardModel.setRedirect(!"false".equals(forwardEle.attributeValue("redirect")));
actionModel.push(forwardModel);
}
configModel.push(actionModel);
}
return configModel;
}
/**
* 默认框架路径的模型对象构建方法
* @return
* @throws Exception
*/
public static ConfigModel build() throws Exception {
return build("/config.xml");
}
public static void main(String[] args) throws Exception {
ConfigModel model=ConfigModelFactory.build();
// ActionModel actionModel = model.pop("/regAction");
//System.out.println(actionModel.getType());
//需求:获取/loginAction下的success结果码对应的页面/main.js
ActionModel actionModel = model.pop("/loginAction");
System.out.println(actionModel.getType());
ForwardModel forwardModel = actionModel.pop("success");
System.out.println(forwardModel.getPath());
}
}
注意:
- 一般在工厂类中会有两个以上的构建方法,一个是默认框架路径的模型对象构建方法(无参的方法,是固定的),
- 还有一个是动态读取任意位置下的框架配置文件(有参的方法,是活用的)
建模案例2
通过上面详细讲解的案例,现在我们自己独立完成一个完整xml建模思路及代码
有一个web.xml文件的内容如下:
<web-app>
<servlet>
<servlet-name>jrebelServlet</servlet-name>
<servlet-class>com.zking.xml.JrebelServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>jrebelServlet</servlet-name>
<url-pattern>/jrebelServlet</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>jrebelServlet2</servlet-name>
<servlet-class>com.zking.xml.JrebelServlet2</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>jrebelServlet2</servlet-name>
<url-pattern>/jrebelServlet2</url-pattern>
<url-pattern>/jrebelServlet3</url-pattern>
</servlet-mapping>
</web-app>
首先我们先来分析一下需求:
需求已经出来了,接下来我们来分析xml建模的过程:
首先先做好准备工作:
导入 架包:
导入web.xml文件,这里我放在源文件夹下
代码实现:
先建url_patternModel类(标签url-pattern)
package com.xiaoqing.webmodel;
public class Url_PatternModel {
private String val;
public String getVal() {
return val;
}
public void setVal(String val) {
this.val = val;
}
}
然后继续往上走,建servlet-classModel类(标签servlet-class)
package com.xiaoqing.webmodel;
public class Servlet_ClassModel {
private String val;
public String getVal() {
return val;
}
public void setVal(String val) {
this.val = val;
}
}
继续向上走,建servlet-nameModel类:(标签servlet-name)
package com.xiaoqing.webmodel;
public class Servlet_NameModel {
private String val;
public String getVal() {
return val;
}
public void setVal(String val) {
this.val = val;
}
}
继续向上走,建servlet-mappingModel类:(标签servlet-mapping)
package com.xiaoqing.webmodel;
import java.util.HashMap;
import java.util.Map;
import com.xiaoqing.model.ActionModel;
public class Servlet_MappingModel {
private Servlet_NameModel servlet_NameModel;//<servlet-mapping>中的子标签<servlet-name>,只能有一个,所以不能用集合
private Map<String,Url_PatternModel> urlMap=new HashMap<String, Url_PatternModel>();//<servlet-mapping>中的子标签<url-pattern>
public Servlet_NameModel getServlet_NameModel() {
return servlet_NameModel;
}
public void setServlet_NameModel(Servlet_NameModel servlet_NameModel) {
this.servlet_NameModel = servlet_NameModel;
}
/**
* 放入servlet对象中
* <servlet-mapping>放入<servlet>中
* @param url_PatternModel
*/
public void push(Url_PatternModel url_PatternModel,Servlet_NameModel servlet_NameModel) {
urlMap.put(servlet_NameModel.getVal(), url_PatternModel);
}
/**
* 取
* @param val
* @return
*/
public Url_PatternModel pop(String val) {
return urlMap.get(val);
}
}
建servlet类:标签(servlet)
package com.xiaoqing.webmodel;
public class ServletModel {
private Servlet_NameModel servlet_NameModel;//<servlet>中的子标签<servlet-name>
private Servlet_ClassModel servlet_ClassModel;//<servlet>中的子标签<servlet-class>
public Servlet_NameModel getServlet_NameModel() {
return servlet_NameModel;
}
public void setServlet_NameModel(Servlet_NameModel servlet_NameModel) {
this.servlet_NameModel = servlet_NameModel;
}
public Servlet_ClassModel getServlet_ClassModel() {
return servlet_ClassModel;
}
public void setServlet_ClassModel(Servlet_ClassModel servlet_ClassModel) {
this.servlet_ClassModel = servlet_ClassModel;
}
}
然后就是web-app类:(标签web-app)
package com.xiaoqing.webmodel;
import java.util.HashMap;
import java.util.Map;
public class Web_AppModel {
private Map<String, ServletModel> servletMap=new HashMap<String, ServletModel>();//<web-app>中的子标签<servlet>
private Map<String, Servlet_MappingModel> servletMappingMap=new HashMap<String, Servlet_MappingModel>();//<web-app>中的子标签<servlet-mapping>
/**
* 放入servlet对象中
* <servlet>放入<web-app>中
* @param servletModel
*/
public void pushServlet(ServletModel servletModel) {
servletMap.put(servletModel.getServlet_NameModel().getVal(), servletModel);
}
public ServletModel popServlet(String servlet_NameModel) {
return servletMap.get(servlet_NameModel);
}
/**
* 放入servlet对象中
* <servlet-mapping>放入<web-app>中
* @param Servlet_MappingModel
*/
public void pushServletMapping(Servlet_MappingModel servlet_MappingModel) {
servletMappingMap.put(servlet_MappingModel.getServlet_NameModel().getVal(), servlet_MappingModel);
}
public Servlet_MappingModel popMapping(String servlet_NameModel) {
return servletMappingMap.get(servlet_NameModel);
}
}
最后建一个web-app工厂类
package com.xiaoqing.webmodel;
import java.io.InputStream;
import java.util.List;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
public class Web_AppModelFactory {
/**
* 通过资源文件构建对应的模型对象
* @param path 具体的资源文件路径
* @return
* @throws Exception
*/
public static Web_AppModel build(String path) throws Exception {
//path=/web.xml
InputStream in = Web_AppModelFactory.class.getResourceAsStream(path);
SAXReader reader=new SAXReader();
//web.xml里的内容
Document doc = reader.read(in);
//System.out.println(doc.asXML());
Web_AppModel web_AppModel=new Web_AppModel();
ServletModel servletModel=null;
Servlet_MappingModel servlet_MappingModel=null;
/*
* 将servlet的标签内容填充进WebApp
*/
List<Element> servletEles = doc.selectNodes("/web-app/servlet");
for (Element servletEle : servletEles) {
/*
* 给ServletModel填充xml的内容
*/
servletModel=new ServletModel();
//查找单节点
Element servletNameEle = (Element)servletEle.selectSingleNode("servlet-name");
Element servletClassEle = (Element)servletEle.selectSingleNode("servlet-class");
Servlet_NameModel servlet_NameModel=new Servlet_NameModel();
Servlet_ClassModel servlet_ClassModel=new Servlet_ClassModel();
servlet_NameModel.setVal(servletNameEle.getText());
servlet_ClassModel.setVal(servletClassEle.getText());
//将<servlet-name>,<servlet-class>放入servlet中,别忘了,正因为这个原因报了很久的空指针异常
servletModel.setServlet_ClassModel(servlet_ClassModel);
servletModel.setServlet_NameModel(servlet_NameModel);
web_AppModel.pushServlet(servletModel);
//System.out.println(servletModel.getServlet_NameModel().getVal());
//
}
/*
* 将servlet-mapping的标签内容填充进WebApp
*/
List<Element> servlet_MappingEles = doc.selectNodes("/web-app/servlet-mapping");
//System.err.println("---------------");
for (Element servlet_MappingEle : servlet_MappingEles) {
servlet_MappingModel=new Servlet_MappingModel();
/*
* 给ServletMappingModel填充xml的内容
*/
Element servletNameEle =(Element) servlet_MappingEle.selectSingleNode("servlet-name");
Servlet_NameModel servlet_NameModel=new Servlet_NameModel();
servlet_NameModel.setVal(servletNameEle.getText());
servlet_MappingModel.setServlet_NameModel(servlet_NameModel);
List<Element> urlPatternEles = servlet_MappingEle.selectNodes("url-pattern");
for (Element urlPatternEle : urlPatternEles) {
Url_PatternModel url_PatternModel=new Url_PatternModel();
url_PatternModel.setVal(urlPatternEle.getText());
servlet_MappingModel.push(url_PatternModel,servlet_NameModel);
}
web_AppModel.pushServletMapping(servlet_MappingModel);
}
return web_AppModel;
}
public static Web_AppModel build() throws Exception {
return build("/web.xml");
}
}
下面我们来测试一下:
public static void main(String[] args) throws Exception {
Web_AppModel wm=Web_AppModelFactory.build();
ServletModel servletModel=wm.popServlet("jrebelServlet");
Servlet_MappingModel servlet_MappingModel = wm.popMapping("jrebelServlet2");
Url_PatternModel urlpattern = servlet_MappingModel.pop(servlet_MappingModel.getServlet_NameModel().getVal()) ;
System.out.println("servlet-name:"+servletModel.getServlet_NameModel().getVal());
System.out.println("servlet-class:"+servletModel.getServlet_ClassModel().getVal());
System.out.println();
System.out.println("servlet-mapping(name):"+servlet_MappingModel.getServlet_NameModel().getVal());
System.out.println("servlet-mapping(url-mapping):"+urlpattern.getVal());
}
最终的结果:
2.总结
1.为什么需要xml建模?
1.代码封装,提高复用性
2.提高性能,xml解析的过程只会有一次了
2.建模的过程
1.获取到资源文件
2.分析xml标签,将其看成对象,分析该对象的属性及行为
3.解析出xml资源文件中的所有内容
4.建模的核心部分:将xml资源文件中的内容填充到各个标签"<action>
"对应的模型对象ActionModel
5.注意要将子模型对象填充到父模型中
6.最后测试
小编留言:一个bug找了差不多半天,一直报空指针异常,原来是servlet没有对象,这次吸取了教训,希望广大的码农别像我一样哦!希望能帮到大家,欢迎大家留言评论!谢谢