一、初识Struts2
Struts2是一个基于MVC设计模式的Web应用框架,它本质上相当于一个servlet,在MVC设计模式中,Struts2作为控制器(Controller)来建立模型与视图的数据交互。许多框架在大家一开始学习的时候都觉得比较繁琐和多此一举,但大体都有相同的目的,那就是增强可扩展性。Struts2的核心其实就是通过改配置文件的方式将请求和视图(结果)分开。
1.1 开发环境搭建
首先下载Struts2,地址http://struts.apache.org/,我这里下载的版本是2.5.10.1,解压之后有如下4个文件夹
所需的基本jar包有以下9个。struts2-core是开发的核心类库,struts2的UI标签的模板使用freemarker编写,OGNL是对象图导航语言,通过它来读写对象属性。
1.2 Struts2配置文件
①web.xml文件
主要完成对StrutsPrepareAndExecuteFilter的配置,它的实质是一个过滤器,负责初始化整个Struts框架并且处理所有的请求。在2.5以及2.1.3之前的版本filter-class会不同,请自行查询官方文档,filter-name和url-pattern是默认写法,不建议修改。
<filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
②struts.xml文件
Struts2的核心配置文件就是struts.xml文件,在这个配置文件里面我们可以根据需要再包括其它一些配置文件。在通常的应用开发中,我们每个人来写不同的模块,每个人单独配置一个struts.xml文件,最后合并,这样也利于管理和维护。
struts.xml中包含全局属性、用户请求和相应Action之间的对应关系、Action可能用到的参数和返回结果以及各种拦截器的配置,具体将在以下几节中慢慢介绍。
struts.xml可以从解压过后的示例程序里复制,拷贝到工程的src目录下,注释或删除掉struts标签中的内容,来填写我们需要的配置。
③struts.properties(default.properties)
default.properties文件在struts2-core.jar中的org.apache.struts2包下,里面保存着许多Struts是的默认属性,如编码格式、是否启用开发模式等等。当要修改某些属性时,建议在struts2的xml配置文档中进行更改,格式如下面一行代码,而不建议自己新建一个struts.properties文件。
<constant name="" value=""></constant>
④struts-default.xml
此文件是struts2框架默认加载的配置文件,它定义了struts2一些核心bean和拦截器,它会自动包含到struts.xml文件中(实质是通过<package extends="struts-default">),并为我们提供了一些标准的配置。我们可以在struts2-core.jar中找到这个文件。
二、struts.xml配置
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.5//EN" "http://struts.apache.org/dtds/struts-2.5.dtd"> <struts> <constant name="struts.ognl.allowStaticMethodAccess" value="true"/> <package name="user" namespace="/user" extends="struts-default"> <action name="user" class="com.dhcc.struts2.action.UserAction"> <result>/user_add_success.jsp</result> </action> </package> </struts>
例子
2.1 配置文件的优先级
在struts2中一些配置(比如常量)可以同时在struts-default.xml(只读性),strtus-plguin.xml(只读性),struts.xml,struts.properties和web.xml文件中配置,它们的优先级逐步升高,即是说后面的配置会覆盖掉前面相同的配置。
以struts.i18n.encoding=UTF-8的配置为例进行说明:
在struts.xml配置形式如下:
<constant name="struts.i18n.encoding" value="gbk"></constant>
在struts.properties的配置形式如下:struts.i18n.encoding=UTF-8
在web.xml中配置如下:
<filter> <filter-name>struts2</filter-name> <filter-class> org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter </filter-class> <init-param> <param-name>struts.i18n.encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter>
2.2 package配置
属性名 |
是否必须 |
说明 |
Name |
是 |
Package的唯一标识,不允许同名 |
Extends |
否 |
指定要继承的包 |
Namespace |
否 |
指定名称空间 |
Abstract |
否 |
声明包为抽象否 |
package元素的namespace属性及action的name属性,它们共同定义了action所映射到的实质文件。
namespace默认值“”,即不配置namespace属性,如果action不能进行完整路径匹配,则会来此namespace下进行匹配。namespace也可以配置成namespace="/"。它代表配置为项目的根。总结action的名称探索顺序:完全对应、逐步追溯到上级目录查找、"/"下查找、默认namespace下查找。
namespace引发的链接问题:当我们为action配置了namespace时,访问此action的形式总会是如下形式:.../webappname/xxx/yyy/ActionName.action,而当此action成功执行跳转到某个jsp页面时,如想在此jsp页面写链接,一定要写绝对路径,因为相对路径是相对.../webappname/xxx/yyy/,而如果以后我们修改了action的namespace时,相对路径又要变,所以链接不能写成相对路径。 可以在建立一个jsp文件时,加上如下内容:
<% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %>
我们写绝对路径可以参此内容。还可以参<head>下的<base href="<%=basePath%>"> 来完成绝对路径的书写。
三、Action
3.1 新建一个Action
第一步,新建一个Class,继承ActionSupport ,ActionSupport实现了execute()方法。
package com.struts2.test; import com.opensymphony.xwork2.ActionSupport; public class UserAction extends ActionSupport { public String add() { return "success"; } public String del() { return "success"; } public String update() { return "success"; } public String query() { return "success"; } }
第二步,配置此Action,在struts.xml中加入如下内容:
<package name="user" extends="struts-default" namespace="/user"> <action name="addUser" class="com.asm.UserAction" method="add"> <result name="success">/user/addUser.jsp</result> </action> <action name="delUser" class="com.asm.UserAction" method="del"> <result name="success">/user/delUser.jsp</result> </action> <action name="updateUser" class="com.asm.UserAction" method="update"> <result name="success">/user/updateUser.jsp</result> </action> <action name="queryUser" class="com.asm.UserAction" method="query"> <result name="success">/user/queryUser.jsp</result> </action> </package>
上面的method方法的值来源于CRUDAction中方法的名字,这样当我们访问上面的每一个Action时,它实质是和method指定的方法关联上。如果没有为action指定class,默认就是ActionSupport类,如果没有为action指定method属性,则默认执行execute方法,如果没有指定result的name属性,默认值为success。
第三步,编写相应的jsp页面,在此略去crud文件夹下的四个跳转jsp页面(addSuccess.jsp等),重点是crud.jsp页面。内容如下:
<html> <% String path=request.getContextPath(); %> <body> <a href="<%=path %>/user/addUser.action">添加数据</a><br> <a href="<%=path %>/user/delUser.action">删除数据</a><br> <a href="<%=path %>/user/queryUser.action">查询数据</a><br> <a href="<%=path %>/user/updateUser.action">修改数据</a><br> </body> </html>
最后发布测试。
3.2 动态调用DMI
不使用method实现统一,我们在struts.xml中增加如下内容:
<action name="op" class="com.struts2.test.UserAction"> <result name="add">/user/addUser.jsp</result> <result name="del">/user/delUser.jsp</result> <result name="query">/user/queryUser.jsp</result> <result name="update">/user/updateUser.jsp</result> </action>
然后再在crud.jsp中定义如下链接:
<a href="<%=path %>/user/op!add.action">添加数据</a><br> <a href="<%=path %>/user/op!del.action">删除数据</a><br> <a href="<%=path %>/user/op!query.action">查询数据</a><br> <a href="<%=path %>/user/op!update.action">修改数据</a><br>
注意查看上面的链接地址,它们都是针对op这个action,然后再加地上“!+UserAction中相应的方法名”,最后再写上.action即可以访问到相应result的name指定的jsp。大家会发现跟上面不同的是,result的name不再都是SUCCESS,这样才能区分开要访问的页面,但千万不要忘记在UserAction中相应的方法也要返回add/del/query/update,而不是SUCCESS。如果不想使用动态方法调用,我们可以通过常量来关闭,即在struts.xml中增加如下配置:
<constant name="struts.enable.DynamicMethodInvocation" value="false"/>
3.3 通配符
为了使用通配符,只需要改写配置文件即可。将3.1节中的配置文件改为如下内容可达到相同的效果:
<package name="user" extends="struts-default" namespace="/user"> <action name="*User" class="com.asm.UserAction" method="{1}"> <result name="success">/crud/{1}User.jsp</result> </package>
当有.../addUser.action请求时,如果不能在当前应用中找到完全相同的addUser名字的Action时,通配符配置这时就起作用了。
其实如果我们有良好的编程命名习惯,所有的Action我们都只需要进行一次配置。举例:规定所有的Action类都用XXXAction来命名,类中所有的CRUD方法都用add/del/update/query。Jsp页面也用add/del/update/query_XXX.jsp这样的形式。即配置文件可以写成如下形式:
<action name="*_*" class="com.struts2.test.{2}Action" method="{1}"> <result name="success">.../{1}_{2}.jsp</result> </action>
name中第一个*代表CRUD操作的名字,第二个*代表类的名字。所以访问链接地址举例如:.../del_User.action将访问到UserAction类的del方法,成功后跳到del_User.jsp页面。说明{0}是代表name中所有的*组合。
3.4 接收参数
①Action属性接收参数
UserAction中建两个属性name和age,并且要生成相应的get/set方法。
public class UserAction extends ActionSupport { private String name; private int age;public int getAge() { return age; } public String getName() { return name; } public void setAge(int age) { this.age = age; } public void setName(String name) { this.name = name; } }
在传参的jsp页面,有一个表单
<form action="<%=request.getContextPath()%>/addUser.action" method="get"> 名字:<input type="text" name="name"><br> 年龄:<input type="text" name="age"><br> <input type="submit" value="login"> </form>
这样name和age就能接收到传入的值。需要注意的是,传参参照的action中的方法名,而非属性名。
②DomainModel接收参数
UserAction中有一个域模型private User user,注意不要自己new对象,User类中有name和age属性和对应的get/set方法。UserAction中要生成User对象对应的get/set方法。
访问http://.../user/user!add?user.name=a&user.age=8 即可对user赋值,相当于调用了user的set方法。
public class UserAction extends ActionSupport { private User user;public User getUser() { return user; } public void setUser(User user) { this.user = user; } }
- 如果传入的参数个数和域模型的属性个数不同,可以用DTO(Data Transfer Object)。比如传入的参数还有一个isAdmin,那么我们建一个UserDTO,包含name,age和isAdmin三个属性,用UserDTO去接收参数,然后用UserDTO再生成相应的User
③ModelDriven接收参数
public class UserAction extends ActionSupport implements ModelDriven<User>{ //需要实现ModelDriven接口 private User user = new User(); //ModelDriven需要自己new @Override public User getModel() { return user; } }
3.5 访问web元素(request、session、application,HttpServletRequest、)
未完待续。。。