Spring Constructor Namespace

En esta oportunidad les escribo sobre un nuevo namespace que apareció en Spring 3.1 (y me dí cuenta recién XD ) y sirve para configurar los beans haciendo uso de su constructor. Como su nombre lo indica este es un namespace que opera sólo sobre los constructores de beans y así permitir la inyección de beans o valores.

Les dejo una imagen de como se activa este nuevo namespace en el STS.

Veamos un ejemplo simple de cómo usar namespace C.

Supongamos la siguiente clase:

1
2
3
4
5
6
7
8
public class SimpleSPImpl extends StoredProcedure
implements SimpleSP {

public SimpleSPImpl(DataSource ds, String spName) {
super(ds, spName);
}

}
1
2
3
4
5
6
7
8
9
10
<bean id="bean_id" class="com.example.SimpleSPImpl">
<constructor-arg name="ds" ref="datasource" />
<constructor-arg name="spName" value="spNameTest" />
</bean>

<!-- Definición del datasource de ejemplo -->
<bean id="datasource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
...
...
</bean>

Ahora usando el namespace c quedaría algo mas simple:

1
2
3
4
5
6
7
8
<bean id="bean_id" class="com.example.SimpleSPImpl 
c:ds-ref="datasource" c:spName="spNameTest" />

<!-- Definición del datasource de ejemplo -->
<bean id="datasource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
...
...
</bean>

Como pueden notar en el ejemplo, el namespace C permite configurar los constructores de los beans y tiene dos formas básicas, por valor y referencia. Para inyectar una instancia preconfigurada se debe usar el sufijo -ref para hacer alusión a que es una referencia. Si se desea inyectar un valor, sólo se usa el nombre del argumento del constructor.

Como siempre sus comentarios son bienvenidos.

SpringFramework: ProxyFactoryBean

Una manera sencilla de crear Proxy es usando SpringFramework, siempre y cuando tengas configurado tu aplicación con Spring, de otra forma, puedes buscar otras librerías que puedan ayudarte con el patrón Proxy (dinámico), (cglib y javassist) o puedes utilizar la API de java para crearlos. Revisa aquí Java Dynamic Proxy o Patrones De Diseño: Proxy.

Por ahora voy a mostrar como configurar un Proxy Dinámico con SprinFramework, configuraciones y par un de conceptos.

Conceptos

  • Target: Es la clase que será envuelta por el proxy (RealSubject)
  • Interceptor(es): Son las clases accesorias donde deberás agregar la lógica de negocio asociada al Proxy. Estos interceptores pueden ser mas de uno para un mismo target.
  • ProxyFactoryBean: Es la clase de Spring Framework para crear proxys dinámicos, al estilo Spring (con un FactoryBean). Deja un bean configurado dentro del contexto, listo para ser usado. Lo bueno de esta estrategia es que la creación del Proxy se realiza sólo una vez y cuando levanta el contexto, al contrario del Proxy que provee la API de Java, por cada invocación se debe crear el Proxy (aunque es rápido pero todo suma).

Interceptores

Los interceptores son clases que contienen la lógica de negocio del Proxy, es el símil a el InvocationHandler de la API de Java. Estas clases deben ser implementadas desde la interfaz Interceptor que es la de mas alta jerarquía o de su herencia, MethodInterceptor o ConstructorInterceptor. Estas últimas clases pertenecen a la librería de aopalliance versión 1.0 en el package org.aopalliance.intercept.

Para poder agregar las 2 funcionalidades (loggear los parámetros de entrada y validar que no sean negativos), implementaremos 2 Interceptores con la finalidad de aislar las lógicas de negocio de cada uno.

Implementación de Validador de Argumentos

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class ValidateArgumentsInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
for (Object arg : invocation.getArguments()) {
if (arg instanceof Integer) {
Integer number = Integer.class.cast(arg);
if (number.intValue() < 0) {
throw new InvalidNumberValueException("Invalid number ["
+ number + "]");
}
} else {
throw new InvalidNumberValueException("Invalid type");
}
}
return invocation.proceed();
}

public static class InvalidNumberValueException extends RuntimeException {

private static final long serialVersionUID = 3067612583360286918L;

/**
* @param message
*/
public InvalidNumberValueException(String message) {
super(message);
}

}
}

Implementación de Logger de Argumentos

1
2
3
4
5
6
7
8
9
10
11
public class LoggerArgumentsInterceptor implements MethodInterceptor {

@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
for (Object arg : invocation.getArguments()) {
System.out.println("Argument " + arg.toString());
}
return invocation.proceed();
}

}

Ahora que tenemos los Interceptores, juntamos todo en la configuración del contexto de Spring.

Configurar contexto Spring

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">

<!-- RealSubject o target -->
<bean id="calculadora" class="patterns.dynamicproxy.CalculadoraImpl" />

<!-- Proxy -->
<bean id="calculadoraWithProxy"
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="calculadora" />
<property name="interceptorNames">
<list>
<value>validate</value>
<value>logger</value>
</list>
</property>
</bean>

<!-- Interceptores -->
<bean id="validate" class="patterns.proxyAOP.ValidateArgumentsInterceptor" />
<bean id="logger" class="patterns.proxyAOP.LoggerArgumentsInterceptor" />
</beans>

Donde:

  • calculadora: Es la clase a envolver en el Proxy (RealSubject o target).
  • calculadoraWithProxy: Proxy Factory que crea el bean configurado como Proxy. En este bean deben fijarse en las propiedades configuradas:
    • target: Es una referencia al bean con id calculadora.
    • interceptorNames: Es una lista con los id de los beans que harán de interceptores. En este caso son dos los interceptores validate y logger.

Notas

  • Como podrán ver, usé las mismas clases del post Java Dynamic Proxy y sólo agregue los Interceptores donde se concentra la lógica de negocio solicitada.
  • Recuerden que como se trata de un Proxy Dinámico se necesita que CalculadoraImpl implemente una interfaz conocida, en este caso, Calculadora.
  • La clase ProxyFactoryBean posee varios atributos mas que permiten agregarle funcionalidad y configuraciones al proxy, échenle un vistazo.

Test Unitario

Para poder probar las funcionalidades realicé el siguiente test unitario:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({ "/proxy-application-context.xml" })
public class CalculadoraServiceAOPTest {

@Autowired
@Qualifier("calculadoraWithProxy")
private Calculadora _proxy;

@Before
public void before() {
System.out.println("----------------------------------");
assertNotNull("Calculadora cannot be null", _proxy);
}

@Test
public void calculatorBeanWithProxyFactory() {
Integer a = 1;
Integer b = 2;

Integer result = _proxy.suma(a, b);
assertNotNull(result);
assertEquals(Integer.valueOf(3), result);
}

@Test(expected = InvalidNumberValueException.class)
public void shouldFailCalculatorBeanWithProxyFactory() {
Integer a = -1;
Integer b = 2;
_proxy.suma(a, b);
}

}

Y finalmente la salida de la consola:

1
2
3
4
5
6
7
8
9
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running patterns.proxyAOP.CalculadoraServiceAOPTest
----------------------------------
Argument 1
Argument 2
----------------------------------
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.592 sec

Espero les sirva, comentarios bienvenidos sean.

Junit, ServletContextAware y MockServletContext

Se me generó el siguiente problema al hacer un test unitario sobre una clase que implementa la interfaz de Spring Framework ServletContextAware.

1
2
3
4
5
public interface ServletContextAware {

void setServletContext(ServletContext servletContext);

}

Mediante esta interfaz puedes inyectar el servletContext a la configuración de tu Bean, siempre y cuando tengas un contexto web funcionando.

He aquí el segundo problema, ¿ Cómo puedo tener el un contexto web dentro de un Junit ?. Puede que exista una mejor forma de hacerlo pero lo logré de la siguiente manera.

1
2
3
4
5
6
7
8
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" 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-3.0.xsd" xmlns:p="http://www.springframework.org/schema/p">

<bean class="org.springframework.mock.web.MockServletContext" />

</beans>

Con esto y usando la anotación Autowired funciona de pelos la inyección del ServletContext.

1
2
3
4
5
6
7
public class ReadFileFromWebinf {

@Autowired
private ServletContext _servletContext;

/*Se omite el resto de los métodos*/
}

Pero!!! cuando quise utilizar la interfaz ServletContextAware no funcionó y me quede con una propiedad en null.

1
2
3
4
5
6
7
8
9
10
public class ReadFileFromWebinf implement ServletContextAware {

private ServletContext _servletContext;

public void setServletContext(ServletContext servletContext){
_servletContext=servletContext;
};

/*Se omite el resto de los métodos*/
}

Buscando por todos lados encontré que a mi test unitario le faltaba un par de configuraciones para la lectura del contexto Spring.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "/test/test-context.xml" }
@TestExecutionListeners({ DependencyInjectionTestExecutionListener.class })
public class ReadFileFromWebinfTest {

@Autowired
private ReadFileFromWebinf _readFileFromWebinf;


public void testReadFile(){
String fileContent = _readFileFromWebinf.load("archivo.xml");
System.out.println(fileContent);
}
}

Las dos primeras anotaciones son para decirle a Spring que se va a utilizar Junit para ejecutar la clase y cual o cuales son los contextos a cargar.

Luego viene la anotación que me arregló el día, TestExecutionListeners y el listener asociado para la completar las inyecciones de dependencia de todos los beans. (Antes con Autowired solo hacía algunos).

Espero les sirva, cualquier duda bienvenida sea.

Spring Framework - InitializingBean

InitializingBean

Esta interfaz de Spring Framework te permite una vez configurado el contexto, ejecutar el método afterPropertiesSet(), y como su nombre lo dice, lo ejecuta luego de ejecutar los métodos setter del bean (post configurar el bean).

1
2
3
4
5
public interface InitializingBean {

void afterPropertiesSet() throws Exception;

}

Algunas aplicaciones de esta interfaz:

  • Validar que los atributos de un Bean estén correctamente inicializados.
  • A partir de los atributos ya setteados, generar otra propiedad en tiempo de configuración. Por ejemplo, se configura el bean con dos atributos de tipo int y en tiempo de configuración quiero calcular la suma de ambos y dejarlo en otra propiedad. ¿ Qué gano con esto ? que esa propiedad no se calcula cada vez que hago el getter sino que ya esta previamente calculada.

Para el primer caso, recomiendo utilizar la clase Assert ya que provee métodos con los que puedes invalidar que el contexto Spring levante si hay errores en la configuración.

Les dejo un código de ejemplo para que le echen un vistazo (le agregué la interfaz al ejemplo del post anterior)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class ExampleBean implements InitializingBean {

private Integer _randomValue;

private Integer _newValue;

public final Integer getRandomValue() {
return _randomValue;
}

public final Integer getNewValue() {
return _newValue;
}

public final void setRandomValue(Integer randomValue) {
_randomValue = randomValue;
}

public void afterPropertiesSet() throws Exception {
Assert.notNull(_randomValue, "randomValue properties cannot be null");
_newValue = _randomValue * 10;
}

}

Voy a explicar un poco y a groso modo el como funciona esta clase desde el punto de vista de Spring:

  • Se lee la configuración asociada a la clase ExampleBean
  • Se crea la instancia de la clase ExampleBean
  • Luego vía inyección se le settean los atributos, entre ellos randomValue
  • Luego de la creación del bean ExampleBean se ejecuta el método afterPropertiesSet(), en donde se realiza una validación del randomValue (este NO puede ser nulo, de serlo, el contexto de Spring no quedara operativo), si eso pasa OK, se calculará el atributo newValue y que será randomValue multipliado por 10.
  • Finalmente y si no hay errores en el contexto de Spring, se deja la instancia del bean ExampleBean en el contexto, es decir, el valor randomValue y newValue quedan listos para ser usados.

Este proceso se realiza sólo una vez (salvo que el scope no sea singleton) y es en el momento de la creación del contexto.

Demás esta decir que esta clase/bean (ExampleBean) debe estar configurada en el archivo del contexto de Spring o vía las anotaciones (metadata) provistas para eso.

¿Por que FactoryBean es útil?

En este post voy a tratar de explicar porque FactoryBean es increíblemente útil para alambrar (aplicar DI) aplicaciones hechas con Spring Framework.

La interfaz de FactoryBean dice lo siguiente:

1
2
3
4
5
6
7
8
public interface FactoryBean<T> {

T getObject() throws Exception;

Class getObjectType();

boolean isSingleton();
}

La utilidad de la implementación de esta interfaz y su posterior declaración como Bean dentro del contexto de la aplicación, es que puede ser usada (como su nombre lo indica) como Factory para inyectar algún valor en alguna propiedad de otro Bean.

¿Dónde y cómo funciona? Dentro del ciclo de vida de la carga del Contexto de spring, la ejecución de la implementación de esta interfaz, esta justo antes de hacer los setters de las propiedades de un Bean. El funcionamiento es simple, siempre se ejecutará el método getObject() que retornará la instancia del tipo T.

Veamos una implementación simple y una configuración de contexto de spring:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class RandomNumberFactoryBean implements FactoryBean<integer> {

public Integer getObject() throws Exception {
return Integer.valueOf(new Random().nextInt());
}

public Class getObjectType() {
return Integer.class;
}

public boolean isSingleton() {
return true;
}

}

Esta simple implementación de FactoryBean retorna un Integer en el método getObject(), simplemente eso (obviamente en este método debes poner todo tu talento para resolver el valor que andas buscando, esto es un ejemplo simple).

El método getObjectType() es usado internamente por el framework para validar que el retorno de getObject() sea correcto o que este dentro de la jerarquía de clases, esto es en caso de que quieras restringir por tipo.

El último método isSingleton() es si quieres que esta clase (no importa la cantidad de declaraciones que tengas, siempre será la misma), es un tanto dificil de entender pero les sugiero que lean sobre el concepto de Singleton que tiene SpringFramework. En el ejemplo de configuración espero que les quede claro, de lo contrario, bienvenidas las preguntas.

Esta es la definición del Bean de ejemplo, en donde se hará uso del FactoryBean:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class ExampleBean {

private Integer _randomValue;

public final Integer getRandomValue() {
return _randomValue;
}

public final void setRandomValue(Integer randomValue) {
_randomValue = randomValue;
}

}

Aquí las configuraciones de contexto de spring.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemalocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="exampleBean1" class="cl.pcollaog.factory.ExampleBean">
<property name="randomValue">
<bean class="cl.pcollaog.factory.RandomNumberFactoryBean" />
</property>
</bean>

<bean id="randomNumberFactoryBean" class="cl.pcollaog.factory.RandomNumberFactoryBean"/>

<bean id="exampleBean21" class="cl.pcollaog.factory.ExampleBean">
<property ref="randomNumberFactoryBean" name="randomValue"></property>
</bean>

<bean id="exampleBean22" class="cl.pcollaog.factory.ExampleBean">
<property ref="randomNumberFactoryBean" name="randomValue"></property>
</bean>

</beans>

Ejemplo 1:

Se declara el Bean exampleBean1 y que tiene como propiedad el atributo randomValue y cuyo valor será producto de la ejecución del Bean RandomNumberFactoryBean en el momento que el contexto de Spring es cargado. En este caso se utiliza la técnica de InnerBean, es decir, no se tiene una instancia del Bean para ser reutilizada sino que esta en la misma declaración del setter del atributo.

Ejemplo 2:

Este ejemplo hace lo mismo indicado arriba, sólo que no se aplica InnerBean sino que se extrae y es reutilizado en el bean exampleBean21 y exampleBean22. En este ejemplo entra en juego el método isSingleton(), si es true, quiere decir que el valor que entregará el FactoryBean siempre será el mismo para ambos Beans. Ahora bien, si se cambia a false, por cada setter, es decir, para exampleBean21 y exampleBean22 se realizara la ejecución de RandomNumberFactoryBean 2 veces. (En este caso, nos entregará dos valores aleatorios para cada bean)

En pocas palabras para cada Bean del ejemplo 2 habrá dos instancias de RandomNumberFactoryBean que entregará cada una un Integer para los atributos de los beans declarados.

Algo interesante de las implementaciones de FactoryBean es que luego de la carga del contexto, estas instancias son desechadas.

Espero les sea útil y cualquier pregunta sera bienvenida.