在doFilter() 方法中,将调用dispatcher.serviceAction ,该方法如果找到相应的Action ,将把用户请求交给ActionProxy 。serviceAction() 代码在Dispatcher.java 中,如代码3.2 所示。
public
class
Dispatcher
{ ... /** * 为mapping加载类,并调用相应的方法或者直接返回result * <p/> * 根据用户请求的参数,建立Action上下文 * 根据指定的Action’名称和包空间名称,加载一个Action代理 <tt>ActionProxy</tt> * 然后Action的相应方法将被执行, */ public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context, ActionMapping mapping) throws ServletException { Map < String, Object > extraContext = createContextMap(request, response, mapping, context); // 如果存在一个值栈,则建立一个新的并复制以备Action使用 ValueStack stack = (ValueStack) request.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY); if (stack != null ) { extraContext.put(ActionContext.VALUE_STACK, ValueStackFactory.getFactory().createValueStack(stack)); } String timerKey = " Handling request from Dispatcher " ; try { UtilTimerStack.push(timerKey); String namespace = mapping.getNamespace(); String name = mapping.getName(); String method = mapping.getMethod(); Configuration config = configurationManager.getConfiguration(); // FilterDispatcher把请求的处理交给ActionProxy ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory. class ).createActionProxy(namespace, name, extraContext, true , false ); proxy.setMethod(method); request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack()); // ActionMapping 直接返回一个result if (mapping.getResult() != null ) { Result result = mapping.getResult(); result.execute(proxy.getInvocation()); } else { proxy.execute(); } if (stack != null ) { request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack); } } catch (ConfigurationException e) { LOG.error( " Could not find action or result " , e); sendError(request, response, context, HttpServletResponse.SC_NOT_FOUND, e); } catch (Exception e) { throw new ServletException(e); } finally { UtilTimerStack.pop(timerKey); } } … }
从上面代码中可以看出来,Struts 2 用于处理用户请求的Action 实例,并不是用户实现的业务控制器,而是Action 代理。关于Action 代理相关内容,读者可以参考拦截器章节的介绍。
★ 提示 ★
前面一直在说Action 可以是一个普通的Java 类,与Servlet API 完全分离,但是为了实现业务逻辑,Action 需要使用HttpServletRequest 内容。
Struts 2 设计的精巧之处就是使用了Action 代理,Action 代理可以根据系统的配置,加载一系列的拦截器,由拦截器将HttpServletRequest 参数解析出来,传入Action 。同样,Action 处理的结果也是通过拦截器传入HttpServletResponse ,然后由HttpServletRequest 传给用户。
其实,该处理过程是典型的AOP (面向切面编程)的方式,读者可以在后面详细了解到。Struts 2 处理过程模型如图3.2 所示。
图3.2 Struts 2 处理过程模型
★ 说明 ★
拦截器是Struts 2 框架的核心,通过拦截器,实现了AOP (面向切面编程)。使用拦截器,可以简化Web 开发中的某些应用,例如,权限拦截器可以简化Web 应用中的权限检查。
3.1.2 业务控制器Action
业务控制器Action 是由开发者自己编写实现的,Action 类可以是一个简单的Java 类,与Servlet API 完全分离。Action 一般都有一个execute() 方法,也可以定义其他业务控制方法,详细内容将在后面介绍。
Action 的execute() 返回一个String 类型值,这与Struts 1 返回的ActionForward 相比,简单易懂。Struts 2 提供了一个ActionSupport 工具类,该类实现了Action 接口和validate() 方法,一般开发者编写Action 可以直接继承ActionSupport 类。编写Action 类后,开发者还必须在配置文件中配置Action 。一个Action 的配置应该包含下面几个元素:
该Action 的name ,即用户请求所指向的URL 。
Action 所对应的class 元素,对应Action 类的位置。
指定result 逻辑名称和实际资源的定位。
Action 是业务控制器,笔者建议在编写Action 的时候,尽量避免将业务逻辑放到其中,尽量减少Action 与业务逻辑模块或者组件的耦合程度。
3.1.3 业务模型组件
业务模型组件可以是实现业务逻辑的模块,可以是EJB 、POJO 或者JavaBean ,在实际开发中,对业务模型组件的区分和定义也是比较模糊的,实际上也超出了Struts 2 框架的范围。不同的开发者或者团队,都有自己的方式来实现业务逻辑模块,Struts 2 框架的目的就是使用Action 来调用业务逻辑模块。例如一个银行存款的业务逻辑模块,如代码3.3 所示。
package
ch3;
public
class
Bank
{ // 定义银行账户 private String accounts; // 定义操作金额 private double money; // 属性的getter和setter方法 public String getAccounts() { return accounts; } public void setAccounts(String accounts) { this .accounts = accounts; } public double getMoney() { return money; } public void setMoney( double money) { this .money = money; } // 模拟银行存款方法 public boolean saving(String accounts, double money) { // 调用DAO等模块读写数据库 return dosomeing(); } }
上面实例在实际开发中没有任何意义,这里只是作为业务逻辑模块来说明,在执行saving(String accounts,double money) 方法时,可以调用相应的数据库访问其他组件,来实现存款操作。使用Action 调用该业务逻辑组件可以在execute() 方法中实现,如代码3.4 所示。