一、MVC概述
MVC是三个单词的缩写,分别为: 模型(Model),视图(View) 和控制Controller), MVC模式的目的就是实现Web系统的职能分工。
- Model层:实现系统中的业务逻辑,通常可以用JavaBean或EJB来实现。
- View层:用于与用户的交互,通常用JSP来实现。
- Controller层:是Model与View之间沟通的桥梁,它可以分派用户的请求并选择恰当的视图以用于显示,同时它也可以解释用户的输入并将它们映射为模型层可执行的操作。
二、Struts2基础
2.1 Struts2框架结构图
2.2 工作流程
- 客户端发送请求到服务器,此时服务器接收到一个HttpServletRequest请求。
- HttpServletRequest请求经过一系列的过滤器,从上到下依次经过ActionContextCleanUp,,再其他过滤器(Othter Filters、SiteMesh等),最后到FilterDispatcher。FilterDispatcher是控制器的核心,就是MVC的Struts 2实现中控制层(Controller)的核心。
- FilterDispatcher调用ActionMapper来决定调用某个Action来处理这个(HttpServlet Request)请求。
- ActionMapper决定需要调用某个Action,FilterDispatcher则把请求的处理交给ActionProxy。
- ActionProxy通过Configuration Manager查看struts.xml,从而找到相应的Action类。
- ActionProxy创建一个ActionInvocation实例,ActionInvocation通过代理模式调用Action的execute方法(默认情况下)。但在调用之前,ActionInvocation会根据配置加载Action相关的所有Interceptor(拦截器)。
- Action执行完毕后,ActionInvocation负责根据struts.xml中的配置找到对应的返回结果result,然后将Result内容通过HttpServletResponse返回给服务器。
需要注意的是:
1、调用Action的过程前后,涉及到相关拦截器(Intercepter)的调用
2、Struts 2的核心控制器是FilterDispatcher,有3个重要的方法:destroy()、doFilter()和Init(),执行顺序是:init()—>doFilter() —>destroy()
2.3 Struts2:配置
2.3.1 Struts2:配置
<1> web.xml
Struts2框架需要在web.xml文件中配置一个前端控制器FilterDispatcher,用于对Struts2框架进行初始化并处理所有的请求。FilterDispatcher是一个Servlet过滤器,它是整个Web应用的配置项。
<2> struts.properties
Struts2提供了很多配置的属性,通过这些属性的设置,可以改变框架的行为,从而满足不同Web应用的需求。
Struts2在default.properties文件(位于struts2-core-*.jar\org\apache\struts2下)中给出了所有属性的列表,并对其中一些属性预设了默认值。如果我们创建了struts。properties文件,那么在该文件中的属性设置会覆盖default.properties文件中的属性设置。
在开发中,可能需要修改的几个属性:
- struts.i18n.reload=true:激活重新载入国际化文件的功能
- struts.devMode=true:激活开发模式,提供更全面的调试功能
- struts.configuration.xml.reload=true:激活重新载入XML配置文件的功能,当文件被修改后,就不需要载入Servlet容器中的整个Web应用了。
- struts2.url.http.port=8080:配置服务器运行的端口
- struts.objectFactory=spring:把Struts2的类的生成交给Spring完成(用于集成Struts2与Spring框架)
<3> struts.xml文件
struts.xml是Struts2框架的核心配置文件,主要配置和管理action。该文件通常放在src目录下,该目录下的struts.xml将被Struts2框架自动加载。
所有标签都包含在struts标签内,其中package元素类似于Java中的包提供了将action、result、result类型、拦截器和拦截器栈组织为一个逻辑单元的方式,从而简化了维护工作,提供了重用性。与Java不同的是,Struts2中的包可以扩展另外的包,从而“继承”原有包的所有定义,并可以添加自己包特有的配置,以及修改原有包的部分配置。
package元素中有一个必须的属性name—指定包的名字,这个名字将作为引用该包的键。需要注意的是,包的名字必须是唯一的。package元素的extends属性是可选的,允许一个包继承一个 或多个先前定义的包中的配置。package元素的abstract属性是可选的,将其设置为true,可以把一个包定义为抽象的,抽象包不能有action定义,只能作为“父”包,被其他的包继承。
action元素是Struts2的核心功能,使用Struts2框架,主要的编码工作就是编写action类。Action类通常都要实现com.opensymphony.xwork2.Action接口,并实现该接口中的execute()方法。当然,Struts2并不要求所编写的action类一定要实现Action接口,也可以使用一个普通的Java类作为一个action,只要改类提供一个返回类型为String的无参的public方法。
在实际开发中,action很少直接实现Action接口,通常都是从com.opensymphony.xwork2.ActionSupport类继承。ActionSupport实现了Action接口和其他一些可选的接口,提供了输入验证、错误信息存取以及国际化的支持,选择从ActionSupport继承,可以简化action的开发。
开发好action后,就需要配置action映射,以告诉Struts2框架,针对某个URL的请求应该交由哪一个action进行处理。action元素中包含了name与class属性用来对应一个action,默认情况下调用execute(),如果要执行action中的其它方法,可以使用method属性指定。
result元素包含在action元素中,一个result代表了一个可能的输出。当Action类的方法执行完成时,他返回一个字符串类型的结果代码,框架根据这个结果代码选择对应的result,向用户输出。
2.4 Action
Struts2控制器最重要的组成部分是Action,它是Web框架的控制中心,是链接后台业务处理的Javabean和前端JSP页面的桥梁和纽带。
2.4.1 Action的定义
Action就是一个普通的类,上面已经接触到了。Action的类名,习惯上以Action结尾,这样更易阅读和理解。在Action中如果希望配置默认方法则定一个名为execute()的方法,注意方法不能带任何参数,且必须返回字符串类型。
在struts.xml配置文件中,将刚才创建的Action注册到这里:
<action name="类名" class="类全路径" method="方法名"></action>
如果该方法返回值为null,表示不跳转到任何地方。如果返回一个字符串,则必须配置<action>的子标签<result>,通过该标签映射一个跳转路径,如:
<result name="返回字符串">/welcom.jsp</result>
2.4.2 通过Action获取请求参数
通过HttpServletRequest的getParameter()或getParameterValues()方法固然可以得到从客户端传送过来的请求参数,但是这样做有些麻烦,而且增加了耦合度,增强了对容器的依赖。Struts2在Action中改进了获取请求参数的方式,自动获取请求参数。在表单域的name属性值必须和Action中定义的属性名称一致,才能正确被Action接收
2.4.3 ActionSupport
在Struts2中,Action与容器已经做到完全解耦,不在继承某个类或实现某个接口,但是在特殊情况下,为了降低编程的难度,充分利用Struts2提供的功能,定义Action时会继承类ActionSupport,该类位于Xwork2提供的包com.opensymphony.xwork2中。
ActionSupport类为Action提供了一些默认实现,主要包括:
- 预定义常量
- 从资源文件中读取文本资源
- 接收验证错误信息
- 验证的默认实现
2.5 简单示例
名称:JSP+Struts2+JDBC实现登录
2.5.1 需要的jar包
(1) 传统Struts2的5个基本类库
- struts2-core-*.jar :Struts2框架类库
- xwork-core-*.jar :Xwork项目,Struts2就是在此基础上构建的
- ognl-*.jar :GONL表达式语言
- commons-logging-*.jar :用于能够插入任何其他的日志系统
- freemarker-*.jar :所有的UI标记末班
(2) 附加的4个库
- commons-io-*.jar
- commons-lang-*.jar
- javassist-*.jar
- commons-fileupload-*.jar
- 外加一个数据库驱动包
2.5.2 界面
注意:我只粘贴主要的代码。
login.jsp
<body>
<form action="login.action" method="post">
用户登录<br>
姓名:<input type="text" name="username"/><br>
密码:<input type="text" name="password"/><br>
<input type="submit" value="登录"/>
</form>
</body>
welcome.jsp
<%@ taglib prefix="s" uri="/struts-tags"%>
<html>
<head><title>成功页面</title></head>
<body>
<s:property value="username"/>,您好!欢迎光临叮当书店。
</body>
</html>
error.jsp:显示错误信息的界面
2.5.3 配置文件
web.xml
<filter>
<filter-name>struts2</filter-name>
<filter-class>
org.apache.struts2.dispatcher.FilterDispatcher
</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
struts.xml(配置Action)
<struts>
<package name="struts" extends="struts-default">
<action name="login" class="org.easybooks.bookstore.action.LoginAction">
<result name="success">/welcome.jsp</result>
<result name="error">/error.jsp</result>
</action>
</package>
</struts>
2.5.4 java源码
LoginAction.java
package org.easybooks.bookstore.action;
import java.sql.*;
import org.easybooks.bookstore.jdbc.MySQLConnBean;
import com.opensymphony.xwork2.ActionSupport;
public class LoginAction extends ActionSupport {
private static final long serialVersionUID = 1L;
private String username;
private String password;
// 处理用户请求的execute方法
public String execute() throws Exception {
//Struts2框架自动注入username与password,只要表单中的定义名与其相同
// String usr = getUsername();// 获取提交的姓名
// String pwd = getPassword();// 获取提交的密码
boolean validated = false;// 验证成功标识
MySQLConnBean MySqlBean = new MySQLConnBean();// 创建连接对象
// 查询user表中的记录
String sql = "select * from user";
MySqlBean.OpenConn();// 调用MySqlBean中加载JDBC驱动的方法
ResultSet rs = MySqlBean.executeQuery(sql);// 取得结果集
while (rs.next()) {
if ((rs.getString("username").compareTo(username) == 0)
&& (rs.getString("password").compareTo(password) == 0)) {
validated = true;// 标识为true表示验证成功通过
}
}
rs.close();
MySqlBean.closeStmt();
MySqlBean.closeConn();
if (validated) {
// 验证成功返回字符串"success"
return "success";
} else {
// 验证失败返回字符串"error"
return "error";
}
}
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;
}
}
MySQLConnBean.java
package org.easybooks.bookstore.jdbc;
import java.sql.*;
public class MySQLConnBean {
private Statement stmt = null;
private Connection conn = null;
ResultSet rs = null;
// 构造函数
public MySQLConnBean() {
}
public void OpenConn() throws Exception {
try {
Class.forName("com.mysql.jdbc.Driver").newInstance();
String url = "jdbc:mysql://localhost:3306/ceshi";
String user = "root";
String password = "root";
conn = DriverManager.getConnection(url, user, password);
} catch (SQLException e) {
System.err.println("Data.executeQuery: " + e.getMessage());
}
}
// 执行查询类的SQL语句,有返回集
public ResultSet executeQuery(String sql) {
rs = null;
try {
stmt = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
rs = stmt.executeQuery(sql);
} catch (SQLException e) {
System.err.println("Data.executeQuery: " + e.getMessage());
}
return rs;
}
// 关闭对象
public void closeStmt() {
try {
stmt.close();
} catch (SQLException e) {
System.err.println("Date.executeQuery: " + e.getMessage());
}
}
public void closeConn() {
try {
conn.close();
} catch (SQLException e) {
System.err.println("Data.executeQuery: " + e.getMessage());
}
}
}
注意:Struts2可以进行自动注入,LoginAction中的两个属性:username和password命名必须与在login.jsp中使用的文本输入框的命名严格匹配。在Struts2中,类变量总是在调用execute()方法之前被设置(通过setUsername()/setPassword()方法),这意味着在execute()方法中可以使用这些类变量,因为在execute()方法执行之前,他们已经被赋予了正确的值。
参考资料:
- 《JavaEE项目开发教程 第二版》
- struts2的工作流程(简单)