Este es el segundo artículo de la serie de Tutoriales de Struts 2. Si has llegado directamente aquí, te recomendaría que también revises el post anterior. Tutorial para Principiantes de Struts 2 En el último tutorial, exploramos la arquitectura de Struts 2, sus componentes y creamos una aplicación web simple de Struts 2 con configuración basada en XML (struts.xml). En este tutorial veremos cómo podemos evitar completamente el archivo de configuración de Struts utilizando anotaciones o convenciones de nombres.
Concepto de Convención en Struts 2
Struts 2 utiliza dos metodologías para encontrar las clases de acción y las clases de resultado. Necesitamos utilizar la API del complemento struts2-convention-plugin para utilizar cualquiera de estas metodologías. Si tienes una aplicación web normal, puedes descargar su archivo jar y colocarlo en el directorio lib de la aplicación web. Para proyectos Maven, simplemente puedes agregar su dependencia como se muestra a continuación.
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-convention-plugin</artifactId>
<version>2.3.15.1</version>
</dependency>
-
Escaneo: En este método, especificamos el paquete que necesita ser escaneado en busca de clases de acción. La configuración debe hacerse en el archivo web.xml para el filtro de Struts 2, como se muestra a continuación.
<filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class> <init-param> <param-name>actionPackages</param-name> <param-value>com.journaldev.struts2.actions</param-value> </init-param> </filter>
Struts 2 encontrará clases de acción mediante los siguientes métodos.
- Cualquier clase anotada con las anotaciones @Action o @Actions.
- Cualquier clase que implemente la interfaz Action o extienda la clase ActionSupport.
- Cualquier clase cuyo nombre termine con Action y contenga el método execute(). Para estas clases, se utiliza una convención de nomenclatura para determinar la acción y los resultados.
-
Convención de Nomenclatura: Struts 2 creará automáticamente una acción para las clases cuyo nombre termina con Action. El nombre de la acción se determina eliminando el sufijo Action y convirtiendo la primera letra a minúscula. Entonces, si el nombre de la clase es HomeAction, la acción será “home”. Si estas clases no están anotadas con @Result para proporcionar el resultado, las páginas de resultado se buscan en el directorio WEB-INF/content y el nombre debería ser {action}-{return_string}.jsp. Así que si la clase de acción HomeAction devuelve “success”, la solicitud se reenviará a la página WEB-INF/content/home-success.jsp. Dependiendo únicamente de la convención de nomenclatura puede ser muy confuso, y no podemos utilizar la misma página JSP para otras clases de acción. Por lo tanto, deberíamos tratar de evitar esto y utilizar una configuración basada en anotaciones.
Ahora estamos listos para crear nuestra aplicación Hola Mundo con struts 2 utilizando anotaciones y no tendremos un archivo de configuración de struts 2. Cree un proyecto web dinámico en Eclipse llamado Struts2AnnotationHelloWorld y conviértalo en un proyecto Maven. El proyecto final se ve como en la imagen a continuación.
Configuración de Maven
Hemos agregado las dependencias struts2-core y struts2-convention-plugin en el archivo pom.xml, el código final de pom.xml es:
<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>Struts2AnnotationHelloWorld</groupId>
<artifactId>Struts2AnnotationHelloWorld</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>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-convention-plugin</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>
Configuración del Descripto de Implementación
<?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>Struts2AnnotationHelloWorld</display-name>
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
<init-param>
<param-name>actionPackages</param-name>
<param-value>com.journaldev.struts2.actions</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
Observa el elemento init-param donde proporcionamos el paquete de clases de acción que será escaneado por Struts 2.
Páginas de Resultados
Tenemos tres páginas de resultados en nuestra aplicación. 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="name" label="User Name"></s:textfield>
<s:textfield name="pwd" label="Password" type="password"></s:textfield>
<s:submit value="Login"></s:submit>
</s:form>
</body>
</html>
error.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>Error Page</title>
</head>
<body>
<h4>User Name or Password is wrong</h4>
<s:include value="login.jsp"></s:include>
</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="name"></s:property></h3>
</body>
</html>
Ahora creemos nuestras clases de acción que vamos a anotar para configurar las páginas de acción y resultados.
Clases de Acción con Anotaciones
package com.journaldev.struts2.actions;
import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.Actions;
import org.apache.struts2.convention.annotation.Namespace;
import org.apache.struts2.convention.annotation.Namespaces;
import org.apache.struts2.convention.annotation.Result;
import com.opensymphony.xwork2.ActionSupport;
/**
* An empty class for default Action implementation for:
*
* <action name="home">
* <result>/login.jsp</result>
* </action>
* HomeAction class will be automatically mapped for home.action
* Default page is login.jsp which will be served to client
* @author pankaj
*
*/
@Namespaces(value={@Namespace("/User"),@Namespace("/")})
@Result(location="/login.jsp")
@Actions(value={@Action(""),@Action("home")})
public class HomeAction extends ActionSupport {
}
Observa que HomeAction es una clase vacía con el único propósito de redirigir la solicitud a la página login.jsp.
package com.journaldev.struts2.actions;
import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.Namespace;
import org.apache.struts2.convention.annotation.Namespaces;
import org.apache.struts2.convention.annotation.Result;
/**
* Notice the @Action annotation where action and result pages are declared
* Also notice that we don't need to implement Action interface or extend ActionSupport
* class, only we need is an execute() method with same signature
* @author pankaj
*
*/
@Action(value = "login", results = {
@Result(name = "SUCCESS", location = "/welcome.jsp"),
@Result(name = "ERROR", location = "/error.jsp") })
@Namespaces(value={@Namespace("/User"),@Namespace("/")})
public class LoginAction {
public String execute() throws Exception {
if("pankaj".equals(getName()) && "admin".equals(getPwd()))
return "SUCCESS";
else return "ERROR";
}
//Java Bean para contener los parámetros del formulario
private String name;
private String pwd;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
}
Ten en cuenta el uso de las anotaciones @Action, @Actions, @Result, @Namespace y @Namespaces, su uso es autoexplicativo. Ahora, al ejecutar nuestra aplicación, obtenemos las siguientes páginas de respuesta.
Si has leído la última publicación donde desarrollamos la misma aplicación con la configuración struts.xml, notarás que es casi igual. El único cambio es la forma en que conectamos nuestras clases de acción de la aplicación y las páginas de resultado.