注:这是一个“重复造轮子”的过程,不对框架做任何讲解

在JavaEE中的MVC(三)中,我在Servlet中引入了命令模式的使用,采用Xml配置的方式,实现了一个Servlet调用多个不同的Action类,但是还不能实现页面地跳转,这一篇博客从之前的代码上,进一步地封装,实现Action跳转Jsp的功能,并且介绍一下如何将我们客户端的参数,传给我们的Action。

JavaEE中的MVC(三)定制Struts——命令模式

改进思路

我们要想办法解决这些问题:

  1. Action内部不应该只有excute()一个方法,还可以有add()、delete()、update()等等,而且在执行完这些方法之后,能进行页面跳转,比如:执行完add()的时候,跳转到add.jsp页面;
  2. 在Xml配置文件上,我们应该为我们的Action节点,添加一个或者多个子节点,这个子节点可以命名为result,用以表示Action期望跳转的Jsp页面;
  3. 有对应的Javabean,解析完Xml配置之后,需要存储数据,自然需要Javabean。因为一个Action可能有多个Result,因此还要有对应的数据结构。

整体的流程就如下所示:客户端发送请求,Servlet负责解析Url,根据Url调用指定的Action,执行Action中相应的操作,执行完这一部分操作之后,会有一个返回值,根据返回值再跳转Jsp页面:

配置文件Plus

重新设计我们的配置文件,在Action节点下添加Result节点,Result拥有3个属性:

  • name:当我们执行Action中的某一个方法的时候,会有一个返回值,返回值与name的值相对应;
  • page:所要跳转的目标页面;
  • redirect:跳转目标页面所用的方法是否是重定向。
<?xml version="1.0" encoding="UTF-8"?>
<mystruts>
<action name="HiAction" class="com.action.HiAction">
<result name="success" page="Hello.jsp" redirect="true" />
<result name="add" page="Hi.jsp" />
<result name="delete" page="Hi.jsp" />
</action>
<action name="HelloAction" class="com.action.HelloAction">
<result name="error" page="Hello.jsp" />
</action>
</mystruts>

Action类

这两个Action都继承自MyAction接口,其中HiAction中包含excute()以外的其它方法。

public interface MyAction {
String SUCCESS = "success";
String NONE = "none";
String ERROR = "error";
String INPUTE = "inpute";
String LOGIN = "login"; String excute() throws Exception;
} public class HelloAction implements MyAction{ @Override
public String excute() {
System.out.println("this is helloAction");
return ERROR;
}
} public class HiAction implements MyAction{ @Override
public String excute() {
System.out.println("this is hiAction:ERROR");
return ERROR;
} public String add() {
System.out.println("this is hiAction:add");
return "add";
} public String delete() {
System.out.println("this is hiAction:delete");
return "delete";
}
}

设计Javabean

ActionDifinition

我们原先的Javabean是Action,但是现在,我们的节点更加多样化,所以重新设计Javabean,取名叫ActionDifinition,ActionDifinition内部封装了Action和一个Result,因为Action和Jsp是一对多的关系,因此,将Result封装在Map中。

注:其中invoke(String methodName)和createAction()两个方法可以提取,单独创建工具类。

public class ActionDifinition {
private MyAction mAction;
private Map<String, Result> mResulteMap = new HashMap<String, Result>(); public void putResult(String key, Result value) {
mResulteMap.put(key, value);
} public Result getResult(String key){
return mResulteMap.get(key);
} public void setAction(MyAction action) {
this.mAction = action;
} /**
* 寻找并执行由Uri中指定的,Action中的方法,并且将返回值return
*/
public String invoke(String methodName) {
try {
if (methodName == null)
return mAction.excute(); MyAction action=createAction();
Method[] methods = action.getClass().getDeclaredMethods();
for (Method method : methods) {
if (methodName.equals(method.getName())) {
return (String) method.invoke(action);
}
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
} /**
* 生成Action的备份,这一点前面提到了,因为希望不同用户的相同业务使用不同的Action
*/
private MyAction createAction() {
try {
return mAction.getClass().newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
}

Result

这个Javabean封装了我们希望跳转的Jsp页面的相关信息,内容与Xml配置文件相对应。

public class Result {
private String name;
private String page;
private boolean redirect; public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public String getPage() {
return page;
} public void setPage(String page) {
this.page = page;
} public boolean isRedirect() {
return redirect;
} public void setRedirect(boolean redirect) {
this.redirect = redirect;
}
}

ActionFactory改进

既然Xml配置文件换掉了,解析用的类自然也要换,原先内部所封装的Action,现在要替换成ActionDifinition。这个类是解析Xml文件的核心类,内部包含一个Map,用于存储Xml文件解析的Javabean。

public class ActionFactory {
private static Map<String, ActionDifinition> actions = new HashMap<>();
private static ActionFactory factory; /**
* 在静态块儿中就开始解析配置文件
*/
static {
parseFile("/my_struts.xml");
System.out.println(actions.toString());
} private ActionFactory() {
//这个类中构造器反而是最后才执行的
System.out.println("======================");
System.out.println("信息:ActionFactory is OK");
} /**
* 单例模式,构造器私有化
*/
public static void init() {
if (factory == null) {
synchronized (ActionFactory.class) {
if (factory == null) {
factory = new ActionFactory();
}
}
}
} public static ActionDifinition getAction(String actionName) throws Exception {
return actions.get(actionName);
} /**
* 根据路径解析
*/
private static void parseFile(String path) {
try {
InputStream inputStream = ActionFactory.class.getResourceAsStream(path);
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse(inputStream); NodeList beanNodeList = document.getElementsByTagName("action");
for (int i = 0; i < beanNodeList.getLength(); i++) {
Node bean = beanNodeList.item(i);
parseActionNodes(bean);
}
} catch (Exception e) {
e.printStackTrace();
}
} /**
* 解析Action
*/
private static void parseActionNodes(Node node) {
try {
Element element = (Element) node;
String name = element.getAttribute("name");
String className = element.getAttribute("class");
MyAction action = (MyAction) Class.forName(className).newInstance(); //往Map中加入ActionDifinition
ActionDifinition actionDifinition=new ActionDifinition();
actionDifinition.setAction(action);
//解析Result节点
parseResultNodes(actionDifinition, node);
actions.put(name, actionDifinition);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
} /**
* 解析Result节点
*/
private static void parseResultNodes(ActionDifinition actionDifinition, Node node) {
NodeList resultNodeList = node.getChildNodes();
for (int j = 0; j < resultNodeList.getLength(); j++) {
Node propertyNode = resultNodeList.item(j);
if (propertyNode.getNodeType() == Node.ELEMENT_NODE && "result".equals(propertyNode.getNodeName())) {
Element resultElement = (Element) propertyNode; String name = resultElement.getAttribute("name");
String page = resultElement.getAttribute("page");
String redirectStr = resultElement.getAttribute("redirect");
boolean redirect = redirectStr != null && "true".equals(redirectStr); Result result = new Result();
result.setName(name);
result.setPage(page);
result.setRedirect(redirect); actionDifinition.putResult(name, result);
}
}
}
}

中央控制器(Servlet)

和原先差不多,因为代码都封装掉了,这里的逻辑依旧很简单,就和开始说得一样,解析客户端传来的Url,根据Url然后找到Action,执行Action的方法,根据方法执行的结果,判断要跳转到哪一个Jsp页面。

//urlPatterns拦截规则我替换成action了
@WebServlet(name = "PrepareAndExcuteServlet", urlPatterns = "/action/*")
public class PrepareAndExcuteServlet extends HttpServlet {
private static final long serialVersionUID = 1965314831568094829L; /**
* 初始化的时候,顺带地把ActionFactory初始化好
*/
@Override
public void init() throws ServletException {
super.init();
ActionFactory.init();
} protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
try {
// 对解析URI进行调整
String pathInfo = request.getPathInfo();
int index = pathInfo.indexOf('!');
String actionUri = pathInfo.substring(1, index == -1 ? pathInfo.length() : index);
String resultUri = index == -1 ? null : pathInfo.substring(index + 1); // 根据URI找到对应的ActionDifinition,ActionDifinition内部封装了Action和Result
ActionDifinition actionDifinition = ActionFactory.getAction(actionUri); // 执行Action中的方法,并且获取返回值
String resultStr = actionDifinition.invoke(resultUri); // 根据返回值,跳转指定的页面
Result result = actionDifinition.getResult(resultStr);
if (result.isRedirect()) {
response.sendRedirect("/" + result.getPage());
} else {
request.getRequestDispatcher("/" + result.getPage()).forward(request, response);
}
} catch (Exception e) {
e.printStackTrace();
}
} protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}

测试效果:

hi.jsp代码我就不发了,下面是测试的结果,客户端发送请求要在结尾加上:“action/HiAction!add”,action是过滤标志,HiAction是我们希望调用的那个Action,而add是Action中的方法,感叹号作为分隔符。

最后呢,蛮发一下我的工程目录吧。

Action接收参数

要想实现Action接收客户端传来的参数,其实通过之前提到的IOC即可,给我们的Action添加属性,然后写它的Set方法,然后注入值即可,需要注意的就是编码问题。

下面是遍历请求体的参数列表的方法

            Enumeration<String> map = request.getParameterNames();
while (map.hasMoreElements()) {
String param = (String) map.nextElement();
//Set方式注入值
setProperty(action, param, request.getParameter(param));
}

关于Set参数注入值的代码如下,其实这一段代码已经发过很多次了。

    public void setProperty(Object instance, String name, String value) throws Exception {
Class<?> clazz = instance.getClass();
Method[] methods = clazz.getMethods();
for (Method method : methods) {
String methodName = method.getName();
if (("set" + name.substring(0, 1).toUpperCase() + name.substring(1)).equals(methodName)) {
System.out.println("methodName" + methodName);
method.invoke(instance, value);
}
}
}

假如说我们的客户端会传来一个值,叫name,我们的Action想要接收这个值,我们的Action就要变成这样了:

public class HelloAction implements MyAction{
private String name; public void setName(String name) {
this.name = name;
} @Override
public String excute() {
System.out.println("this is helloAction");
return ERROR;
}
}

JavaEE中的MVC(五)定制Struts——Action跳转JSP的更多相关文章

  1. JavaEE中的MVC(三)定制Struts——命令模式

    注:本文并不讲解Struts框架,只研究这种思想的使用 JavaEE中的MVC(五)定制Struts--Action跳转Jsp 在讲这个之前,先给你们看一下我服务器1.0版本的截图,是不是很可笑,看起 ...

  2. JavaEE中的MVC(一)Dao层彻底封装

    最近Android工作实在难找,考虑是不是该转行做Java了,今天开始,花几天的事件,研究一下JavaEE各层优化. 首先是Dao层 增删改方法封装 使用PreparedStatement执行一条Sq ...

  3. struts配置文件中如何从一个package的action跳到另一个package中的某个action

    <package name="pack1" namespace="/test1" extends="struts-default"&g ...

  4. JavaEE中的MVC(四)AOP代理

    咱们来吹牛,JDK的动态代理在AOP(Aspect Oriented Programming,面向切面编程)中被称为AOP代理,而AOP是Spring框架中的重要组成部分. 代理模式 但是什么是代理模 ...

  5. JavaEE中的MVC(二)Xml配置实现IOC控制反转

    毕竟我的经验有限,这篇文章要是有什么谬误,欢迎留言并指出,我们可以一起讨论讨论. 我要讲的是IOC控制反转,然后我要拿它做一件什么事?两个字:"解耦",形象点就是:表明当前类中需要 ...

  6. windows server 证书的颁发与IIS证书的使用 Dapper入门使用,代替你的DbSQLhelper Asp.Net MVC中Action跳转(转载)

    windows server 证书的颁发与IIS证书的使用   最近工作业务要是用服务器证书验证,在这里记录下一. 1.添加服务器角色 [证书服务] 2.一路下一步直到证书服务安装完成; 3.选择圈选 ...

  7. C# MVC 用户登录状态判断 【C#】list 去重(转载) js 日期格式转换(转载) C#日期转换(转载) Nullable<System.DateTime>日期格式转换 (转载) Asp.Net MVC中Action跳转(转载)

    C# MVC 用户登录状态判断   来源:https://www.cnblogs.com/cherryzhou/p/4978342.html 在Filters文件夹下添加一个类Authenticati ...

  8. struts 2中为什么抽象包不能包含action?

    struts 2中为什么抽象包不能包含action?麻烦写详细点!

  9. 第五课 Struts的控制器【续】Action类的execute()方法

    1.Action类的execute()方法: public ActionForward execute(ActionMapping mapping,                           ...

随机推荐

  1. 张高兴的 Windows 10 IoT 开发笔记:BMP180 气压传感器

    注意:海拔高度仅供参考 GitHub : https://github.com/ZhangGaoxing/windows-iot-demo/tree/master/BMP180Demo

  2. 461. Hamming Distance(leetcode)

    The Hamming distance between two integers is the number of positions at which the corresponding bits ...

  3. 树莓派.安装Samba环境

    适用于树莓派3 树莓派装好系统后, 为了方便传文件到树莓派, 建议使用Samba这类文件夹级别的应用, 比ftp方便多了 如果你想把树莓派变成Nas, Samba也是不可或缺的应用 通过samba服务 ...

  4. PHP操作Memcached

    一.PHP连接Memcached: 一个简单的使用示例: $memcache = new Memcache; $memcache->connect("127.0.0.1",1 ...

  5. LeetCode 280. Wiggle Sort (摆动排序)$

    Given an unsorted array nums, reorder it in-place such that nums[0] <= nums[1] >= nums[2] < ...

  6. 04-从零玩转JavaWeb-JVM内存详情分析

    JVM内存划分栈与栈帧 JVM将内存主要划分为: 方法区 虚拟机栈 本地方法栈 堆 程序计数器 一.方法区:存放字节码,常量 ,静态变量,是一个共享的区域 二.虚拟机栈:执行方法其实就是栈帧入栈,出栈 ...

  7. Java中的的画正三角方法

    在循环的语句的练习中,画正三角是一个很经典的例子,但是如果方法找的不对的话,即使最终画出来了,那么得到的代码也是非常的复杂,应用性不高. 下面有两种方法来画正三角,第一种是一种比较麻烦的办法,是通过归 ...

  8. poj 3321Apple Tree

    Apple Tree Time Limit:2000MS     Memory Limit:65536KB     64bit IO Format:%I64d & %I64u Submit S ...

  9. kettle闪退问题

    确定Java的配置环境没问题 kettle闪退的时候把spoon.bat里面的配置项改一下 if "%PENTAHO_DI_JAVA_OPTIONS%"=="" ...

  10. 【Aladdin Unity3D Shader编程】之三 光照模型(二)

    高光反射模型 Specular=直射光*pow(cosθ,高光的参数) θ:是反射光和视野方向的夹角 编写高光反射Shader Shader "AladdinShader/07 Specul ...