Struts 2 Interceptor示例

时间:2020-02-23 14:36:04  来源:igfitidea点击:

欢迎来到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类。