通过前面的学习,我们了解到Action才是具体处理用户的业务实现部分,但有的时候我们需要在Action处理业务之前进行有效性校验或数据类型转换,或者在Action处理业务之后进行日志记录或转换等,而且这些操作是低耦合,可自定义动态拆卸的。因此,就引入了Struts2拦截器的概念。
主要内容:
1.什么是Struts拦截器;
2.Struts拦截器的工作原理;
3.如何在struts.xml中配置使用Struts拦截器;
4.Struts2框架所提供的内置拦截器;
5.自定义Struts拦截器。
6.案例:自定义拦截器实现用户登录权限控制
1.什么是Struts拦截器
拦截器是Struts2框架的重要组成部分,Struts2的很多功能都是构建在拦截器之上的,如数据校验、转换器、国际化等。
作用:它可以动态拦截Action调用的对象,类似于Servlet中的过滤器。
其中,
单个拦截器:可以完成单个功能。
(多个)拦截器链:不同功能的拦截器按照一定的顺序排列在一起形成的链。
拦截器栈:拦截器链组成的集合。
2.Struts拦截器的工作原理;
Struts2的拦截器是AOP (Aspect-Object-Programming,面向切面编程)的一种实现策略,是可插拔的。它可以任意地组合Action提供的附加功能,而不需要修改Action的代码,开发者只需要提供拦截器的实现类,并将其配置在struts.xml中即可。
当用户触发事件,系统会在struts.xml配置文件中寻找对应的Action配置信息时,根据配置信息会先执行附加到Action之上的拦截器,然后再执行Action对象。
3.如何在struts.xml中配置使用Struts拦截器;
因为拦截器与Action之间处在一种关联关系,因此,我们需要现在struts.xml中声明它们的这种关系,系统才能根据配置信息顺序执行。
在struts.xml中声明拦截器,使用的是<interceptor>标签
。
1.配置单个拦截器
<interceptor name="interceptorName“ class="interceptorClass">
<param name="paramName">paramValue</param>
</interceptor>
2.配置拦截器栈(多个拦截器)
<interceptors>
<interceptor-stack name="interceptorStackName">
<interceptor-ref name="interceptorName" />
...
</interceptor-stack>
</interceptors>
3.配置当前所使用的默认拦截器
<default-interceptor-ref name="拦截器(栈)的名称"/>
4.Struts2框架所提供的内置拦截器;
Struts2利用其内建的拦截器其实已经可以完成大部分的操作, 当内置拦截器不能满足时,开发者也可以自己扩展。
Struts2中内置了许多拦截器,这些拦截器以name-class对的形式配置在struts-default.xml文件
中。
name 是拦截器的名称,也就是引用的名字;
class 是指定了该拦截器所对应的实现类。
在项目中,只要自定义的包继承了Struts2的struts-default包
,就可以使用默认包中所定义的这些内建拦截器。在struts-default.xml中,每一个拦截器都具有不同的意义和功能。
在struts-core-2.3.24.jar包中的根目录下找到struts-default.xml文件,打开后找到<interceptors>元素下的内建拦截器和拦截器栈。
5.自定义Struts拦截器。
在实际的项目开发中,Struts2的内置拦截器可以完成大部分的拦截任务。但是,一些与系统逻辑或业务逻辑相关的通用功能(如权限的控制、用户登录控制等),则需要通过自定义拦截器类来实现功能。
方法一:实现Interceptor接口
在程序开发过程中,如果需要开发自己的拦截器类,就需要直接或间接的实现com.opensymphony.xwork2.interceptor.Interceptor接口。
public interface Interceptor extends Serializable {
void init();
void destroy();
String intercept(ActionInvocation invocation) throws Exception;
}
如果需要自定义拦截器,只需要实现Interceptor接口的三个方法即可。
- init()方法在拦截器被创建后会立即被调用, 它在拦截器的生命周期内只被调用一次. 可以在该方法中对相关资源进行必要的初始化。
- destroy()方法与init()方法相对应,在拦截器实例被销毁之前,将调用该方法来释放和拦截器相关的资源。它在拦截器的生命周期内,也只被调用一次。
- Intercep()方法是拦截器的核心方法,用来添加真正执行拦截工作的代码,实现具体的拦截操作。它返回一个字符串作为逻辑视图,系统根据返回的字符串跳转到对应的视图资源。每拦截一个动作请求, 该方法就会被调用一次。该方法的ActionInvocation参数包含了被拦截的Action的引用,可以通过该参数的invoke()方法,将控制权转给下一个拦截器或者转给Action的execute()方法。
方法二:继承AbstractIntercepter类(建议)
该类实现了Interceptor接口,并且提供了init()方法和destroy()方法的空实现。使用时,可以直接继承该抽象类,而不用实现那些不必要的方法。因此,使用更为简单方便。
public abstract class AbstractInterceptor implements Interceptor {
public void init() {}
public void destroy() {}
public abstract String intercept(ActionInvocation invocation)
throws Exception;
}
用法:
只需继承AbstractInterceptor类,实现interceptor()方法就可以创建自定义拦截器。
只有当自定义的拦截器需要打开系统资源时,才需要覆盖AbstractInterceptor类的init()方法和destroy()方法。
- 总结:自定义拦截器的流程
1.必须实现Interceptor接口或继承AbstractInterceptor类;
2.需要在Struts.xml中声明自定义的拦截器;
3.在Struts.xml中的Action中使用拦截器。
6.案例:自定义拦截器实现用户登录权限控制
1.web.xml:引入Struts框架的包,并把Struts框架配置到项目中;
2.login.jsp:用户登录页面
3.User.java:模型实体类(JavaBean),作为传输参数Model;
4.LoginAction:验证登录的业务处理;
5.main.jsp:用户登录成功后,自动跳转到主页;
6.OperationAction:(用户登录后)用户操作的业务处理;
7.PrivilegeInterceptor.java:自定义拦截器,用于拦截没登录的用户。
8.success.jsp:用户操作成功所跳转到的模拟页面;
9.struts.xml:Struts框架的路由配置文件。
1.web.xml
先创建Web项目LoginPower,把Struts框架的包都引入到项目WebContent—WEB-INF—lib中,并把Struts框架配置到项目的web.xml中;
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">
<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>
<welcome-file-list>
<welcome-file>main.jsp</welcome-file>
</welcome-file-list>
</web-app>
2.login.jsp
在WebContent目录下新建用户登录页面login.jsp。
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>登录</title>
</head>
<body>
<center>
${requestScope.msg}<br>
<form action="/LoginPower/login.action" method="post">
<table>
<tr>
<td><label style="text-align: right;">用户名:</label></td>
<td><input type="text" name="username"></td>
</tr>
<tr>
<td><label style="text-align: right;">密码:</label></td>
<td><input type="password" name="password"></td>
</tr>
<tr>
<td align="right" colspan="2"><input type="submit" value="登录"></td>
</tr>
</table>
</form>
</center>
</body>
</html>
3.User.java
模型实体类(JavaBean),作为传输参数Model;
在项目Java Resource—src中新建一个cn.demo.domain包,用于存放所有的Model,并在里面新建一个JavaBean实体类:User用户类
package cn.demo.domain;
public class User {
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;
}
}
4.LoginAction
验证登录的业务处理;
在项目Java Resource—src中新建一个cn.demo.action包,用于存放所有的Action类。在里面新建LoginAction.java用于实现用户登录验证的功能。
package cn.demo.action;
import cn.demo.domain.User;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;
public class LoginAction extends ActionSupport implements ModelDriven<User> {
private static final long serialVersionUID = 1L;
private User user = new User();
@Override
public User getModel() {
return user;
}
@Override
public String execute() throws Exception {
//获取ActionContext
ActionContext actionContext = ActionContext.getContext();
if ("xiao".equals(user.getUsername())
&& "zhbit".equals(user.getPassword())) {
// 将用户存储在session中.
actionContext.getSession().put("user", user);
return SUCCESS;
} else {
actionContext.put("msg", "用户名或密码不正确");
return INPUT;
}
}
}
5.main.jsp
在WebContent目录下新建main.jsp;用于用户登录成功后,自动跳转到主页;
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>main.jsp</title>
</head>
<body>
<a href="/LoginPower/operation_add">增加功能</a><br>
<a href="/LoginPower/operation_update">更新功能</a><br>
<a href="/LoginPower/operation_del">删除功能</a><br>
<a href="/LoginPower/operation_find">查询功能</a><br>
</body>
</html>
6.OperationAction
(用户登录后)用户操作的业务处理;
在项目Java Resource—src的cn.demo.action包中,新建OperationAction.java用于实现用户登录后可执行的操作。
package cn.demo.action;
import com.opensymphony.xwork2.ActionSupport;
@SuppressWarnings("serial")
public class OperationAction extends ActionSupport {
public String add() {
System.out.println("增加功能 执行代码……");
return SUCCESS;
}
public String del() {
System.out.println("删除功能 执行代码……");
return SUCCESS;
}
public String update() {
System.out.println("更新功能 执行代码……");
return SUCCESS;
}
public String find() {
System.out.println("查询功能 执行代码……");
return SUCCESS;
}
}
7.PrivilegeInterceptor.java
自定义拦截器,用于拦截没登录的用户。
在项目Java Resource—src中新建一个cn.demo.interceptor包,用于存放所有的自定义拦截器。在里面新建PrivilegeInterceptor.java用于实现拦截没登录成功的用户。
package cn.demo.interceptor;
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
public class PrivilegeInterceptor extends AbstractInterceptor {
private static final long serialVersionUID = 1L;
@Override
public String intercept(ActionInvocation invocation) throws Exception {
ActionContext actionContext = invocation.getInvocationContext();// 得到ActionContext.
Object user = actionContext.getSession().get("user");//获取user对象
if (user != null) {
return invocation.invoke(); // 继续向下执行.
} else {
actionContext.put("msg", "您还未登录,请先登录");
return Action.LOGIN; //如果用户不存在,返回login值
}
}
}
8.success.jsp
在WebContent目录下新建success.jsp,用于用户操作成功所跳转到的成功提示页面;
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>操作成功页面</title>
</head>
<body>
用户:${user.username} 执行管理操作成功!
</body>
</html>
9.struts.xml
Struts框架的路由配置文件。
在src文件中新建struts.xml配置文件,用于本项目中所有的包、拦截器、Action等。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
"http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
<package name="struts2" namespace="/" extends="struts-default">
<!-- 声明自定义的拦截器 -->
<interceptors>
<!-- 定义:自定义拦截器privilege,用于拦截没登录的用户 -->
<interceptor name="privilege"
class="cn.demo.interceptor.PrivilegeInterceptor"/>
<!-- 定义一个拦截器栈myStack,里面包含:默认拦截器以及自定义的 privilege拦截器。-->
<interceptor-stack name="myStack">
<interceptor-ref name="defaultStack" />
<interceptor-ref name="privilege"></interceptor-ref>
</interceptor-stack>
</interceptors>
<!-- Action:用户登录 -->
<action name="login" class="cn.demo.action.LoginAction">
<result>/main.jsp</result>
<result name="input">/login.jsp</result>
</action>
<!-- Action:用户登录后的操作 -->
<action name="operation_*" class="cn.demo.action.OperationAction"
method="{1}">
<result>/success.jsp</result>
<result name="login">/login.jsp</result>
<!--在当前Action中,调用上面所定义的拦截器栈myStack -->
<interceptor-ref name="myStack" />
</action>
</package>
</struts>
运行效果
1.项目启动时,默认访问登录页面login.jsp
2.但如果用户直接访问main.jsp时,也能显示相应功能。此时,就需要通过Interceptor拦截器控制用户权限。
3.如果用户点击操作功能,则拦截,并返回登录页面
4.只有用户输入用户名:xiao和密码:zhbit后,才被拦截器放行。
作者: 肖sir@ZHBIT
2018 年 09月 27日