上篇已经基本介绍了关于MVC的一些知识,现在我们来讲解Struts估计很容易就会理解了。
1. 理论讲解
Struts1的程序流程图:
流程详解:
Struts1框架是以ActionServlet作为核心控制器,整个的应用由客户端请求驱动,当我们的客户端向web发送请求的时候,请求将被Struts1的核心控制器ActionServlet拦截,ActionServlet根据请求决定是否调用业务逻辑控制器来处理用户的请求,当结果处理完毕后,处理结果将通过JSP呈现给用户。
整个框架的核心就是控制器,控制器包括两个部分,一个是核心控制器,另一个就是业务逻辑控制器。ActionServlet是核心控制器,而我们定义的action就是我们说的业务逻辑控制器,下面的例子会进行一一的讲解。
核心控制器的职责是将用户相应的请求转发给相应的业务逻辑控制器进行处理。
那么MVC是如何在Struts1中进行体现的呢?
Struts1的MVC体现
Model部分:Struts1的Model部分主要由底层业务逻辑处理来担当,封装了业务逻辑以及数据访问层实现,Model部分的组成对于大型的项目来说,不仅仅是一个简单的javaBean就可以完成,比如在ITOO这个项目中,我们业务逻辑层用到是多个EJB组件,并且存在远程调用其他的服务,也就是说整个的model提供了一套完整的业务逻辑处理。
View视图部分的显示是通过渲染实现的。View采用的是jsp来实现的,利用Struts1丰富的标签库或者是el表达式等等,从而减少脚本的出现,方便的进行处理结果的显示。
Controller部分:包含上面我们讲解到的核心控制器ActionServlet以及业务逻辑控制器Action。
2. 实战应用
俗话说的好“光说不练假把式,光练不说傻把式,又练又说真把式”,讲解了那么多的理论,下面我们来实战一下吧。
对应上面的理论讲解,我们来进行项目的实战。以登录为例。
拷贝相应的配置文件和jar包
新建一个web项目,Structs只能是web项目,因为其是对servlet做了一个包装。
拷贝相应的jar以及Struts1的核心配置文件struts-config.xml,这里进行了相关的相应的业务逻辑控制器的相关的配置。
struts-config.xml:
<span style="font-family:Microsoft YaHei;font-size:14px;"><strong><?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE struts-config PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 1.2//EN"
"http://jakarta.apache.org/struts/dtds/struts-config_1_2.dtd">
<struts-config>
<form-beans>
<form-bean name="loginForm" type="com.bjpowernode.struts.LoginActionForm"/>
</form-beans>
<action-mappings>
<action path="/login"
type="com.bjpowernode.struts.LoginAction"
name="loginForm"
scope="request"
>
<forward name="success" path="/login_success.jsp" />
<forward name="error" path="/login_error.jsp"/>
</action>
</action-mappings>
</struts-config></strong></span>
这里面的东西一会我们一一解释。
我们先来建立一个ActionForm类,命名为LoginActionForm:
<span style="font-family:Microsoft YaHei;font-size:14px;"><strong>package com.bjpowernode.struts;
import org.apache.struts.action.ActionForm;
/**
* 登录ActionForm,负责表单收集数据
* 表单的属性必须和ActionForm中的get和set的属性一致
* @author Administrator
*
*/
@SuppressWarnings("serial")
public class LoginActionForm extends ActionForm {
private String username;
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}</strong></span>
ActionForm类的主要作用就是负责收集表单上的数据,建立的属性字段与表单上的是统一的,用专门的类来收集表单上数据故而很方便,但是我们可以看到这个类并不是一个pojo对象,而是一个具有侵入性也就是存在了依赖,体现在上面的就是继承了某一个类。
建立一个业务逻辑控制器Action实例:
<span style="font-family:Microsoft YaHei;font-size:14px;"><strong>package com.bjpowernode.struts;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
/**
* 登录Action
* 负责取得表单数据、调用业务逻辑、返回转向信息
*
* @author Administrator
*
*/
public class LoginAction extends Action {
@Override
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response)
throws Exception {
LoginActionForm laf = (LoginActionForm)form;
String username = laf.getUsername();
String password = laf.getPassword();
UserManager userManager = new UserManager();
try {
userManager.login(username, password);
//request.setAttribute("username", username);
return mapping.findForward("success");
}catch(UserNotFoundException e) {
e.printStackTrace();
request.setAttribute("msg", "用户不能找到,用户名称=【" + username + "】");
}catch(PasswordErrorException e) {
e.printStackTrace();
request.setAttribute("msg", "密码错误");
}
return mapping.findForward("error");
}
}</strong></span>
这个是一个业务逻辑处理的控制器,不仅仅可以获取到表单上的数据,细心的同学会发现调用了JavaBean来处理页面的请求,没有看错,就是UserManager userManager = new UserManager();但是我们也是可以看到的Action实例不仅仅是继承了Action,并且其方法excute方法中的参数也是依赖于Struts api以及Servlet api的,这样的依赖注定其具有不灵活性或者良好的维护性,怎么办?之后我们会讲解。
javaBean(UserManager)
这是控制器调用业务逻辑层的处理逻辑的写法,那么我们的javaBean(UserManager)是如何书写的呢?
<span style="font-family:Microsoft YaHei;font-size:14px;"><strong>package com.bjpowernode.struts;
public class UserManager {
public void login(String username, String password) {
if (!"admin".equals(username)) {
throw new UserNotFoundException();
}
if (!"admin".equals(password)) {
throw new PasswordErrorException();
}
}
}</strong></span>
这里我们并没有和数据库进行交互,自己手动书写的用户名和密码,并且进行了相关的异常处理,这些都是属于运行时的异常,具体异常的代码不在这里进行展示了。这里展示对于前台的进行如何去进行相关的处理并且将结果进行返回。
页面的相关处理
处理结果返回成功后页面:
在Action中我们看到:return mapping.findForward("success");这其实是将处理的结果返回给jsp页面,经过视图的处理和渲染最终输送到浏览器客户端进行显示,呈现给用户。
<span style="font-family:Microsoft YaHei;font-size:14px;"><strong><%@ page language="java" contentType="text/html; charset=GB18030"
pageEncoding="GB18030"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=GB18030">
<title>Insert title here</title>
</head>
<body>
${loginForm.username },登录成功
</body>
</html></strong></span>
那么我们是如何向Action发送请求的呢?
登录页面了 login.jsp
<span style="font-family:Microsoft YaHei;font-size:14px;"><strong><%@ page language="java" contentType="text/html; charset=GB18030"
pageEncoding="GB18030"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=GB18030">
<title>Insert title here</title>
</head>
<body>
<form action="login.do" method="post">
用户:<input type="text" name="username"><br>
密码:<input type="password" name="password"></br>
<input type="submit" value="登录">
</form>
</body>
</html></strong></span>
Web.xml配置
还有一个最重要就是我们的web配置文件,这是全局最重要的配置文件,因为默认这是程序运行时自动加载的项,如果Struts-config.xml文件没有在此处进行配置是无法进行加载的
<span style="font-family:Microsoft YaHei;font-size:14px;"><strong><?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/struts-config.xml</param-value>
</init-param>
<init-param>
<param-name>debug</param-name>
<param-value>2</param-value>
</init-param>
<init-param>
<param-name>detail</param-name>
<param-value>2</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>
<!-- Standard Action Servlet Mapping -->
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
</web-app></strong></span>
这里配置了一些在初始化的时候的参数
<span style="font-family:Microsoft YaHei;font-size:14px;"><strong><init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/struts-config.xml</param-value>
</init-param></strong></span>
就是我们的业务逻辑控制器的配置,<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>,这个就是我们的核心控制器的配置。
此时来解析Struts-config.xml的配置就容易了。
解析Struts-config.xml
<span style="font-family:Microsoft YaHei;font-size:14px;"><strong><struts-config>
<form-beans>
<form-bean name="loginForm" type="com.bjpowernode.struts.LoginActionForm"/>
</form-beans>
<action-mappings>
<action path="/login"
type="com.bjpowernode.struts.LoginAction"
name="loginForm"
scope="request"
>
<forward name="success" path="/login_success.jsp" />
<forward name="error" path="/login_error.jsp"/>
</action>
</action-mappings>
</struts-config></strong></span>
这里配置了我们的Actionform以及action实例,当我们请求一个Action的时候,通过核心控制器ActionServlet进行action的分发,直到找到相应的action实例进行处理,利用action-mappings进行相关的action查找,
其中path的名字与jsp请求的路径名称是一样的,这是form表单中的<form action="login.do" method="post">;<url-pattern>*.do</url-pattern>,.do是映射通配符。
Type:是action实例的具体路径;name就是我们获取表单数据的ActionForm类,scope是作用域的范围;而forward是对于处理结果相应的返回页面的显示和处理。至此是不是明白多了。
3. 小结
Struts1是MVC模式的一个体现,虽然很好地将表现层与业务逻辑进行了很好的分离,但是是还是存在很多的不足。
第一就是:支持的表现层的技术单一,此时的Struts1只是支持JSP表现层技术,而不支持其他的(Velocity,FreeMaker技术(此时这些技术还没有出现,与时间的出现晚也存在关系))。
但是不得不承认Struts1确实是存在很大的优势的,比如Action实例处理完用户的请求后,并没有直接转到特定的资源,而是返回的是一个ActionForward对象,其实实质上是一个逻辑视图名,这个名字与具体的视图资源的关系在我们Struts-config.xml中进行了定义,这样的好处的是可以根据action处理结果将相应的视图资源程序给用户,从而达到了视图名字与视图资源的解耦。
第二就是与ServletAPI 以及Struts API的严重耦合。我们知道Struts1是基于Model2发展起来的,因此是完全基于Servlet API的,因此在Action实例中,我们可以看到大量的Servlet API。
<span style="font-family:Microsoft YaHei;font-size:14px;"><strong>public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response)
throws Exception { }</strong></span>
我们可以看出execute方法中存在四个参数,HttpServletRequest request, HttpServletResponse response就是Servlet API,通常这两个参数由web容器负责进行初始化,故而严重依赖于web容器。
而我们说代码严重依赖于Struts1API,在于action实例必须继承Action基类,其中包含了大量的Struts1API,比如:ActionMapping,ActionForm以及ActionForward等等,从设计上说属于侵入性设计,这种侵入性的设计会使得一旦重构,代码复用率极低,故而使得代码复用变得不现实。
看到了Struts1 的不足,聪明的人类绝对不会止步于此,是不是有新的解决方法呢?请继续关注下面的博客~