Struts 2 Interceptor示例
欢迎来到Struts 2拦截器示例。
在Struts 2上工作时,大多数时候您都会花在Action Class上,但是Interceptor是Struts 2 Framework的基础。
Struts 2 拦截器(Interceptor)
Struts 2拦截器负责框架完成的大多数处理。
例如,将请求参数传递给动作类,使Servlet API请求,响应,会话可用于动作类,验证,i18n支持等。
Struts 2提供了一堆拦截器,其中大多数是在struts-default包中定义的,并在defaultStack拦截器堆栈中使用。
拦截器是Struts 2框架的强大功能,它在实现关注点的高度分离方面起着至关重要的作用。
在我们的Struts 2教程中,我们研究了Struts 2架构,并发现每个请求和响应都通过拦截器传递。
因此,拦截器可以注入不同的对象,根据请求和响应执行特定的操作,并为应用程序执行一些交叉任务。
ActionInvocation负责封装Action类和拦截器,并按顺序触发它们。
在ActionInvocation中使用的最重要的方法是invoke()方法,该方法跟踪拦截器链并调用下一个拦截器或者操作。
这是Java EE框架中"责任链"模式的最佳示例之一。
Struts 2 API按照声明的顺序读取为操作定义的拦截器。
因此,如果为action1定义了interceptor1和interceptor2,则ActionInvocation将在随后的链中调用它们。
拦截器1->拦截器2->动作1->拦截器2->拦截器1
如果任何一个拦截器intercept()方法确定不应执行该操作,则它可以返回字符串值,并且将从struts配置文件中具有该名称的全局结果创建响应。
Struts 2拦截器和全局结果配置
我们可以在struts.xml文件中将全局结果定义为:
<global-results> <result name="login" type="redirect">/login.action</result> </global-results>
这些结果可以被任何动作类使用。
为一个程序包定义拦截器包括几个步骤,我们需要使用拦截器,拦截器,拦截器堆栈和默认拦截器-ref元素对其进行定义。
它们应该是包的第一个元素,否则您将收到以下错误消息:
元素类型"包"的内容必须与"(结果类型?,拦截器?,默认拦截器参考?,默认动作参考?,默认类参考?,全局结果?,全局异常?映射?,动作*)"。
Struts 2拦截器示例
拦截器配置的一个简单示例是:
<package name="user" namespace="/" extends="struts-default"> <interceptors> <interceptor name="authentication" class="com.theitroad.struts2.interceptors.AuthenticationInterceptor"></interceptor> <interceptor-stack name="authStack"> <interceptor-ref name="authentication"></interceptor-ref> <interceptor-ref name="defaultStack"></interceptor-ref> </interceptor-stack> </interceptors> <default-interceptor-ref name="authStack"></default-interceptor-ref> </package>
请注意,已经在struts-default软件包中配置了defaultStack,这就是为什么我们不需要在上面的示例中定义它的原因。
让我们创建一个简单的应用程序,其中我们将使用自定义拦截器类为我们的应用程序提供身份验证。
使用拦截器将使我们的应用程序松散耦合,灵活且可配置。
我们的最终项目将如下图所示。
Struts2拦截器示例项目配置文件
web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xmlns="https://java.sun.com/xml/ns/javaee" xsi:schemaLocation="https://java.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0"> <display-name>Struts2InterceptorExample</display-name> <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> </web-app>
pom.xml
<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>Struts2InterceptorExample</groupId>
<artifactId>Struts2InterceptorExample</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<dependencies>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-core</artifactId>
<version>2.3.15.1</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>2.3</version>
<configuration>
<warSourceDirectory>WebContent</warSourceDirectory>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
</plugins>
<finalName>${project.artifactId}</finalName>
</build>
</project>
pom.xml和web.xml配置文件是可以理解的。
struts.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN" "https://struts.apache.org/dtds/struts-2.3.dtd"> <struts> <constant name="struts.convention.result.path" value="/"></constant> <package name="user" namespace="/" extends="struts-default"> <interceptors> <interceptor name="authentication" class="com.theitroad.struts2.interceptors.AuthenticationInterceptor"></interceptor> <interceptor-stack name="authStack"> <interceptor-ref name="authentication"></interceptor-ref> <interceptor-ref name="defaultStack"></interceptor-ref> </interceptor-stack> </interceptors> <default-interceptor-ref name="authStack"></default-interceptor-ref> <global-results> <result name="login" type="redirect">/home.action</result> </global-results> <action name="home"> <interceptor-ref name="defaultStack"></interceptor-ref> <result>/login.jsp</result> </action> <action name="login" class="com.theitroad.struts2.actions.LoginAction"> <interceptor-ref name="defaultStack"></interceptor-ref> <result name="success">/welcome.jsp</result> <result name="input">/login.jsp</result> </action> <action name="welcome" class="com.theitroad.struts2.actions.WelcomeAction"> <result name="success">/welcome.jsp</result> </action> </package> </struts>
注意,我在声明一个拦截器" authentication",它是指类" com.theitroad.struts2.interceptors.AuthenticationInterceptor"。
还要注意," authStack"是该软件包的默认拦截器堆栈。
我没有将这个堆栈用于login.jsp,因为它用于登录,并且那时用户将没有会话可以进行身份验证。
结果页面
login.jsp
<%@ page language="java" contentType="text/html; charset=US-ASCII" pageEncoding="US-ASCII"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "https://www.w3.org/TR/html4/loose.dtd"> <%-- Using Struts2 Tags in JSP --%> <%@ taglib uri="/struts-tags" prefix="s"%> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=US-ASCII"> <title>Login Page</title> </head> <body> <h3>Welcome User, please login below</h3> <s:form action="login"> <s:textfield name="user" label="User Name"></s:textfield> <s:textfield name="password" label="Password" type="password"></s:textfield> <s:submit value="Login"></s:submit> </s:form> </body> </html>
welcome.jsp
<%@ page language="java" contentType="text/html; charset=US-ASCII" pageEncoding="US-ASCII"%> <%@ taglib uri="/struts-tags" prefix="s"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "https://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=US-ASCII"> <title>Welcome Page</title> </head> <body> <h3>Welcome <s:property value="userName"></s:property></h3> </body> </html>
JSP结果页面易于理解,与拦截器无关。
模型类
User.java
package com.theitroad.struts2.models;
public class User {
private String user;
private String password;
private String userName;
public String getUser() {
return user;
}
public void setUser(String user) {
this.user = user;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
}
一个简单的Java Bean,具有在结果页面中使用的属性。
我们将在动作类中使用此java bean来保存属性。
定制拦截器
UserAware.java
package com.theitroad.struts2.interceptors;
import com.theitroad.struts2.models.User;
public interface UserAware {
public void setUser(User user);
}
与Struts 2 API * Aware接口类似的接口,我们将在自定义拦截器中使用它来将值注入操作类。
要了解有关Struts 2 API * Aware接口的更多信息,请阅读:
如何在Struts 2操作中获取Servlet会话,请求,响应,上下文属性
AuthenticationInterceptor.java
package com.theitroad.struts2.interceptors;
import java.util.Map;
import com.theitroad.struts2.models.User;
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.Interceptor;
public class AuthenticationInterceptor implements Interceptor {
private static final long serialVersionUID = -5011962009065225959L;
@Override
public void destroy() {
//release resources here
}
@Override
public void init() {
//create resources here
}
@Override
public String intercept(ActionInvocation actionInvocation)
throws Exception {
System.out.println("inside auth interceptor");
Map<String, Object> sessionAttributes = actionInvocation.getInvocationContext().getSession();
User user = (User) sessionAttributes.get("USER");
if(user == null){
return Action.LOGIN;
}else{
Action action = (Action) actionInvocation.getAction();
if(action instanceof UserAware){
((UserAware) action).setUser(user);
}
return actionInvocation.invoke();
}
}
}
这是我们的自定义拦截器类,正在实现com.opensymphony.xwork2.interceptor.Interceptor接口。
init()和destroy()是拦截器生命周期方法,我们可以实现这些方法来初始化和销毁拦截器intercept()方法中使用的某些资源。
Intercept()是ActionInvocation类调用的方法,因此这就是我们的拦截器代码所在的位置。
我们可以从ActionInvocation参考获取会话属性,并使用它来确保会话有效。
如果找不到该属性,则返回的是全局结果"登录",该结果会将用户重定向到登录页面。
如果会话有效,那么我们会将用户注入到动作类中。
instanceof关键字用于确保操作类正在实现UserAware接口。
最后,我们调用ActionInvocation invoke()方法,该方法将调用链中的下一个拦截器或者操作类。
动作类
LoginAction.java
package com.theitroad.struts2.actions;
import java.util.Map;
import org.apache.struts2.interceptor.SessionAware;
import com.theitroad.struts2.models.User;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;
public class LoginAction extends ActionSupport implements SessionAware, ModelDriven<User>{
private static final long serialVersionUID = -3369875299120377549L;
@Override
public String execute(){
System.out.println("inside execute");
if("hyman".equals(user.getUser()) && "admin".equals(user.getPassword())){
user.setUserName("hyman Kumar");
sessionAttributes.put("USER", user);
return SUCCESS;
}
return INPUT;
}
private User user = new User();
private Map<String, Object> sessionAttributes = null;
@Override
public void setSession(Map<String, Object> sessionAttributes) {
this.sessionAttributes = sessionAttributes;
}
@Override
public User getModel() {
return user;
}
}
请注意,LoginAction正在实现SessionAware接口以获取会话属性,并且如果验证了用户,则将用户属性放在会话中以在身份验证拦截器中用于验证。
请注意,LoginAction没有实现UserAware接口。
WelcomeAction.java
package com.theitroad.struts2.actions;
import com.theitroad.struts2.interceptors.UserAware;
import com.theitroad.struts2.models.User;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;
public class WelcomeAction extends ActionSupport implements UserAware, ModelDriven<User> {
private static final long serialVersionUID = 8111120314704779336L;
@Override
public String execute(){
return SUCCESS;
}
private User user;
@Override
public void setUser(User user) {
this.user=user;
}
public User getUser(User user){
return this.user;
}
@Override
public User getModel() {
return this.user;
}
}
注意,WelcomeAction正在实现UserAware接口,我们的AuthenticationInterceptor将其注入到幕后的action类中。
请注意,动作类很简单,没有用于验证用户会话或者设置用户Bean的代码。
从上面的动作类代码中可以明显看出,如果我们巧妙地使用拦截器,则可以减少执行多个动作所需的常见任务的大量代码冗余。
如果您尚未登录,并且尝试调用login.action,则我们的身份验证拦截器会将您转发到登录页面,并且永远不会调用action类。

