Перехватчик Interceptor

Действие в Struts2 являются основным объектом фреймворка, определяющим логику приложения. Но часто требуется решать отдельные задачи, которые должны располагаться вне действий. К таким задачам можно отнести вспомогательный функционал, связанный с авторизацией пользователя, проверкой корректности данных и т.д. Для решения данных задач в Struts2 используется перехватчик Interceptor. Фреймворк включает большой набор перехватчиков, но при необходимости можно создать и собственные.

На следующем рисунке представлена схема обработки запроса, поступившего от пользователя. FilterDispatcher уже выполнил свою миссию и передал управление фреймворку для выполнения соответствующего действия, к которому подключено несколько интерцепторов. Согласно данной структуры Interceptors включаются в процесс обработки перед выполнением действия. Потом управление передается действию, по окончанию которого управление вновь возвращается к интерцептору для завершения своих функций.

При использовании перехватчиков очень важно понимать их предназначение. Interceptors в Struts2 предназначены для перехвата процесса обработки запроса. Не стоит делать перехватчики, чтобы использовать их в 1-2 действиях. В таких случаях проще ограничиться вызовом из методов дополнительных действий. С перехватчиками надо обращаться достаточно осторожно, это очень эффективный инструмент, но поддержка огромного количества перехватчиков через некоторое время может сделать работу "кошмаром".

Interceptors в Struts2 позволяют значительно упростить приложение. Но вместе с плюсами у каждого решения бывают и подводные камни. Многослойная архитектура, получаемая при использовании перехватчиков, способна при неумелом использовании привнести "неявные" зависимости.

Рассмотрим пример наиболее частого использования перехватчиков Interceptors. Допустим в WEB-приложении имеются две области : открытая и закрытая. В открытой области "public" пользователь должен авторизоваться, т.е. ввести логин и пароль. Если информация введена правильно и пользователь с данной учетной записью присутствует в нашей Базе Данных, то ему открывается закрытая область "private", включающая несколько страниц.

Пользователь может попытаться войти на одну из страниц закрытой области минуя страницы авторизации. Поскольку в закрытой области страниц может быть сколько угодно, то удобнее использовать перехватчик, чтобы не "городить огород" на каждой странице. Наш Interceptor должен "преградить интервенту" дорогу, и отправить его на страницу авторизации.

Создание перехватчика Interceptor, метод intercept

Перехватчик создается на основе класса Interceptor и включает три метода : destroy, init и intercept. В методе intercept выполняется обработка данных. Но можно создать интерцептор и на основе класса AbstractInterceptor. В этом случае не надо будет переопределять чаще всего не используемые методы destroy, init, и можно будет сосредоточиться только на методе intercept.

В качестве параметра методу intercept передается класс ActionInvocation - это основной класс в процессе выполнения действия перехватчика. ActionInvocation управляет всей задачей обработки запроса и содержит в себе всю информацию о процессе обработки запроса действием.

Объект ActionInvocation создается фреймворком, когда поступает от пользователя запрос. После этого определяется действие и оно устанавливается как свойство объекта ActionInvocation. Далее фреймворк определяет какой набор перехватчиков должен быть вызван и в каком порядке, после этого они добавляются в ActionInvocation. Также ActionInvocation содержит параметры запроса и варианты результатов действия.

После того, как все необходимые атрибуты (действие, перехватчики, параметры запроса) созданы, вызывается метод invoke. Ошибочно утверждать что вызовется первый перехватчик из стека. Это обязанность реализации ActionInvocation определить, какой из перехватчиков должен быть вызван и на какой стадии обработки запроса находится сейчас приложение.

Struts2 пример с использованием Interceptor

Рассмотрим пример использования двух интерцепторов, которые только перехватывают управления не вмешиваясь в бизнес-логику. На следующем скриншоте представлена структура проекта StrutsInterceptor в среде разработки Eclipse.

Проект включает действие DummyAction.java, интерцепторы FirstInterceptor.java и SecondInterceptor.java, файл конфигурации struts.xml, три JSP-страницы и дескриптор приложения web.xml. Листинг и описание web.xml приведен на странице Пример Struts2, где дополнительно рассматривается вопрос кодировки передаваемых серверу сообщений.

Листинг класса-действия DummyAction.java

package example;

public class DummyAction
{  
    private String result;
	
    public String execute()  
    {  
        System.out.println("\tВызов действия DummyAction.execute()");  
        if (result.equalsIgnoreCase("welcome"))
            return "success";
        return "input";
    }
    public String getResult() {
        return result;
    }
    public void setResult(String result) {
        this.result = result;
    }
}

Действие DummyAction реализует функцию execute и дополнительно включает методы свойства поля "result" (get и set). Как только будет обращение к этому действию Struts2 сразу же вызовет его метод execute, который в зависимости от значения поля "result", вернет либо "success", либо "input". Дополнительно действие выведет в консоль сообщение о работе метода.

Листинг класса-интерцептора FirstInterceptor.java

package example;

import com.opensymphony.xwork2.ActionInvocation;  
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;

public class FirstInterceptor extends AbstractInterceptor
{  
    private static final long serialVersionUID = 1L;
    @Override  
    public String intercept(ActionInvocation actionInvocation) throws Exception
    {  
        System.out.println("Вход в Interceptor_1");
        
        String result = actionInvocation.invoke();  
        
        System.out.println(Выход из Interceptor_1);
        
        return result;  
    }
}

Листинг класса-интерцептора SecondInterceptor.java

package example;

import com.opensymphony.xwork2.ActionInvocation;  
import com.opensymphony.xwork2.interceptor.Interceptor;  

public class SecondInterceptor implements Interceptor
{  
    private static final long serialVersionUID = 1L;

    @Override  
    public void destroy() {}  
	  
    @Override  
    public void init() {}  
	  
    @Override  
    public String intercept(ActionInvocation actionInvocation) throws Exception
    {  
        System.out.println("Вход в Interceptor_2");  
		  
        String result = actionInvocation.invoke();  
		  
        System.out.println("Выход из Interceptor_2");
		  
        return result;  
    } 
}

Представленные перехватчики получают управления, выводят в консоль сообщение и передают управление действию вызовом метода actionInvocation.invoke(). Как только управление возвращается к ним снова, они выводят в консоль сообщение о завершении работы и возвращают результат выполнения действия.

Обратите внимание на наследование родительских классов и переопределение методов.

Листинг файла конфигурации struts.xml

Следует обратить внимание на подключение перхватчиков к действию.

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" 
          "http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
    <package name="default" extends="struts-default" namespace="/">  
        <interceptors>
            <interceptor name="firstInterceptor"  class="example.FirstInterceptor" />  
            <interceptor name="secondInterceptor" class="example.SecondInterceptor" />

            <interceptor-stack name="myStack">
                <interceptor-ref name="firstInterceptor"/>
                <interceptor-ref name="secondInterceptor"/>
            </interceptor-stack>
        </interceptors>
                  
        <action name="Interceptor" class="example.DummyAction">  
            <interceptor-ref name="params" />
            <interceptor-ref name="myStack" />
            <result name="success">welcome.jsp</result>  
            <result name="input" type="redirect">login.jsp</result>  
        </action>
    </package>
</struts>

В представленном файле конфигурации определены действие "Interceptor" и два интерцептора, объединенные в один "myStack". Интерцепторы списком (стеком) подключены к действию. Дополнительно к действию подключен интерцептор "params", который обеспечивает передачу в действие значение свойства "result". Если "params" не подключить, то в действие DummyAction не будет передано свойство "result".

Следует обратить на описание атрибутов действия. При возвращении результата действия "success" сервер в строке URL отобразит наименование действия "Interceptor" со страницей welcome.jsp. А для результата "input" должен и в строке URL отобразить наименование страницы login.jsp.

Листинг файла index.jsp

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<%@page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@taglib uri="/struts-tags" prefix="s" %>

<html>
  <head>
     <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">  
     <title>Dummy Action</title>
  </head>  
  <body>
     <s:form action="Interceptor">
       <<:textfield name="result" label="Текст" /><br />
       <s:label cssStyle="color:green;font-style:italic;font-size:0.8em;" 
                value="'welcome' для перехода на страницу welcome.jsp"/>
              
       <s:submit value="Вызов действия" align="center" />
     </s:form>
  </body>
</html>

На этой странице необходимо ввести текст и нажать кнопку. Если введете текст "welcome", то попадете на страницу welcome.jsp, в противном случае на страницу login.jsp.

Примечание : чтобы в класс-действие DummyAction с подключенным интерцептором было передано значение текстового поля необходимо было к этому действию дополнительно подключить интерцептор "params" в файле конфигурации.

Листинг файла welcome.jsp

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<html>
  <head>
    <title>Welcome</title>
  </head>
  <body>
    <h3>Struts2 приветствует Вас на странице Welcome.jsp</h3>
  </body>
</html>

Примечание : обратите внимание, что перенаправления нет и в строке URL отображается наименование действия.

Листинг файла login.jsp

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<%@page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@taglib uri="/struts-tags" prefix="s" %>

<html>
  <head>
    <title>Страница авторизации</title>
  </head>
  <body>
    <!-- Действие "login" в файле конфигурации не определено -->
    <s:form action="login" >
	    <s:textfield name="userName" label="Логин"/>
	    <s:password  name="password" label="Пароль"/>
        <s:submit />
    </s:form>
  </body>
</html>

Примечание : обратите внимание на строку URL - перенаправление есть.

При работе приложения в консоль будут выведены следующие сообщения :


Вход в Interceptor_1
Вход в Interceptor_2
	Вызов действия DummyAction.execute()
Выход из Interceptor_2
Выход из Interceptor_1
 

Как видно из результатов протоколирования, первоначально в работу вступают перехватчики. Они вызывают действие, получают от него результат и завершают свою работу.

Подводя итоги

можно повторить, что перехватчики - это очень мощный механизм в Struts2, который может позволить создавать очень гибкие и легко поддерживаемые решения. Понимание работы перехватчиков - ключ к понимаю Struts2. Но как у любого инструмента у него есть плюсы и минусы.

Interceptors в Struts2 могут применяться в случаях:

  • взаимодействия с действиями, чтобы предоставить им дополнительную информацию;
  • извлечения параметров из запроса, обработки загруженных файлов;
  • выполнения различных функций перед выполнением действия, например логирование URL запросов и результатов действия;
  • отслеживания и обработки исключений.

Скачать пример

Исходные коды рассмотренного примера можно скачать здесь (5.20 Мб).

  Рейтинг@Mail.ru