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类。