手撸一个自己的简易struts2框架吧!
捋清逻辑
思想
1、整个应用只有一个servlet
2、每个请求访问的是单个的Action
3、所有请求的Action类都有公共的接口,接口中有个execute()
4、解耦,通过xml进行Action配置,后续操作无需更改核心Servlet源码,只需按规定方法创建Action类并配置xml文件即可
路由请求的执行路径
UML类图
包设计
–lf.blank
----lf.blank.framework
-------bean
-----------ActionMapping.java
-----------Result.java
-----------ConfigurationManager.java
----Action.java
----MainServlet.java
–blankstruts.xml
写代码前准备
编译环境:tomcat任意版本+jdk任意版本+eclipse (本人用tomcat9+jdk11+eclipse)
准备读取xml文件的jar包
dom4j-2.1.1.jar
jaxen-full.jar
saxpath.jar
开始撸代码
Action接口
package lf.blank.framework;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Action通用接口
*
* @author blank
*/
public interface Action {
public String execute(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException;
}
ActionMapping封装类
package lf.blank.framework.bean;
import java.util.Map;
/**
* 封装Action
*
* @author blank
* @Param name Action名
* @Param className Action类全称
* @Param method 要执行的方法
* @Param results 结果视图Map
*/
public class ActionMapping {
private String name;
private String className;
private String method;
private Map<String, Result> results;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public String getMethod() {
return method;
}
public void setMethod(String method) {
this.method = method;
}
public Map<String, Result> getResults() {
return results;
}
public void setResults(Map<String, Result> results) {
this.results = results;
}
}
Result封装类
package lf.blank.framework.bean;
/**
* 封装结果视图
*
* @author blank
* @Param name 视图名称
* @Param type 视图类型
* @param page 视图地址
*/
public class Result {
private String name;
private String type;
private String page;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getPage() {
return page;
}
public void setPage(String page) {
this.page = page;
}
}
blankstruts.xml
<?xml version="1.0" encoding="UTF-8"?>
<struts>
<package>
<!-- 我们对action标签属性限定
name、class必填 且示例如下
method非必填,默认为execute-->
<action name="login" class="lf.blank.web.LoginAction">
<!-- result标签我们也做限定
name必填
页面路径必填
type非必填 默认为dispatcher 还可为redirect -->
<result name="success">/index.jsp</result>
<result name="error">/error.jsp</result>
<result name="login" type="redirect">/login.jsp</result>
</action>
</package>
</struts>
配置文件管理类
package lf.blank.framework.bean;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
/**
* blankstruts.xml配置文件管理者 读取配置文件
*
* @author blank
* @Param actionMappings 所有配置的Action
*/
public class ConfigurationManager {
private Map<String, ActionMapping> actionMappings;
public ConfigurationManager() {
// TODO Auto-generated constructor stub
actionMappings = new HashMap<String, ActionMapping>();
this.init();
}
/**
* 初始化函数,读取xml文件
*/
private void init() {
try {
// 1) 获得配置文件输入流
InputStream inputStream = ConfigurationManager.class.getResourceAsStream("/blankstruts.xml");
// 2) 创建SAXReader对象
SAXReader saxReader = new SAXReader();
// 3)获得文档信息
Document doc = saxReader.read(inputStream);
// 4)获得action标签列表
List<Node> actionNodes = doc.selectNodes("//action");
// 5)遍历action标签将信息加入actionMappings
for (Node node : actionNodes) {
Element actionElement = (Element) node;
// 新建一个ActionMapping对象
ActionMapping actionMapping = new ActionMapping();
actionMapping.setName(actionElement.attributeValue("name"));
actionMapping.setClassName(actionElement.attributeValue("class"));
if (actionElement.attributeValue("method") == null) {
// 设置默认方法
actionMapping.setMethod("execute");
} else {
actionMapping.setMethod(actionElement.attributeValue("method"));
}
// 获取action内配置的结果视图
List<Element> resultList = actionElement.elements("result");
Map<String, Result> results = new HashMap<String, Result>();
for (Element resultElement : resultList) {
// 新建结果视图对象进行封装
Result result = new Result();
result.setName(resultElement.attributeValue("name"));
if (resultElement.attributeValue("type") == null) {
// 设置默认视图类型为 转发
result.setType("dispatcher");
} else {
result.setType(resultElement.attributeValue("type"));
}
result.setPage(resultElement.getText().trim());
// 将result放入results中
results.put(result.getName(), result);
}
actionMapping.setResults(results);
actionMappings.put(actionMapping.getName(), actionMapping);
}
} catch (DocumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public Map<String, ActionMapping> getActionMappings() {
return actionMappings;
}
}
MainServlet
package lf.blank.framework;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import lf.blank.framework.bean.ActionMapping;
import lf.blank.framework.bean.ConfigurationManager;
import lf.blank.framework.bean.Result;
/**
* 框架入口Servlet 对所有后缀为.action的访问进行处理
*
* @author blank
* @Param manager 配置文件管理者对象
*/
@WebServlet("*.action")
public class MainServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private ConfigurationManager manager;
/**
* @see HttpServlet#HttpServlet()
*/
public MainServlet() {
super();
// TODO Auto-generated constructor stub
}
@Override
public void init() throws ServletException {
// TODO Auto-generated method stub
// super.init();
// 实例化管理者 仅创建一次
manager = new ConfigurationManager();
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
* response)
*/
@SuppressWarnings("deprecation")
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// TODO Auto-generated method stub
try {
// 1) 获得配置文件管理者提供的配置文件信息
Map<String, ActionMapping> actionMappings = manager.getActionMappings();
// 2) 获得请求uri并截取
String uri = request.getRequestURI();
String path = uri.substring(uri.lastIndexOf('/') + 1, uri.lastIndexOf('.'));
// 3) 获取访问的ActionMapping对象
if (!actionMappings.containsKey(path)) {
throw new RuntimeException("未找到" + path + "Action");
}
ActionMapping actionMapping = actionMappings.get(path);
// 4) 实例化具体的Action类
Class<?> classObj = Class.forName(actionMapping.getClassName());
Object actionObj = classObj.newInstance();
// 5) 利用反射调用actionObj的方法并获得结果视图名称
String methodName = actionMapping.getMethod();
Method method = classObj.getDeclaredMethod(methodName, HttpServletRequest.class, HttpServletResponse.class);
String resultStr = (String) method.invoke(actionObj, request, response);
// 6) 处理结果视图
Map<String, Result> results = actionMapping.getResults();
if (!results.containsKey(resultStr)) {
throw new RuntimeException("未找到" + resultStr + "视图");
}
Result result = results.get(resultStr);
String type = result.getType();
String page = result.getPage();
if (type.equals("dispatcher")) {
request.getRequestDispatcher(page).forward(request, response);
} else {
response.sendRedirect(request.getContextPath() + page);
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
* response)
*/
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}
}
ok,现在这个我们自己搞得一个小的针对MVC模式的框架即可投入使用了,让我们测试一下吧!
代码测试
我这里是用假数据做了一个登陆做测试,目录结构如图