1. Estructura del proyecto
Crea un proyecto Maven en tu IDE y configúralo con empaquetado war:
<packaging>war</packaging>
Añade la carpeta src/main/webapp y el descriptor web.xml en src/main/webapp/WEB-INF. La estructura final debe verse así:
mi-aplicacion
├── src
│ ├── main
│ │ ├── java
│ │ ├── resources
│ │ └── webapp
│ │ └── WEB-INF
│ │ └── web.xml
│ └── test
├── pom.xml
└── ...
2. Configuración de la base de datos
Archivo jdbc.properties con los datos de conexión:
jdbc.user=root
jdbc.pass=root
jdbc.url=jdbc:mysql://localhost/gestion?useUnicode=true&characterEncoding=UTF-8&serverTimezone=America/Mexico_City
3. Configuración raíz de Spring
En applicationContext.xml se escanean todos los componentes excepto los controladores, se configura el origen de datos, MyBatis y la trasnacción por anotaciones:
<?xml version="1.0" encoding="UTF-8"? >
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- Escanear todo excepto los @Controller -->
<context:component-scan base-package="com.ejemplo.gestion">
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!-- Propiedades externas -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- Origen de datos con Druid -->
<bean id="dataSourceMain" class="com.alibaba.druid.pool.DruidDataSource">
<property name="username" value="${jdbc.user}"/>
<property name="password" value="${jdbc.pass}"/>
<property name="url" value="${jdbc.url}"/>
</bean>
<!-- Sesión de MyBatis -->
<bean id="sessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSourceMain"/>
<property name="typeAliasesPackage" value="com.ejemplo.gestion.entity"/>
<property name="mapperLocations">
<array>
<value>classpath:com/ejemplo/gestion/mapper/*Mapper.xml</value>
</array>
</property>
</bean>
<!-- Escáner de mapeadores -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="sqlSessionFactoryBeanName" value="sessionFactory"/>
<property name="basePackage" value="com.ejemplo.gestion.mapper"/>
</bean>
<!-- Gestor de transacciones -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSourceMain"/>
</bean>
<!-- Habilitar @Transactional -->
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
Para que las transacciones funcionen, anota los métodos de servicio con @Transactional:
import org.springframework.transaction.annotation.Transactional;
@Service
public class ProductoService {
@Transactional
public void guardar(Producto producto) {
// operaciones de persistencia
}
}
4. Configuración de Spring MVC y FreeMarker
Archivo spring-mvc.xml:
<?xml version="1.0" encoding="UTF-8"? >
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- Solo los controladores -->
<context:component-scan base-package="com.ejemplo.gestion" use-default-filters="false">
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<mvc:annotation-driven/>
<!-- Recursos estáticos -->
<mvc:resources mapping="/static/**" location="/static/"/>
<!-- Cargar variables para FreeMarker -->
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:freemarker.properties</value>
</list>
</property>
</bean>
<!-- Configuración de FreeMarker -->
<bean id="freeMarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
<property name="templateLoaderPath" value="/WEB-INF/vistas/"/>
<property name="defaultEncoding" value="UTF-8"/>
<property name="freemarkerVariables">
<map>
<entry key="ctx" value="${app.context}"/>
</map>
</property>
<property name="freemarkerSettings">
<props>
<prop key="template_update_delay">10</prop>
<prop key="locale">es_ES</prop>
<prop key="datetime_format">dd/MM/yyyy HH:mm:ss</prop>
<prop key="date_format">dd/MM/yyyy</prop>
<prop key="time_format">HH:mm:ss</prop>
<prop key="number_format">#.####</prop>
</props>
</property>
</bean>
<!-- Resolver de vistas -->
<bean class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
<property name="viewClass"
value="org.springframework.web.servlet.view.freemarker.FreeMarkerView"/>
<property name="suffix" value=".ftl"/>
<property name="contentType" value="text/html;charset=UTF-8"/>
<property name="allowRequestOverride" value="true"/>
<property name="allowSessionOverride" value="true"/>
<property name="exposeRequestAttributes" value="true"/>
<property name="exposeSessionAttributes" value="true"/>
</bean>
</beans>
5. Variables globales de FreeMarker
Archivo freemarker.properties:
app.context=/mi-aplicacion
6. Configuración del servlet
Archivo web.xml:
<?xml version="1.0" encoding="UTF-8"? >
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<filter>
<filter-name>charsetFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceRequestEncoding</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>forceResponseEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>charsetFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
7. Controlador de ejemplo
Crea una plantilla en /WEB-INF/vistas/inicio.ftl y un controlador para probar la integración:
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class InicioController {
@GetMapping("/")
public String inicio(Model modelo) {
modelo.addAttribute("mensaje", "¡SSM + FreeMarker funcionando!");
return "inicio";
}
}
En la plantilla inicio.ftl se puede usar la variable global definida:
<html>
<head>
<meta charset="UTF-8">
<title>Inicio</title>
</head>
<body>
<h1>${mensaje}</h1>
<p>Contexto de la app: ${ctx}</p>
</body>
</html>