1、基本概念
1.1、掌握Struts 2的执行过程
使用Struts 2 开发程序的基本步骤
- 加载Struts 2 类库
- 配置web.xml文件
- 开发视图层页面
- 开发控制层Action
- 配置struts.xml文件
- 部署、运行项目
1.1.1、加载Struts2 类库
文件名 | 说 明 |
---|---|
struts2-core-xxx.jar Struts 2 | 框架的核心类库 |
xwork-core-xxx.jar | XWork类库Struts 2的构建基础 |
ognl-xxx.jar | Struts 2使用的一种表达式语言类库 |
freemarker-xxx.jar | Struts 2的标签模板使用类库 |
javassist-xxx.GA.jar | 对字节码进行处理 |
commons-fileupload-xxx.jar | 文件上传时需要使用 |
commons-io-xxx.jar | Java IO扩展 |
commons-lang-xxx.jar | 包含了一些数据类型的工具类 |
可以在idea工具里直接点出来
配置pom.xml文件(加载类库)
<dependencies>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-core</artifactId>
<version>2.5.22</version>
</dependency>
<dependency>
<groupId>ognl</groupId>
<artifactId>ognl</artifactId>
<version>3.2.26</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.30</version>
</dependency>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.27.0-GA</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.7</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.11</version>
</dependency>
</dependencies>
1.1.2、配置web.xml文件
将全部请求定位到指定的Struts 2过滤器中
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" id="WebApp_ID" version="4.0">
<display-name>struts2demo</display-name>
<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>
</web-app>
1.1.3、开发视图层页面
login.jsp登陆页面
<%@ taglib prefix="s" uri="/struts-tags" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>用户登录</title>
</head>
<body>
<h2>用户登录</h2>
<s:form method="post">
<p>
用户名:
<s:textfield name="user.username"/>
<s:fielderror fieldName="user.username"/>
</p>
<p>
密 码:
<s:password name="user.password"/>
<s:fielderror fieldName="user.password"/>
</p>
<p>
<s:submit value="登录"/>
</p>
<p style="color: red"><s:property value="errorMessage"/></p>
</s:form>
</body>
</html>
login_success.jsp登陆成功页面
<%@ taglib prefix="s" uri="/struts-tags" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>登录成功</title>
</head>
<body>
<h1>登录成功</h1>
<p>欢迎你:${sessionScope.LoginUser}</p>
<s:if test="#session.LoginUser==null">没有登录</s:if>
<s:else>已登录</s:else>
<hr>
<s:iterator value="userList">
<s:property value="username"/>
<s:property value="password"/>
<br>
</s:iterator>
</body>
</html>
1.1.4、开发控制层Action
package cn.st.struts2demo.action;
import cn.st.struts2demo.entity.User;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import java.util.ArrayList;
import java.util.List;
public class LoginAction extends ActionSupport {
private User user;
private String errorMessage;
private List<User> userList = new ArrayList<>();
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public String getErrorMessage() {
return errorMessage;
}
public void setErrorMessage(String errorMessage) {
this.errorMessage = errorMessage;
}
public List<User> getUserList() {
return userList;
}
public void setUserList(List<User> userList) {
this.userList = userList;
}
@Override
public void validate() {
if (user != null){
if ("".equals(this.user.getUsername())){
addFieldError("user.username","用户名不能为空");
}
if ("".equals(this.user.getPassword())){
addFieldError("user.password","密码不能为空");
}
}
}
//此处的名字不叫excute()方法,所以配置struts文件里要加上method属性
public String login(){
if (null == this.user){
return INPUT;
}
if (this.user.getUsername().equals("admin") && this.user.getPassword().equals("123")){
//把当前登陆的信息存到session里
ActionContext.getContext().getSession().put("LoginUser",this.user.getUsername());
return SUCCESS;
}
//返回error之前,提示错误消息
this.errorMessage="用户名或者密码错误";
return ERROR;
}
//希望登陆成功后重定向,地址栏不再显示login,还要去struts文件配置
public String loginSuccess(){
userList.add(new User("aaa","1234576"));
userList.add(new User("bbb","123412"));
return SUCCESS;
}
}
1.1.5、配置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>
<!--配置URL的扩展名,现在为没有后缀-->
<constant name="struts.action.extension" value=","/>
<!--表示为简单标签,不要自动生成table等熟悉-->
<constant name="struts.ui.theme" value="simple"/>
<!--解决中文乱码问题-->
<constant name="struts.i18n.encoding" value="UTF-8"/>
<!--name的名字随意,如xxx;struts-default这个包有很多拦截器;namespace是访问的URL的前缀-->
<package name="default" extends="struts-default" namespace="/">
<!--默认打开的的配置-->
<default-action-ref name="login"/>
<action name="hello" class="cn.st.struts2demo.action.HelloAction">
<!--这里就是action类返回的xxx,然后返回welcome页面-->
<result name="input">/welcome.jsp</result>
<result name="success">/welcome.jsp</result>
</action>
<!--method表示的是action类里方法名-->
<action name="login" class="cn.st.struts2demo.action.LoginAction" method="login">
<result name="input" >/login.jsp</result>
<!--跳转到loginSuccess,且改下类型,默认类型为dispacher(转发)redirect(后面要加url-->
<result name="success" type="redirectAction">loginSuccess</result>
<!--<result name="success" type="redirect">/loginSuccess</result>-->
<result name="error">/login.jsp</result>
</action>
<!--重定向-->
<action name="loginSuccess" class="cn.st.struts2demo.action.LoginAction" method="loginSuccess">
<result name="success">/login_success.jsp</result>
</action>
</package>
</struts>
type | result |
---|---|
dispacher | /login.jsp |
redirect | /loginSuccess |
redirectAction | loginSuccess |
运行部署结果
1.2、Struts 2访问Servlet API
登录成功,如何使用session保存用户信息?
解耦(推荐) | 从ActionContext中获取session,以key/value形式保存数据 |
---|---|
耦合 | 获取session,以setAttribute()的方式保存数据 |
1.2.1、与Servlet API解耦的访问方式
对Servlet API进行封装
- 提供了三个Map对象访问request、session、application作用域
通过ActionContext类获取这三个Map对象
- Object get(“request”)
- Map getSession()
- Map getApplication()
ActionContext.getContext().getSession().put("LoginUser", this.user.getUsername());
1.2.2、与Servlet API耦合的访问方式
通过ServletActionContext类获取Servlet API对象
- ServletContext getServletContext()
- HttpServletResponse getResponse()
- HttpServletRequest getRequest()
通过request.getSession()获取session对象
- 通过xxx.setAttribute()和xxx.getAttribute() 方法在不同的页面或Action中传递数据
ServletActionContext.getRequest().getSession().setAttribute("LoginUser", this.username);
需要在pom.xml配置文件中把jar包加进来
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
1.3、能够使用Struts 2实现数据校验
- 服务器端验证
- 客户端验证
- 表单验证功能
Struts 2提供了数据验证机制
- Action控制类继承ActionSupport类来完成Action开发
- ActionSupport类不仅对Action接口进行简单实现,同时增加了验证、本地化等支持
重写ActionSupport的validate()方法
@Override
public void validate() {
if(user != null) {
if ("".equals(this.user.getUsername())) {
addFieldError("user.username", "用户名不能为空");
}
if ("".equals(this.user.getPassword())) {
addFieldError("user.password", "密码不能为空");
}
}
}
表单提交过来,会先去验证validate方法,如果调用了addFieldError()这种方法,添加了错误的消失,就不会去调用action方法,而是直接去调用input方法
在jsp页面,s是struts定义的标签库
<%@ taglib prefix="s" uri="/struts-tags" %>
<s:fielderror fieldName="user.password"/>
1.4、掌握Struts 2框架的标签使用
1.4.1、常用表单标签
标 签 | 说 明 |
---|---|
<s:form>…</s:form> | 表单标签 |
<s:textfield>…</s: textfield> | 文本输入框 |
<s:password>…</s: password> | 密码输入框 |
<s:textarea>…</s: textarea> | 文本域输入框 |
<s:radio>…</s: radio> | 单选按钮 |
<s:checkbox>…</s: checkbox> | 多选框 |
<s:submit/> | 提交标签 |
<s:reset/> | 重置标签 |
<s:hidden/> | 隐藏域标签 |
1.4.2、常用非表单标签
标 签 | 说 明 |
---|---|
<s:actionerror/> | 显示Action错误 |
<s:actionmessage/> | 显示Action消息 |
<s:fielderror/> | 显示字段错误 |
1.4.3、常用通用标签
名称 | 标 签 | 说 明 |
---|---|---|
条件标签 | <s:if>……</s:if> | 根据表达式的值,判断将要执行的内容 |
条件标签 | <s:elseif>……</s:elseif> | 根据表达式的值,判断将要执行的内容 |
条件标签 | <s:else>……</s:else> | 根据表达式的值,判断将要执行的内容 |
迭代 | <s:iterator>……</s:iterator> | 用于遍历集合 |
2、Struts 2配置详解
2.1、掌握Struts 2的基本架构
Login.jsp页面——>核心控制器——>Action——>Result——>manager.jsp页面
2.1.1、核心控制器
需要在web.xml中进行配置
对框架进行初始化,以及处理所有的请求
<filter>
<filter-name>struts2</filter-name>
<filter-class>
org.apache.struts2.dispatcher.ng.filter.
StrutsPrepareAndExecuteFilter
</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
2.1.2、Action
创建Action
- 实现Action接口
- 继承ActionSupport类(建议)
配置Action
<struts>
<package name="default" namespace="/" extends="struts-default">
<!--name里面相当于一个URL-->
<action name="login" class="cn.houserent.action.LoginAction">
<result name="success">/page/manage.jsp</result>
<result name="input">/page/login.jsp</result>
<result name="error">/page/error.jsp</result>
</action>
</package>
</struts>
Action的作用
- 封装工作单元
- 数据转移的场所
- 返回结果字符串
示例
public class HelloWorldAction implements Action {
private String name = "";
private String message = "";
public String execute() {
this.setMessage("Hello,"+this.getName()+"!");
return SUCCESS;
}
//...省略setter/getter方法
}
2.1.2.1、method属性
- 实现Action中不同方法的调用
- 特点:避免动态方法调用的安全隐患、导致大量的Action配置
<action name="login"
class="cn.houserent.action.UserAction" method="login">
……
</action>
<action name="register"
class="cn.houserent.action.UserAction" method="register">
……
</action>
1、 动态方法调用(调用有安全隐患)
作用:减少Action数量
使用:actionName!methodName.action
禁用:将常量struts.enable.DynamicMethodInvocation设置为false
在struts.xml文件
<!--动态调用方法-->
<constant name="struts.enable.DynamicMethodInvocation" value="true"/>
但是这里只允许调用全局里允许的方法
在struts-dafault.xml(这里是系统文件不允许修改)
<global-allowed-methods>execute,input,back,cancel,browse,save,delete,list,index</global-allowed-methods>
所以可以在struts.xml文件的package里添加
<!--.在正则表达式表示一个任意值,*表示任意值-->
<global-allowed-methods>regex:.*</global-allowed-methods>
新建一个方法
- 可在配置文件了再添加一个action
- 配置动态绑定访问,直接用!方法名调用
<action name="hello" class="cn.bdqn.struts2demo.action.HelloAction">
<result name="input">/welcome.jsp</result>
<result name="success">/welcome.jsp</result>
<result name="error">/welcome_success.jsp</result>
</action>
调用hello里的方法haha
http://localhost:8080/struts2demo2_war_exploded/hello!haha
2、通配符(*)
另一种形式的动态方法调用,比动态方法安全,且
{1}都表示*
<action name="*Hello" class="cn.bdqn.struts2demo.action.HelloAction" method="{1}">
<result name="input">/welcome.jsp</result>
<result name="success">/{1}_success.jsp</result>
</action>
访问http://localhost:8080/struts2demo2_war_exploded/executeHello
注:两个*不能连在一起,可以*_*Hallo
这样命名
且与把动态方法的配置改为false无关<constant name="struts.enable.DynamicMethodInvocation" value="false"/>
2.1.3、Result
实现对结果的调用
result元素的值指定对应的实际资源位置
name属性表示result逻辑名
<struts>
<package name="default" namespace="/" extends="struts-default">
<action name="login" class="cn.houserent.action.LoginAction">
<result name="success">/page/manage.jsp</result>
<result name="input">/page/login.jsp</result>
<result name="error">/page/error.jsp</result>
</action>
</package>
</struts>
优点:减少了action的数量,还比较安全
置默认Action
没有Action匹配请求时,默认Action将被执行
通过<default-action-ref … />元素配置默认Action
1、配置default属性,2、配置defaultAction的action
<struts>
<default-action-ref name="defaultAction"/ >
<package name="default" extends="struts-default">
<action name="defaultAction">
<result>error.jsp</result>
</action>
</package>
</struts>
2.2、 struts.xml(命名固定)
核心配置文件,主要负责管理Action
通常放在WEB-INF/classes(项目编译之后才有class,可在target里找到)目录下,在该目录下的struts.xml文件可以被自动加载
可有若干个constant常量
<struts>
<constant name="" value=""/>
<package name="" namespace="" extends="">
<action name="" class="">
<result name=""></result>
</action>
</package>
</struts>
2.2.1、constant元素
配置常量,可以改变Struts 2框架的一些行为
name属性表示常量名称,value属性表示常量值
<constant name="struts.i18n.encoding" value="UTF-8"/>
产生table的标签,simple是不产生任何标签
<constant name="struts.ui.theme" value="simple"/>
2.2.2、package元素
- 包的作用:简化维护工作,提高重用性
- 包可以“继承”已定义的包,并可以添加自己包的配置(除非有重要原因,自定义的包应该总是扩展struts-default包)
- name属性为必需的且唯一,用于指定包的名称
- extends属性指定要扩展的包
- namespace属性定义该包中action的命名空间 ,可选
<struts>
<constant name="" value=""/>
<package name="default" namespace="/" extends="struts-default">
<action name="" class="">
<result name=""></result>
</action>
</package>
</struts>
struts-default.xml
Struts 2默认配置文件,会自动加载
struts-default包在struts-default.xml文件中定义
struts-plugin.xml
Struts 2插件使用的配置文件
加载顺序:struts-default.xml ——struts-plugin.xml ——struts.xml
常用结果类型
dispatcher类型 | 默认结果类型,后台使用RequestDispatcher转发请求 |
---|---|
redirect类型 | 后台使用的sendRedirect()将请求重定向至指定的URL |
redirectAction类型 | 主要用于重定向到Action |
<result name="success" type="redirectAction">loginSuccess</result>
动态结果
配置时不知道执行后的结果是哪一个,运行时才知道哪个结果作为视图显示给用户
this.result="loginSuccess";
<result name="success" type="redirectAction">${result}</result>
可以只配置一个result,但是可以跳转到不同界面
注意type的不同,地址赋值也不一样
全局结果
实现同一个包中多个action共享一个结果
<struts>
<default-action-ref name="defaultAction"/ >
<package name="default" extends="struts-default">
<global-results>
<result name="error">/page/error.jsp</result>
<result name="login" type="redirect">/page/login.jsp</result>
</global-results>
//注意package的配置
…省略action的配置…
</package>
</struts>
但是这个全局只在当前package里可用
掌握Struts 2中文乱码的解决?
3、OGNL
3.1、什么是OGNL
- Object Graph Navigation Language
- 开源项目,取代页面中Java脚本,简化数据访问
- 和EL同属于表达式语言,但功能更为强大
3.2、OGNL在Struts 2中做的两件事情
- 表达式语言:将表单或Struts 2标签与特定的Java数据绑定起来,用来将数据移入、移出框架
- 类型转换 :数据进入和流出框架,在页面数据的字符串类型和Java数据类型之间进行转换
- OGNL在框架中的作用:数据流入、数据流出
3.3、值栈和OGNL
值栈(ValueStack)
- 由Struts 2框架创建的存储区域,具有栈的特点
- Action的实例会被存放到值栈中
OGNL访问值栈
- 按照从上到下的顺序
- 靠近栈顶的同名属性会被读取
3.4、类型转换
- 在基于HTTP协议的Web应用中
- 客户端请求的所有内容都以文本编码方式传输到服务器端
- 服务器端的编程语言却有着丰富的数据类型
- Servlet中,类型转换工作由开发者自己完成
int age = Integer.parseInt(agestr);
3.4.1、内置类型转换器
Struts 2提供了多种内置类型转换器,自动对客户端传来的数据进行类型转换
内置类型转换器 | 说 明 |
---|---|
String | 将int、long、double、boolean、String类型的数组或者java.util.Date类型转换为字符串 |
boolean/Boolean | 在字符串和布尔值之间进行转换 |
char/Character | 在字符串和字符之间进行转换 |
int/Integer、float/Float、long/Long、double/Double | 在字符串和数值型数据之间进行转换 |
Date | 在字符串和日期类型之间进行转换。具体输入输出格式与当前的Locale相关 |
数组和集合 | 在字符串数组和数组对象、集合对象间进行转换 |
3.4.1.1、原始类型和包装类型
内置的日期数据转换
1、创建实体类
package cn.bdqn.struts2demo.entity;
import java.util.Date;
public class User {
private String username;
private String password;
private String age;
private Date birthday;
public User() {
}
public User(String username, String password) {
this.username = username;
this.password = password;
}
public Date getBirthday() { return birthday; }
public void setBirthday(Date birthday) { this.birthday = birthday; }
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
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;
}
}
2、register.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>用户注册</title>
</head>
<body>
<h2>用户注册</h2>
<s:form method="post">
<p>
用户名:
<s:textfield name="user.username"/>
<s:fielderror fieldName="user.username"/>
</p>
<p>
密 码:
<s:password name="user.password"/>
<s:fielderror fieldName="user.password"/>
</p>
<p>
年龄:
<s:textfield name="user.age"/>
<s:fielderror fieldName="user.age"/>
</p>
<p>
生日:
<s:textfield name="user.birthday"/>
<s:fielderror fieldName="user.age"/>
</p>
<p>
<s:submit value="注册"/>
</p>
<p style="color: red"><s:property value="errorMessage"/></p>
</s:form>
</body>
</html>
3、register_success.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>注册成功</title>
</head>
<body>
<p>用户名:<s:property value="user.username"/></p>
<p>密码:<s:property value="user.password"/></p>
<p>年龄:<s:property value="user.age"/></p>
<p>生日:<s:property value="user.birthday"/></p>
</body>
</html>
5、RegisterAction.java
package cn.bdqn.struts2demo.action;
import cn.bdqn.struts2demo.entity.User;
import com.opensymphony.xwork2.ActionSupport;
public class RegisterAction extends ActionSupport {
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public String register(){
if (null==user){
return INPUT;
}
return SUCCESS;
}
}
6、struts.xml配置action
<action name="register" class="cn.bdqn.struts2demo.action.RegisterAction" method="register">
<result name="input">/register.jsp</result>
<result name="success">/register_success.jsp</result>
</action>
3.4.1.2、自定义类型转换器
新建一个包converter,再新建一个类Dateconverter继承StrutsTypeConverter
package cn.bdqn.struts2demo.converter;
import org.apache.struts2.util.StrutsTypeConverter;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
public class Dateconverter extends StrutsTypeConverter {
@Override
public Object convertFromString(Map map, String[] strings, Class aClass) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date date = null;
try {
date = sdf.parse(strings[0]);
}catch (Exception e){
e.printStackTrace();
}
return date;
}
@Override
public String convertToString(Map map, Object o) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date date = (Date)o;
return sdf.format(date);
}
}
在资源文件夹新建xwork-conversion.properties
文件(应用于全局范围的类型转换器)
java.util.Date=cn.bdqn.struts2demo.converter.Dateconverter
部署运行:此处会发现生日里的年份的格式是2012,而不是默认的12
自定义多种类型转换
package cn.bdqn.struts2demo.converter;
import org.apache.struts2.util.StrutsTypeConverter;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
public class Dateconverter extends StrutsTypeConverter {
private SimpleDateFormat[] sdfs = {
new SimpleDateFormat("yyyy-MM-dd"),
new SimpleDateFormat("yyyy年MM月dd日"),
new SimpleDateFormat("yyyy/MM/dd"),
new SimpleDateFormat("yyyy.MM.dd")
};
@Override
public Object convertFromString(Map map, String[] strings, Class aClass) {
// SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date date = null;
for (SimpleDateFormat sdf:sdfs) {
try {
date = sdf.parse(strings[0]);
break;
} catch (Exception e) {
continue;
}
}
return date;
}
@Override
public String convertToString(Map map, Object o) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date date = (Date)o;
return sdf.format(date);
}
}
3.4.1.3、多值类型数据处理(数组和集合)
- 在registerAction.java类里添加数组
private String[] hobbies;
public String[] getHobbies() {
return hobbies;
}
public void setHobbies(String[] hobbies) {
this.hobbies = hobbies;
}
- 在registerAction.java类里添加集合
private List<String> hobbies;
public List<String> getHobbies() {
return hobbies;
}
public void setHobbies(List<String> hobbies) {
this.hobbies = hobbies;
}
在register.jsp里添加多选
<p>
爱好:
<s:checkbox name="hobbies" fieldValue="sport"/>运动
<s:checkbox name="hobbies" fieldValue="music"/>音乐
<s:checkbox name="hobbies" fieldValue="game"/>游戏
<s:checkbox name="hobbies" fieldValue="reading"/>读书
</p>
在register_success.jsp里添加迭代
<p>
<s:iterator value="hobbies">
<s:property />
</s:iterator>
</p>
3.4.1.4、处理类型转换错误
1、向用户输出类型转换错误的前提条件
- 启动StrutsConversionErrorInterceptor拦截器:拦截器已经包含在defaultStack拦截器栈中
- Action要继承ActionSupport类 :实质是实现ValidationAware接口
- 配置input结果映射:页面使用Struts 2表单标签或<s:fielderror>标签
- Struts 2表单标签内嵌了输出错误信息功能
- 普通HTML标签需使用<s:fielderror>标签输出转换错误
<p>
用户名:
<s:textfield name="user.username"/>
<s:fielderror fieldName="user.username"/>
</p>
2、修改所有类型的转换错误信息
思路:修改xwork.default.invalid.fieldvalue键的值
- 在struts.xml中指定资源文件的基名
<constant name="struts.custom.i18n.resources" value="message"/>
- 在src目录下创建资源文件message.properties并修改键值
xwork.default.invalid.fieldvalue=字段“{0}”的值无效
struts.xml中的value的值对应src下的资源文件的名字
3、定制特定字段的类型转换错误信息
思路:在Action范围的资源文件中添加I18N消息键invalid.fieldvalue.xxx
在Action包下创建RegisterAction.properties,指定键值invalid.fieldvalue.birthday=生日转换错误
3.5、OGNL表达式注意事项
访问Bean的属性
- Bean的类型必须遵循JavaBean规范:必须具有无参构造
- setter/getter方法符合JavaBean规范
public class Address { // 家庭地址
private String country; // 国家
private String city; // 城市
private String street; // 街道
... //省略各个属性的setter和getter方法
}
public class User { //用户类
private String name; //姓名
private int age; //年龄
private Address address; //家庭地址
... //省略各个属性的setter和getter方法
}
User对象作为Action的属性,其键名为user
- 访问name属性 user.name
- 访问country属性user.address.country
3.5.1、访问集合对象
- 可以使用属性名[index]的方式访问:列表、数组
- 可以使用属性名[key]的方式访问:Map对象
- 使用size或者length获取集合长度
访问列表、数组?
3.6、访问非值栈对象
<s:set var="list" value="{'aaa','bbb','scc'}" />
<%--这里不是action的属性,就是非值栈,所以要用#--%>
<p><s:property value="#list.size"/></p>
<s:iterator value="#list" >
<s:property/>,
</s:iterator>
查看ActionContext中的数据
使用<s:debug/>查看数据
ActionContext的组成
- 值栈-ValueStack
- 非值栈-Stack Context
在register_success.jsp页面加上
在struts.xml中添加,默认是false
<!--开发模式,可以调试-->
<constant name="struts.devMode" value="true"/>
可以在debug里面看到很多信息
4、拦截器
掌握Struts 2架构
HTTPServletRequest——ActionContextCleanUp(清空action的内容)——Other fiters(自己定义的用户编码过滤器、权限过滤器等等)——FilterDispatcher(前端控制器的过滤器)——ActionMapper(建了一个映射表,对应URL要用哪个action处理)——FilterDispatcher——ActionProxy(代理action,会读取struts.xml配置文件)——ConfigurationManager——ActionProxy——ActionInvocation———在执行action之前会经过——系列的过滤拦截器interceptor1到3——Action处理得到结果——result(字符串,根据字符串转发到哪个页面)——Template——321——HTTPServletResponse——客户端
来和去都要进过拦截器
掌握Struts 2拦截器
Struts 2核心接口和类
名称 | 作用 |
---|---|
ActionMapper | 根据请求的URI查找是否存在对应Action调用 |
ActionMapping | 保存调用Action的映射信息,如namespace、name等 |
ActionProxy | 在XWork和真正的Action之间充当代理 |
ActionInvocation | 表示Action的执行状态,保存拦截器、Action实例 |
Interceptor | 在请求处理之前或者之后执行的Struts 2组件 |
什么是拦截器
- truts 2大多数核心功能是通过拦截器实现的,每个拦截器完成某项功能
- 拦截器方法在Action执行之前和之后执行
- 拦截器栈
- 从结构上看,拦截器栈相当于多个拦截器的组合
- 在功能上看,拦截器栈也是拦截器
- 拦截器与过滤器原理很相似
- 三阶段执行周期:
1、做一些Action执行前的预处理
2、将控制交给后续拦截器或返回
结果字符串
3、做一些Action执行后的处理
Action提供附加功能时,无需修改Action代码,使用拦截器来提供
示例
继承父类AbstractInterceptor
关键代码String result = invocation.invoke();
实现接口 | 继承父类(推荐) |
---|---|
implements Interceptor | extends AbstractInterceptor |
import com.opensymphony.xwork2.interceptor.Interceptor; | import com.opensymphony.xwork2.interceptor.AbstractInterceptor; |
新建一个文件夹interceptor,新建一个类MyTimewInterceptor继承AbstractInterceptor
package cn.bdqn.struts2demo.interceptor;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
public class MyTimewInterceptor extends AbstractInterceptor {
@Override
public String intercept(ActionInvocation actionInvocation) throws Exception {
//预处理工作
long startTime = System.currentTimeMillis();
//执行后续拦截器或action
String result = actionInvocation.invoke();
//后续处理工作
long timeSpan = System.currentTimeMillis()-startTime;
System.out.println("user Time"+timeSpan+"ms");
//返回结果字符串
return result;
}
}
修改配置文件
<package name="default" extends="struts-default" namespace="/">
<interceptors>
<interceptor name="myTime" class="cn.bdqn.struts2demo.interceptor.MyTimewInterceptor"/>
</interceptors>
<default-action-ref name="login"/>
<global-results>
<result name="login">/login.jsp</result>
</global-results>
<global-allowed-methods>regex:.*</global-allowed-methods>
<action name="login" class="cn.bdqn.struts2demo.action.LoginAction" method="login">
<result name="input">/login.jsp</result>
<interceptor-ref name="myTime"/>
<result name="success" type="redirectAction">${result}</result>
<result name="error">/login.jsp</result>
</action>
</package>
看进拦截器和出拦截器的时间
配置多个拦截器
<?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>
<!--配置URL的扩展名,现在为没有后缀-->
<constant name="struts.action.extension" value=","/>
<!--表示为简单标签,不要自动生成table等熟悉-->
<constant name="struts.ui.theme" value="simple"/>
<!--解决中文乱码问题-->
<constant name="struts.i18n.encoding" value="UTF-8"/>
<!--全局错误变量-->
<!--<constant name="struts.custom.i18n.resources" value="message"/>-->
<!--name的名字随意,如xxx;struts-default这个包有很多拦截器;namespace是访问的URL的前缀-->
<constant name="struts.enable.DynamicMethodInvocation" value="true"/>
<!--开发模式,可以调试-->
<constant name="struts.devMode" value="true"/>
<package name="default" extends="struts-default" namespace="/">
<interceptors>
<interceptor name="myTime" class="cn.bdqn.struts2demo.interceptor.MyTimewInterceptor"/>
<!--定义拦截器栈-->
<interceptor-stack name="myStack">
<interceptor-ref name="myTime"/>
<interceptor-ref name="defaultStack"/>
</interceptor-stack>
</interceptors>
<!--引用设置的拦截器栈-->
<default-interceptor-ref name="myStack"/>
<default-action-ref name="login"/>
<global-results>
<result name="login">/login.jsp</result>
</global-results>
<global-allowed-methods>regex:.*</global-allowed-methods>
<!--<action name="hello" class="cn.bdqn.struts2demo.action.HelloAction">-->
<!--<result name="input">/welcome.jsp</result>-->
<!--<result name="success">/welcome.jsp</result>-->
<!--<result name="error">/welcome_success.jsp</r
esult>-->
<!--</action>-->
<action name="*Hello" class="cn.bdqn.struts2demo.action.HelloAction" method="{1}">
<result name="input">/welcome.jsp</result>
<result name="success">/{1}_success.jsp</result>
</action>
<action name="login" class="cn.bdqn.struts2demo.action.LoginAction" method="login">
<result name="input">/login.jsp</result>
<interceptor-ref name="myTime"/>
<result name="success" type="redirectAction">${result}</result>
<result name="error">/login.jsp</result>
</action>
<action name="loginSuccess" class="cn.bdqn.struts2demo.action.LoginAction" method="loginSuccess">
<result name="success">/login_success.jsp</result>
<!--引用默认的拦截器栈-->
<interceptor-ref name="defaultStack"/>
</action>
<action name="register" class="cn.bdqn.struts2demo.action.RegisterAction" method="register">
<result name="input">/register.jsp</result>
<result name="success">/register_success.jsp</result>
</action>
</package>
</struts>
struts2自带的拦截器
params拦截器 | 负责将请求参数设置为Action属性 |
servletConfig拦截器 | 将源于Servlet API的各种对象注入到Action |
fileUpload拦截器 | 对文件上传提供支持 |
exception拦截器 | 捕获异常,并且将异常映射到用户自定义的错误页面 |
validation拦截器 | 调用验证框架进行数据验证 |
workflow拦截器 | 调用Action类的validate(),执行数据验证 |
- struts-default.xml中定义一个defaultStack拦截器栈,并将其指定为默认拦截器
- 只要在定义包的过程中继承struts-default包,那么defaultStack将是默认的拦截器
自定义拦截器
实现Interceptor接口 | 继承AbstractInterceptor类 |
---|---|
void init():初始化拦截器所需资源 | 提供了init()和destroy()方法的空实现 |
void destroy():释放在init()中分配的资源 | —— |
String intercept(ActionInvocation ai) throws Exception | —— |
利用ActionInvocation参数获取Action状态 | 只需要实现intercept方法即可 |
返回结果码(result)字符串 | —— |
权限验证拦截器(判断用户是否登录)
1、编写拦截器,继承AbstractInterceptor类
ublic class AuthorizationInterceptor extends AbstractInterceptor {
public String intercept(ActionInvocation invocation) throws Exception{
//获取用户会话信息
Map session = invocation.getInvocationContext().getSession();
User user = (User)session.get("login");
if (user == null) {
//终止执行,返回登录页面
return Action.LOGIN;
} else {
//继续执行剩余的拦截器和Action
return invocation.invoke();
}
}
}
2、在struts.xml中配置拦截器
这个拦截器包含在默认拦截器内,所以Action中无需再引用权限拦截器
<package name="renthouse" extends="struts-default">
<interceptors>
<!--定义权限验证拦截器-->
<interceptor name="myAuthorization"
class="cn.houserent.interceptor.AuthorizationInterceptor">
</interceptor>
<!--定义拦截器栈-->
<interceptor-stack name="myStack">
<interceptor-ref name="defaultStack"/>
<interceptor-ref name="myAuthorization"/>
</interceptor-stack>
</interceptors>
<!-- 定义默认拦截器 -->
<default-interceptor-ref name="myStack"/>
...略...
</package>
3、在struts.xml中配置拦截器
掌握Struts 2框架的文件上传和下载
Commons-FileUpload组件 | Commons-FileUpload组件特点 |
---|---|
Commons是Apache开放源代码组织的一个Java子项目,其中的FileUpload是用来处理HTTP文件上传的子项目 | 使用简单:可以方便地嵌入到JSP文件中,编写少量代码即可完成文件的上传功能 |
—— | 能够全程控制上传内容 |
—— | 能够对上传文件的大小、类型进行控制 |
文件上传
jar包环境要求
- commons-fileupload-xxx.jar
- commons-io-xxx.jar
实现步骤
- 设置表单提交属性
- 编写文件上传处理Action
- 配置Action
文件下载
Spring中Bean的作用域
作用域 | 说 明 |
---|---|
singleton 默认值 | Spring以单例模式创建Bean的实例,即容器中该Bean的实例只有一个 |
prototype | 每次从容器中获取Bean时,都会创建一个新的实例 |
request | 用于Web应用环境,针对每次HTTP请求都会创建一个实例 |
session | 用于Web应用环境,同一个会话共享同一个实例,不同的会话使用不同的实例 |
global session | 仅在Portlet的Web应用中使用,同一个全局会话共享一个实例。对于非Portlet环境,等同于session |
1、使用Web环境下的作用域,要在web.xml文件中配置RequestContextListener或RequestContextFilter
!-- 指定Bean的作用域为prototype -->
<bean id="userAction" class="cn.houserent.action.UserAction" scope="prototype">
2、在action的类里添加注解
@Scope("prototype")
回调机制
Spring提供了回调机制
- 模板固化了不变的、流程化的内容,简化使用
- 回调允许我们在固化的流程中加入变化的内容
HibernateCallback接口
- public T execute(HibernateCallback action) throws DataAccessException
public List<Employee> find(final int page, final int size) {
return this.getHibernateTemplate().execute(
new HibernateCallback<List<Employee>>() {
public List<Employee> doInHibernate(Session session)
throws HibernateException, SQLException {
Query query = session.createQuery("from Employee");
query.setFirstResult((page - 1) * size);
query.setMaxResults(size);
return query.list();
}
}
);
}
当把方法中的局部变量传递给它的内部类使用时,必须把该变量声明为final
5、搭建ssh框架
掌握Spring与Hibernate的集成
掌握Spring与Struts 2的集成
方法一:定义独立的Hibernate配置文件,由Spring导入并创建会话工厂Bean
方法二:在Spring配置文件中进行集中配置
先配置数据源,再以此为基础配置会话工厂Bean
持久化类的映射文件可以逐个配置,也可以按目录导入
以上可以统一变为以下方法
AO类继承HibernateDaoSupport
使用getHibernateTemplate()方法获取HibernateTemplate实例完成持久化操作
创建baseDao.java
package cn.st.houserents.dao;
import org.hibernate.SessionFactory;
import org.springframework.orm.hibernate5.support.HibernateDaoSupport;
import javax.annotation.Resource;
//继承HibernateDaoSupport可以注入
public class BaseDao extends HibernateDaoSupport {
@Resource
public void setSessionFacotry(SessionFactory sessionFactory){// 这里单词ot换了位置
super.setSessionFactory(sessionFactory);
}
}
创建需要的dao和daoImpl
编写用户业务接口和实现类service和serviceImpl
Spring与Struts 2集成
Hibernate注解
Spring注解
添加struts2-spring-plugin-xxx.jar
按照名称匹配的原则定义业务Bean和Action中的setter方法
在struts.xml正常配置Action
- 采用此种配置方式时,Action的实例由Spring创建,Struts 2插件的工厂类只需根据Action Bean的id查找该组件使用即可
- 此种方式可以为Action进行更灵活的配置,但代价是在Spring配置文件中需要定义很多Action Bean,增加了配置工作量,如非必需并非首选
- 采用此种配置方式,同样需要添加struts2-spring-plugin-xxx.jar文件
struts2注解
实体类 | @Entity | 声明此类是实体类 |
@Table(name=“数据库表名”) | 声明此对象映射到数据库的数据表 | |
@Id | ||
@Column(name=“ID”) | 对应数据库表的列 | |