关于STRUTS的技术文章我很早就想写一篇了,只是不知道要写些什么东西,主要是以前没怎么接触过这个框架,因此打算用熟了之后再来写,可能感受会更深一些;其实等我会了之后,依然不知道要写些什么东西,因为总感觉都很简单的样子,没什么重点可述;但基于之前在《Jsp学习——filter的使用》一文中谈到了STRUTS2的原理,在最后也说道:”struts2的配置文件中没有servlet类的配置,只有一个filter的配置,由此可以看出其用filter处理所有的消息,至于如何处理前台传来的各种消息,我会在下一篇文章中详细阐述“ 。所以打算不管如何,这篇文章还是要写的,曾经不知在哪看到过这样一句话:”Writing was invented by someone who was too lazy to memorize“,用这句话来阐释我写技术博客的原因再好不过了。废话不多说了,开始进入主题了。
我在写《Jsp学习——servlet详解》这篇文章时候,使用了MVC架构模式来阐述那个DEMO,而STRUTS2也是公认的经典MVC框架,其使用一个filter和action类来充当控制层(control),使用JSP来充当视图层(view),使用DAO+SERVICE来充当模型层(model),整个执行流程大概是:浏览器收到用户的请求ACTION,发送到服务器端,FILTER将接收到的ACTION与特定视图关联(关联的物理视图和逻辑视图的关系全在配置文件中设置),之后继续将ACTION转发到指定的ACTION-CLASS中去处理,ACTION-CLASS调用MODEL层来完成所有的业务逻辑处理,之后再根据FILTER指定的视图资源跳转到对应的物理视图。
对于STURTS2的配置,由于其整个框架只使用了一个FILTER来充当核心控制器,而没有使用任何SERVLET,所以只需配置一下FILTER就行了,其配置方法与配置FILTER没太大差别,只是FILTER类是STRUTS2指定的框架中实现了的类(org.apache.struts2.dispatcher.FilterDispatcher),而不能由我们自己乱定。如下所示,在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,当放在src目录下,如果放在同web.xml的同目录下是不会起作用的,运行时期会出很多难以预料的错误。关于其配置方式,有很多种,可以专门写一篇博文来讨论了,在这里,我暂且用我认为较简单而且易用的方式,其内容如下:
- <struts>
- <package name="com.struts2.projection.action" extends="struts-default">
- <action name="*" class="com.struts2.projection.action.Action" method="{1} ">
- <result name="success">success.jsp</result>
- <result name="error">error.jsp</result>
- </action>
- </package>
- </struts>
这种方式是通过指定通配符的方式,”*“意思是对于前台传来的所有.action消息,都将分配给com.struts2.projection.action.Action类来处理,这是个自定义类,至于具体再分配给哪个方法,由method = "{1}"来指示,其意思是指定前面action的name属性中第一个*号所代表的单词为该action对应的方法名,如:前台传来login.action,则对应的处理方法为login(),传来regist.action,则对应的处理方法为regist();
另外还有一种常用的方式,同样很受欢迎,即将action名改为以 类名+!+动作名 的方式来动态调用aciton类中的方法,其动作名就是指定的方法名。在这种方式下,struts.xml的配置为一般型的配置:
- <struts>
- <package name="com.struts2.projection.action" extends = "struts-default">
- <action name="login" class="com.struts2.projection.action.Action">
- <result name="error">/error.jsp</result>
- <result name="success">/success.jsp</result>
- </action>
- </package>
- </struts>
如此依然可以实现动态调用,但对ACTION类的要求是,其必须有一个默认的excute()方法。这种方法在前台为ajax框架或类似的框架如extjs中经常被很多人青睐。还有很多其它不常用的方式在此就不详细阐述。
以下是一个登陆框的完整示例:
view:index.jsp
- <%@ page language="java" import="java.util.*" pageEncoding="gb2312"%>
- <%@ taglib prefix="s" uri="/struts-tags" %>
- <%
- String path = request.getContextPath();
- String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
- %>
- <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
- <html>
- <head>
- <base href="<%=basePath%>">
- <title>My JSP 'index.jsp' starting page</title>
- <meta http-equiv="pragma" content="no-cache">
- <meta http-equiv="cache-control" content="no-cache">
- <meta http-equiv="expires" content="0">
- <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
- <meta http-equiv="description" content="This is my page">
- <!--
- <link rel="stylesheet" type="text/css" href="styles.css">
- -->
- </head>
- <body>
- <s:form action = "login" method="login">
- <s:textfield name="username" label="user name"></s:textfield>
- <s:textfield name="userpassword" label="user password"></s:textfield>
- <s:submit value="login"></s:submit>
- <s:submit value="regist" onclick="regist()"></s:submit>
- </s:form>
- <script type="text/javascript">
- function regist()
- {
- targetForm = document.forms[0];
- targetForm.action = "regist.action";
- }
- </script>
- </body>
- </html>
web.xml
- <?xml version="1.0" encoding="UTF-8"?>
- <web-app version="2.5"
- xmlns="http://java.sun.com/xml/ns/javaee"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
- http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
- <welcome-file-list>
- <welcome-file>index.jsp</welcome-file>
- </welcome-file-list>
- <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>
- </web-app>
struts.xml
- <?xml version="1.0" encoding="UTF-8"?>
- <!DOCTYPE struts PUBLIC
- "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
- "http://struts.apache.org/dtds/struts-2.0 dtd"
- >
- <struts>
- <package name="com.struts2.projection.action" extends="struts-default">
- <action name="*" class="com.struts2.projection.action.Action" method="{1} ">
- <result name="success">success.jsp</result>
- <result name="error">error.jsp</result>
- </action>
- </package>
- </struts>
control:Action.java
- package com.struts2.projection.action;
- import java.sql.*;
- import com.opensymphony.xwork2.ActionContext;
- import com.struts2.projection.dao.DbDao;
- public class Action {
- private String userpassword;
- private String username;
- public String getUserpassword() {
- return userpassword;
- }
- public void setUserpassword(String userpassword) {
- this.userpassword = userpassword;
- }
- public String getUsername() {
- return username;
- }
- public void setUsername(String username) {
- this.username = username;
- }
- public String regist() throws ClassNotFoundException, SQLException
- {
- String driver = "com.mysql.jdbc.Driver";
- String url = "jdbc:mysql://localhost:3306/test_db";
- String user = "root";
- String pass = "900622";
- DbDao dao = DbDao.instance(driver,url,user,pass);
- ActionContext actionContext = ActionContext.getContext();
- if(username.replace(" ", "").equals("") || userpassword.replace(" ", "").equals(""))
- {
- actionContext.put("tip", "客户端提示:user name or user password can not be blank!");
- return "error";
- }
- String insertSql = "insert into user_information(username,userpassword) values('"+ username +"','"+ userpassword +"')";
- System.out.println(username+";"+userpassword);
- dao.insert(insertSql);
- actionContext.put("tip", "服务器提示: regist success!");
- return "error";
- }
- public String login() throws ClassNotFoundException, SQLException
- {
- String driver = "com.mysql.jdbc.Driver";
- String url = "jdbc:mysql://localhost:3306/test_db";
- String user = "root";
- String pass = "900622";
- DbDao dao = DbDao.instance(driver,url,user,pass);
- ActionContext actionContext = ActionContext.getContext();
- String sql = "select userpassword from user_information where username = '" + username + "'";
- ResultSet resultSet = dao.query(sql);
- if(resultSet.next())
- {
- if(resultSet.getString("userpassword").equals(userpassword))
- {
- Integer counter=(Integer)actionContext.getApplication().get("counter");
- if(counter == null)
- {
- counter=1;
- }
- else
- {
- counter++;
- }
- actionContext.getApplication().put("counter", counter);
- actionContext.getSession().put("user", this.getUsername());
- return "success";
- }
- else
- {
- actionContext.put("tip", "服务器提示:password error!so login failed!");
- return "error";
- }
- }
- else
- {
- actionContext.put("tip", "服务器提示:user name not exist!");
- return "error";
- }
- }
- }
Model:DbDao.java
- package com.struts2.projection.dao;
- import java.sql.*;
- public class DbDao {
- private static DbDao dao;
- private Connection conn;
- private String driver;
- private String url;
- private String username;
- private String pass;
- private DbDao()
- {}
- private DbDao(String driver,String url,
- String username,String pass)
- {
- this.driver = driver;
- this.url = url;
- this.username = username;
- this.pass = pass;
- }
- public static DbDao getDao() {
- return dao;
- }
- public static void setDao(DbDao dao) {
- DbDao.dao = dao;
- }
- public String getDriver() {
- return driver;
- }
- public void setDriver(String driver) {
- this.driver = driver;
- }
- public String getUrl() {
- return url;
- }
- public void setUrl(String url) {
- this.url = url;
- }
- public String getUsername() {
- return username;
- }
- public void setUsername(String username) {
- this.username = username;
- }
- public String getPass() {
- return pass;
- }
- public void setPass(String pass) {
- this.pass = pass;
- }
- public void getConnection() throws ClassNotFoundException, SQLException
- {
- if(conn == null)
- {
- Class.forName(this.driver);
- conn = DriverManager.getConnection(url, username, pass);
- }
- }
- public static DbDao instance(String driver,String url,
- String username,String pass)
- {
- if(dao == null)
- {
- dao = new DbDao(driver,url,username,pass);
- }
- return dao;
- }
- public boolean insert(String sql) throws ClassNotFoundException, SQLException
- {
- getConnection();
- Statement statement = conn.createStatement();
- if(statement.executeUpdate(sql) != -1)
- {
- return false;
- }
- return true;
- }
- public ResultSet query(String sql) throws ClassNotFoundException, SQLException
- {
- getConnection();
- Statement statement = conn.createStatement();
- return statement.executeQuery(sql);
- }
- public void delete(String sql) throws ClassNotFoundException, SQLException
- {
- getConnection();
- Statement statement = conn.createStatement();
- statement.executeUpdate(sql);
- }
- public void update(String sql) throws ClassNotFoundException, SQLException
- {
- getConnection();
- Statement statement = conn.createStatement();
- statement.executeUpdate(sql);
- }
- }
最后,说一下Action.java类,对于Action.java的成员变量,我需要特意说明一下:Action类中必须要声明与前台交互的数据,并且数据的标示符与前台定义的要相同,另外要定义setter和getter函数,因为Action与前台打交道就是通过filter和拦截器来控制这几个值,当前台传来值时,拦截器就调用setter方法来设置对应的成员值。
Action.java可以继承默认的SupportAction类作为其父类,SupportAction中实现了一些一般情形的响应,继承之后,就可以使用其中方法或者重写其中方法来达到自己的需求。(在这里,我没有继承SupportActoin类)。
当然的,代码的分层结构还不够好,因为Action中依然有许多的逻辑处理,在标准的SSH框架中,DbDao上面还有一层Service类,来处理脱离数据库之外的逻辑,因为这里暂且只介绍了Struts2,所以分层并不是很标准。