手动编写struts2框架核心内容
首先我们要做的第一件事就是替换servlet实现能够使用普通Java类实现servlet的功能,而替换servlet我们首先要考虑到的是servlet的职能
那么servlet能做些什么?
在之前的博客中我提到了Servlet 是运行在 Web 服务器中的小型 Java 程序。Servlet 通常通过 HTTP(超文本传输协议)接收和响应来自 Web 客户端的请求。
所以这里暂且将servlet的职能归纳为
- 获取服务器整理后的请求信息
- 处理请求信息
- 根据处理的结果进行响应
那么要实现这个几个功能的话,只有Filter能够胜任了
/**
*普通JAVA类
*/
public class UserAction {
public String execute() {
System.out.println("执行");
return "success";
}
}
/**
*过滤器
*/
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
String uri = req.getRequestURI();
uri = uri.substring(uri.lastIndexOf("/") + 1);
if("user".equals(uri)){
UserAction user = new UserAction();
String result = user.execute();
if(result.equals("success")){
response.sendRedirect(request.getContextPath()+"/user.jsp");
}
}
}
这样就基本实现了servlet功能的替换,但是作为框架很多东西是不能写死的,大多要通过配置文件来灵活地执行功能
所以在这个基础上,我们结合反射技术便可以做到通过配置文件灵活地执行功能
配置文件
<root>
<action name="user" class="wltyx.nyybw" method="execute">
<result name="success" type=""></result>
</action>
</root>
创建封装类用于存储从配置文件中解析出来的数据
public class ActionConfig {
private String name;
private String className;
private String method;
private Map<String, ResultConfig> map = new HashMap<>();
public Map<String, ResultConfig> getMap() {
return map;
}
public void setMap(Map<String, ResultConfig> map) {
this.map = map;
}
public String getMethod() {
return method;
}
public void setMethod(String method) {
this.method = method;
}
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;
}
@Override
public String toString() {
return "ActionConfig [name=" + name + ", className=" + className + ", method=" + method + "]";
}
}
public class ResultConfig {
private String name;
private String type;
private String url;
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 getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
@Override
public String toString() {
return "ResultConfig [name=" + name + ", type=" + type + ", url=" + url + "]";
}
}
创建封装类用于存储请求和响应信息
public class ActionContext {
private HttpServletRequest req;
private HttpServletResponse resp;
private static ThreadLocal<ActionContext> context = new ThreadLocal<>();
public static ActionContext getContext() {
return context.get();
}
public static void setContext(ActionContext context) {
ActionContext.context.set(context);
}
public HttpServletRequest getReq() {
return req;
}
public void setReq(HttpServletRequest req) {
this.req = req;
}
public HttpServletResponse getResp() {
return resp;
}
public void setResp(HttpServletResponse resp) {
this.resp = resp;
}
}
解析xml配置文件
/*
* 解析xml文件中的配置
*/
@Override
public void init(FilterConfig config) throws ServletException {
try {
//获取解析文档对象
SAXReader reader = new SAXReader();
Document document = reader.read(Thread.currentThread().getContextClassLoader().getResourceAsStream("core.xml"));
//获取到action标签
@SuppressWarnings("unchecked")
List<Element> actions = document.selectNodes("//action");
//遍历(org.dom4j.Element)
for (Element action : actions) {
/**
* 将读取到的xml数据封装到ActionConfig中
*/
ActionConfig actionConfig = new ActionConfig();
//获取xml数据
String name = action.attributeValue("name");
String className = action.attributeValue("class");
String method = action.attributeValue("method");
//判断method如果没有定义给于默认值
if(method==null){
method="execute";
}
//将数据封装
actionConfig.setName(name);
actionConfig.setClassName(className);
actionConfig.setMethod(method);
/**
* 解析action下的result标签
*/
String xpath = "//action[@name="+name+"]/result";
//获取action下的所有result标签
@SuppressWarnings("unchecked")
List<Element> results = action.selectNodes(xpath);
//遍历获取到的当前的result对象
for (Element element : results) {
/**
* 将name封装到结果对象ResultConfig中
*/
ResultConfig resultConfig = new ResultConfig();
//封装result标签的name属性
String resultName = element.attributeValue("name");
resultConfig.setName(resultName);
//封装result标签的type属性(如果没有值给于默认值)
String type = element.attributeValue("type");
if(type==null){
type="forward";
}
resultConfig.setType(type);
//封装result标签的url属性(去空格)
resultConfig.setUrl(element.getTextTrim());
/**
* 将result结果对象ResultConfig封装到action结果对象中ActionConfig
*/
Map<String, ResultConfig> actionMap = actionConfig.getMap();
actionMap.put(resultName, resultConfig);
}
//将配置信息对象存储到map中(key是访问路径,value是配置对象)
map.put(name, actionConfig);
}
} catch (DocumentException e) {
e.printStackTrace();
}
}
使用反射调用方法
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
throws IOException, ServletException {
/**
* 转换成带协议的请求和响应
*/
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
/**
* 封装请求和响应
*/
ActionContext context = new ActionContext();
context.setReq(request);
context.setResp(response);
ActionContext.setContext(context);
//请求路径
String requestURI = request.getRequestURI();// /项目名/访问路径
//遍历包含配置信息的map
for (Entry<String, ActionConfig> set : map.entrySet()) {
//获取当前的路径可承接的子级完整路径
String url = request.getContextPath()+"/"+set.getKey();
//判断请求的是否是后台路径
if(requestURI.equals(url)){
try {
//获取字节码对象
ActionConfig config = set.getValue();
Class<?> clz = Class.forName(config.getClassName());
//创建对象
Object newInstance = clz.newInstance();
//获取方法对象
Method method = clz.getMethod(config.getMethod());
//调用方法并获取其返回值
Object result = method.invoke(newInstance);
/**
* 判断结果是否符合
*/
if(config.getMap().containsKey(result)){
/**
* 判断跳转方式
*/
//获取ResultConfig对象
ResultConfig resultConfig = config.getMap().get(result);
if("redirect".equals(resultConfig.getType())){
//重定向
response.sendRedirect(request.getContextPath()+resultConfig.getUrl());
}else{
//转发
request.getRequestDispatcher(request.getContextPath()+resultConfig.getUrl()).forward(request, response);
}
}
//结束
return;
} catch (Exception e) {
e.printStackTrace();
}
}
}
}