Spring framework 5 : AOP After throwing Advice


Como vimos en el post pasado https://devs4j.com/2019/02/28/spring-framework-5-aop-conceptos-basicos/ existen diferentes tipos de advices, en este post nos enfocaremos en After throwing Advice.

Para poder seguir estos ejemplos es necesario crear un proyecto spring boot simple.

Creación de un servicio de spring

El primer paso para entender como funcionan los advices será crear un servicio de spring, este objeto será nuestro Target object.

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

/**
 * @author raidentrance
 *
 */
@Service
public class HelloWorldService {

	private static final Logger log = LoggerFactory.getLogger(HelloWorldService.class);

	public void print() {
		log.info("Hello world");
		throw new IllegalStateException();
	}

}

Como vemos nuestro servicio es solo una clase llamada HelloWorldService con un método llamado print().

After throwing Advice

En este ejemplo interceptaremos las peticiones a la clase HelloWorldService en su método print utilizando un After Throwing Advice, veamos el siguiente ejemplo:

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

/**
 * @author raidentrance
 *
 */
@Aspect
@Component
public class AfterThrowingAdviceExample {
	
	private static final Logger log = LoggerFactory.getLogger(AfterThrowingAdviceExample.class);

	@AfterThrowing("execution(* com.devs4j.service.HelloWorldService.*(..))")
	public void logAfterThrowing(JoinPoint joinPoint) {
		log.info("After throwing example");
	}
}

Del código anterior podemos analizar los siguientes puntos:

  • La clase esta anotada con @Component y @Aspect esto permite a spring identificarlo como un bean y como un aspecto.
  • La anotación @AfterThrowing nos permite utilizar un After throwing advice.
  • Los advices reciben como parámetro un Pointcut el cual define los objetos que serán afectados por el Advice (Explicaremos Pointcut expression language en otro post).
  • El método recibe como parámetro un objeto que implementa la interfaz JoinPoint, esto nos permite acceder a información del JoinPoint que se interceptó.
  • Lo único que hace nuestro aspecto es imprimir el mensaje After throwing example
  • Los Advices After throwing se ejecutan después de que se ejecutó el método interceptado siempre y cuando haya habido una excepción.

Una vez que tenemos listo nuestro aspecto el siguiente paso será probarlo, para esto crearemos la siguiente clase:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

import com.devs4j.service.HelloWorldService;

@SpringBootApplication
public class Devs4jSpringAopApplication {

	public static void main(String[] args) {
		ConfigurableApplicationContext applicationContext = SpringApplication.run(Devs4jSpringAopApplication.class,
				args);
		HelloWorldService helloWorldService = applicationContext.getBean(HelloWorldService.class);
		helloWorldService.print();
	}

}

Del código anterior podemos analizar los siguientes puntos:

  • Obtenemos un bean del tipo HelloWorldService
  • Ejecutamos el método print()

Salida:

2019-03-01 12:53:58.076  INFO 76486 --- [           main] com.devs4j.service.HelloWorldService     : Hello world
2019-03-01 12:53:58.078  INFO 76486 --- [           main] c.devs4j.aop.AfterThrowingAdviceExample  : After throwing example
Exception in thread "main" java.lang.IllegalStateException
	at com.devs4j.service.HelloWorldService.print(HelloWorldService.java:21)
	at com.devs4j.service.HelloWorldService$$FastClassBySpringCGLIB$$c594d12f.invoke(<generated>)
	at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:749)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
	at org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:62)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
	at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:93)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688)
	at com.devs4j.service.HelloWorldService$$EnhancerBySpringCGLIB$$28652767.print(<generated>)
	at com.devs4j.Devs4jSpringAopApplication.main(Devs4jSpringAopApplication.java:16)

Como vemos se imprimió el mensaje After throwing example después de Hello world, esto nos indica que el aspecto que utiliza after throwing advice se ejecuta después del método print debido a que este arroja una IllegalStateException.

Para estar al pendiente sobre nuestro contenido nuevo síguenos en nuestras redes sociales https://www.facebook.com/devs4j/ y https://twitter.com/devs4j.

Autor: Alejandro Agapito Bautista
Twitter: @raidentrance
Contacto:raidentrance@gmail.com

Spring framework 5 : AOP After returning Advice


Como vimos en el post pasado https://devs4j.com/2019/02/28/spring-framework-5-aop-conceptos-basicos/ existen diferentes tipos de advices, en este post nos enfocaremos en After returning Advice.

Para poder seguir estos ejemplos es necesario crear un proyecto spring boot simple.

Creación de un servicio de spring

El primer paso para entender como funcionan los advices será crear un servicio de spring, este objeto será nuestro Target object.

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

/**
 * @author raidentrance
 *
 */
@Service
public class HelloWorldService {

	private static final Logger log = LoggerFactory.getLogger(HelloWorldService.class);

	public void print() {
		log.info("Hello world");
	}

}

Como vemos nuestro servicio es solo una clase llamada HelloWorldService con un método llamado print().

After returning Advice

En este ejemplo interceptaremos las peticiones a la clase HelloWorldService en su método print utilizando un After Returning Advice, veamos el siguiente ejemplo:

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

/**
 * @author raidentrance
 *
 */
@Aspect
@Component
public class AfterReturningAdviceExample {

	private static final Logger log = LoggerFactory.getLogger(AfterReturningAdviceExample.class);

	@AfterReturning("execution(* com.devs4j.service.HelloWorldService.*(..))")
	public void logAfterReturning(JoinPoint joinPoint) {
		log.info("After returning example");
	}

}

Del código anterior podemos analizar los siguientes puntos:

  • La clase esta anotada con @Component y @Aspect esto permite a spring identificarlo como un bean y como un aspecto.
  • La anotación @AfterReturning nos permite utilizar un After returning advice.
  • Los advices reciben como parámetro un Pointcut el cual define los objetos que serán afectados por el Advice (Explicaremos Pointcut expression language en otro post).
  • El método recibe como parámetro un objeto que implementa la interfaz JoinPoint, esto nos permite acceder a información del JoinPoint que se interceptó.
  • Lo único que hace nuestro aspecto es imprimir el mensaje After returning example
  • Los Advices After returning se ejecutan después de que se ejecutó el método interceptado siempre y cuando no haya habido una excepción.

Una vez que tenemos listo nuestro aspecto el siguiente paso será probarlo, para esto crearemos la siguiente clase:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

import com.devs4j.service.HelloWorldService;

@SpringBootApplication
public class Devs4jSpringAopApplication {

	public static void main(String[] args) {
		ConfigurableApplicationContext applicationContext = SpringApplication.run(Devs4jSpringAopApplication.class,
				args);
		HelloWorldService helloWorldService = applicationContext.getBean(HelloWorldService.class);
		helloWorldService.print();
	}

}

Del código anterior podemos analizar los siguientes puntos:

  • Obtenemos un bean del tipo HelloWorldService
  • Ejecutamos el método print()

Salida:

2019-03-01 10:57:37.307  INFO 72532 --- [           main] com.devs4j.service.HelloWorldService     : Hello world
2019-03-01 10:57:37.308  INFO 72532 --- [           main] c.d.aop.AfterReturningAdviceExample      : After returning example

Como vemos se imprimió el mensaje After returning example después de Hello world, esto nos indica que el aspecto que utiliza after returning advice se ejecuta después del método print que se invocó de la clase HelloWorldService.

Para estar al pendiente sobre nuestro contenido nuevo síguenos en nuestras redes sociales https://www.facebook.com/devs4j/ y https://twitter.com/devs4j.

Autor: Alejandro Agapito Bautista
Twitter: @raidentrance
Contacto:raidentrance@gmail.com

Spring framework 5 : AOP Conceptos básicos


AOP (Aspect oriented programming) es un paradigma de programación al igual que la POO (Programación orientada a objetos), en la POO el punto principal de la programación es un objeto mientras que en AOP es un aspecto.

Los aspectos nos ayudan a solucionar problemas llamados crosscutting concerns, para poder adentrarnos a esto primero debemos entender algunos conceptos básicos de AOP:

  • Aspect : Un aspecto es una preocupación o funcionalidad que se aplica a multiples clases, el logging es un buen ejemplo.
  • Join point : Un punto durante la ejecución de un programa como la ejecución de un método o el manejo de una excepción, en Spring Aop siempre será la ejecución de un método.
  • Advice : Será la acción a tomar en un join point específico, existen diferentes tipos de advice “around”, “before” y “after”, se pueden considerar como “interceptores” de nuestros join points.
  • Pointcut : Un predicado que junta un conjunto de join points, para definir este predicado se utiliza AspectJ pointcut expression language.
  • Target object : Un objeto advised por uno o más aspectos, como Spring Aop utiliza proxys este siempre será un objeto proxied.
  • Aop proxy : Un objeto creado por el framework Aop para dar el soporte Aop, en Spring Aop se utiliza proxies dinámicos a través de CGLIB.
  • Weaving : Es el enlace entre los aspectos y los objetos, este se puede hacer de diferentes formas, Spring Aop lo hace en tiempo de ejecución.

Tipos de Advices

Como se menciono anteriormente existen diferentes tipos de advices:

  • Before advice : Este advice se ejecuta antes del join point, no tiene la habilidad de detener la ejecución a menos de que arroje una excepción.
  • After returning : Este advice se ejecuta después del join point, no se ejecuta si hay una excepción.
  • After throwing : Este advise se ejecuta si el método termina por una excepción.
  • After finally : Este advise se ejecuta si el método termina su ejecución de forma normal o por una excepción.
  • Around advice : Este advise rodea la ejecución de un método, puede ejecutar su comportamiento antes, después o incluso si se produce una excepción. También es responsable de determinar si quiere o no proceder con la invocación del método.

Around advice es el advice más poderoso pero es el más general, por esto se recomienda utilizar el más especifico para resolver el problema que necesitamos.

Usos prácticos

Algunos usos prácticos para el uso de Spring Aop son :

  • Logging
  • Seguridad
  • Administración de transacciones
  • Monitoreo de performance
  • Caching
  • Manejo de errores

Para estar al pendiente sobre nuestro contenido nuevo síguenos en nuestras redes sociales https://www.facebook.com/devs4j/ y https://twitter.com/devs4j.

Autor: Alejandro Agapito Bautista
Twitter: @raidentrance
Contacto:raidentrance@gmail.com

Spring framework 5 : Spring Expression Language SpEL


Spring expression language (SpEL) es un lenguaje de expresiones que permite realizar operaciones sobre la información en tiempo de ejecución, en el post anterior se utilizó para leer información de un archivo .properties, en este post hablaremos más sobre expression language en detalle.

Los operadores disponibles en SpEL son los siguientes:

Operadores aritmeticos+, -, *, /, %, ^, div, mod
Relacionales <, >, ==, !=, <=, >=, lt, gt, eq, ne, le, ge
Lógicosand, or, not, &&, ||, !
Condicionales?:
Expresiones regularesMatchers

A continuación mostraremos como utilizarlos en una aplicación de spring.

SpelExpressionParser

Antes de iniciar, el primer paso será aprender a utilizar la clase SpelExpressionParser la cual nos permite evaluar expresiones sin necesidad de iniciar el contexto de Spring framework, a continuación se muestra como crearlo:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;

/**
 * @author raidentrance
 *
 */
public class SpelExpressionApplication {

	private static final Logger log = LoggerFactory.getLogger(SpelExpressionApplication.class);

	public static void main(String[] args) {
		ExpressionParser expressionParser = new SpelExpressionParser();
		Expression expression = expressionParser.parseExpression("'Hi from devs4j'");
		log.info("String expression {}",expression.getValue());
		
	}
}

Analizando el código anterior podemos ver lo siguiente :

  • SpelExpressionParser nos permite evaluar expresiones de spring SpEL
  • El método Expression parseExpression(String expression) recibe como parámetro una expresión de spring y devuelve un objeto de tipo Expression.
  • La clase Expression contiene un método llamado Object getValue() el cuál devuelve el resultado de la expresión.

Ejemplos básicos de expresiones

Una vez que entendimos como evaluar expresiones SpEL, el siguiente paso será ver algunos ejemplos:

Uso de Strings

public static void main(String[] args) {
	ExpressionParser expressionParser = new SpelExpressionParser();
	Expression expression = expressionParser.parseExpression("'Hi'.concat('from devs4j')");
	log.info("Concat expression {}", expression.getValue());
	expression = expressionParser.parseExpression("'Hi from devs4j'.toUpperCase()");
	log.info("To upper case expression {}", expression.getValue());
	expression = expressionParser.parseExpression("'Hi from devs4j'.length()");
	log.info("Get length expression {}", expression.getValue());

}

La expresión anterior concatena los Strings “Hi” y “from devs4j” al ejecutarlo el resultado será:

11:05:36.022 [main] INFO com.devs4j.spel.SpelExpressionApplication - Concat expression Hifrom devs4j
11:05:36.026 [main] INFO com.devs4j.spel.SpelExpressionApplication - To upper case expression HI FROM DEVS4J
11:05:36.026 [main] INFO com.devs4j.spel.SpelExpressionApplication - Get length expression 14

Operadores aritméticos

private static final Logger log = LoggerFactory.getLogger(SpelExpressionApplication.class);

public static void main(String[] args) {
	ExpressionParser expressionParser = new SpelExpressionParser();
	Expression expression = expressionParser.parseExpression("10 + 20");
	log.info("Arithmetic expression {}", expression.getValue());

	expression = expressionParser.parseExpression("10 - 20");
	log.info("Arithmetic expression {}", expression.getValue());

	expression = expressionParser.parseExpression("10 * 20");
	log.info("Arithmetic expression {}", expression.getValue());

	expression = expressionParser.parseExpression("20 / 10");
	log.info("Arithmetic expression {}", expression.getValue());

	expression = expressionParser.parseExpression("20 mod 10");
	log.info("Arithmetic expression {}", expression.getValue());

	expression = expressionParser.parseExpression("2 ^ 3");
	log.info("Arithmetic expression {}", expression.getValue());

	expression = expressionParser.parseExpression("(2 ^ 3) * 2 + 24");
	log.info("Arithmetic expression {}", expression.getValue());

}

La salida del código anterior sería:

11:17:42.464 [main] INFO com.devs4j.spel.SpelExpressionApplication - Arithmetic expression 30
11:17:42.468 [main] INFO com.devs4j.spel.SpelExpressionApplication - Arithmetic expression -10
11:17:42.469 [main] INFO com.devs4j.spel.SpelExpressionApplication - Arithmetic expression 200
11:17:42.469 [main] INFO com.devs4j.spel.SpelExpressionApplication - Arithmetic expression 2
11:17:42.470 [main] INFO com.devs4j.spel.SpelExpressionApplication - Arithmetic expression 0
11:17:42.470 [main] INFO com.devs4j.spel.SpelExpressionApplication - Arithmetic expression 8
11:17:42.470 [main] INFO com.devs4j.spel.SpelExpressionApplication - Arithmetic expression 40

Operadores relacionales

public static void main(String[] args) {
	ExpressionParser expressionParser = new SpelExpressionParser();
	Expression expression = expressionParser.parseExpression("10 < 20");
	log.info("Relational expression {}", expression.getValue());

	expression = expressionParser.parseExpression("10 > 20");
	log.info("Relational expression {}", expression.getValue());

	expression = expressionParser.parseExpression("10 == 20");
	log.info("Relational expression {}", expression.getValue());

	expression = expressionParser.parseExpression("10 != 20");
	log.info("Relational expression {}", expression.getValue());

}

Salida del código anterior:

11:19:59.557 [main] INFO com.devs4j.spel.SpelExpressionApplication - Relational expression true
11:19:59.561 [main] INFO com.devs4j.spel.SpelExpressionApplication - Relational expression false
11:19:59.562 [main] INFO com.devs4j.spel.SpelExpressionApplication - Relational expression false
11:19:59.562 [main] INFO com.devs4j.spel.SpelExpressionApplication - Relational expression true

Operadores lógicos

public static void main(String[] args) {
	ExpressionParser expressionParser = new SpelExpressionParser();
	Expression expression = expressionParser.parseExpression("100 > 20 && 100 < 10000");
	log.info("Relational expression {}", expression.getValue());

	expression = expressionParser.parseExpression("100 < 20 || 100 < 10000");
	log.info("Relational expression {}", expression.getValue());

	expression = expressionParser.parseExpression("!(100 < 20) ");
	log.info("Relational expression {}", expression.getValue());
}

Salida:

11:22:29.569 [main] INFO com.devs4j.spel.SpelExpressionApplication - Relational expression true
11:22:29.573 [main] INFO com.devs4j.spel.SpelExpressionApplication - Relational expression true
11:22:29.573 [main] INFO com.devs4j.spel.SpelExpressionApplication - Relational expression true

Operadores condicionales

private static final Logger log = LoggerFactory.getLogger(SpelExpressionApplication.class);

public static void main(String[] args) {
	ExpressionParser expressionParser = new SpelExpressionParser();
	Expression expression = expressionParser.parseExpression("(10 ==20) ? 'Hi':'Adios'");
	log.info("Conditional expression {}", expression.getValue());
}

Salida:

11:24:44.866 [main] INFO com.devs4j.spel.SpelExpressionApplication - Relational expression Adios

Uso de expresiones regulares en SpEL

Veamos el siguiente modelo:

class User {
	private String username;
	private String password;

	public User(String username, String password) {
		this.username = username;
		this.password = password;
	}

	// Getters y setters
}
public static void main(String[] args) {

	User user = new User("raidentrance", "devs4jRules");
	
	ExpressionParser expressionParser = new SpelExpressionParser();
	Expression expression = expressionParser.parseExpression("username matches '[a-zA-Z]+'");
	log.info("Regex expression {}", expression.getValue(user));
	
	expression = expressionParser.parseExpression("password matches '[a-zA-Z]+'");
	log.info("Regex expression {}", expression.getValue(user));
}

Salida:

11:37:49.944 [main] INFO com.devs4j.spel.SpelExpressionApplication - Regex expression true
11:37:49.947 [main] INFO com.devs4j.spel.SpelExpressionApplication - Regex expression false

Estos son solo algunos ejemplos sobre el uso de Expression language en Spring, cabe aclarar que es posible utilizarlo también con la anotación @Value(String value) lo cual lo hace un lenguaje muy poderoso y fácil de utilizar en Spring.

Para estar al pendiente sobre nuestro contenido nuevo síguenos en nuestras redes sociales

 https://www.facebook.com/devs4j/ 

https://twitter.com/devs4j.

Autor: Alejandro Agapito Bautista
Twitter: @raidentrance
Contacto:raidentrance@gmail.com

Spring framework 5 : Leer información de archivos .properties


Una tarea común al hacer aplicaciones utilizando spring es leer configuraciones de archivos de tipo .properties, en este ejemplo tomaremos como base el post Spring framework 5 : Uso de @Autowire para listas de objetos y lo modificaremos para leer el valor de las figuras de un archivo properties en lugar de inyectarlo directamente.

Paso 1 Creación del archivo properties

El primer paso será crear el archivo .properties que contendrá los valores de radio, ancho, largo y lado de las figuras que construiremos:

/src/main/resources/areas.properties

circle.radius = 10.0
rectangle.width = 10.0
rectangle.height = 5.0
square.side=10.0

Paso 2 Carga de properties a spring

Una vez que se creó el archivo de propiedades el siguiente paso será cargar esas propiedades a spring para esto crearemos la siguiente clase de configuración:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;

/**
 * @author raidentrance
 *
 */
@Configuration
@PropertySource("classpath:areas.properties")
public class FigurePropertyCopnfiguration {

	@Bean
	public PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
		return new PropertySourcesPlaceholderConfigurer();
	}
}

Como se puede ver en la anotación @PropertySource se define el archivo .properties del cuál se cargaran las propiedades.

Paso 3 Modificar los beans para utilizar las propiedades

Una vez que se cargaron las propiedades a spring el siguiente paso será modificar nuestras clases, en el ejemplo anterior se utilizó @Value(“10.0”) lo cual inyectaba el valor de 10 a la referencia, ahora en lugar de hacer eso haremos un @Value(“${circle.radius:0}”) lo cual tomará el valor de la propiedad circle.radius y lo inyectará en nuestra variable, veamos como queda el código:

/**
 * @author raidentrance
 *
 */
public abstract class Figure {

	public abstract double getArea();

}
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

/**
 * @author raidentrance
 *
 */
@Component
public class Circle extends Figure {

	private double radius;

	private static final Logger log = LoggerFactory.getLogger(Circle.class);

	public Circle(@Value("${circle.radius:0}") double radius) {
		this.radius = radius;
	}

	@Override
	public double getArea() {
		log.info("Calculating the are of a circle with radius {}", radius);
		return Math.pow(Math.PI * radius, 2);
	}

}
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

/**
 * @author raidentrance
 *
 */
@Component
public class Rectangle extends Figure {

	private double width;

	private double height;

	private static final Logger log = LoggerFactory.getLogger(Rectangle.class);

	public Rectangle(@Value("${rectangle.width:0}") double width, @Value("${rectangle.height:0}") double height) {
		this.width = width;
		this.height = height;
	}

	@Override
	public double getArea() {
		log.info("Calculating the are of a rectangle with with {} and height", width, height);
		return width * height;
	}

}
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

/**
 * @author raidentrance
 *
 */
@Component
public class Square extends Figure {

	private double side;
	
	private static final Logger log = LoggerFactory.getLogger(Square.class);

	public Square(@Value("${square.side:0}") double side) {
		this.side = side;
	}

	@Override
	public double getArea() {
		log.info("Calculating the are of a square with side {}", side);
		return Math.pow(side, 2);
	}

}

La siguiente sintaxis @Value(“${square.side:0}”) significa toma el valor de la propiedad square.side, en caso de que no exista se asignará el valor 0.

El uso de propiedades es muy común debido a que es posible cambiar las configuraciones del código sin tener que re compilar el código.

Para estar al pendiente sobre nuestro contenido nuevo síguenos en nuestras redes sociales https://www.facebook.com/devs4j/ y https://twitter.com/devs4j.

Autor: Alejandro Agapito Bautista
Twitter: @raidentrance
Contacto:raidentrance@gmail.com

Spring framework 5 : Uso de @Autowire para listas de objetos


En ejemplos anteriores se explicó como inyectar objetos en spring utilizando la anotación @Autowired para objetos simples, en este post explicaremos como inyectar listas de objetos.

Paso 1 Crear las clases a inyectar

El primer paso será crear una lista de clases a inyectar, en este caso tendremos una clase padre y multiples clases hijas, veamos el siguiente código:

/**
 * @author raidentrance
 *
 */
public abstract class Figure {

	public abstract double getArea();

}
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

/**
 * @author raidentrance
 *
 */
@Component
public class Circle extends Figure {

	private double radius;

	private static final Logger log = LoggerFactory.getLogger(Circle.class);

	public Circle(@Value("10.0") double radius) {
		this.radius = radius;
	}

	@Override
	public double getArea() {
		log.info("Calculating the are of a circle with radius {}", radius);
		return Math.pow(Math.PI * radius, 2);
	}

}
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

/**
 * @author raidentrance
 *
 */
@Component
public class Square extends Figure {

	private double side;
	
	private static final Logger log = LoggerFactory.getLogger(Square.class);

	public Square(@Value("10.0") double side) {
		this.side = side;
	}

	@Override
	public double getArea() {
		log.info("Calculating the are of a square with side {}", side);
		return Math.pow(side, 2);
	}

}
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

/**
 * @author raidentrance
 *
 */
@Component
public class Rectangle extends Figure {

	private double width;

	private double height;

	private static final Logger log = LoggerFactory.getLogger(Rectangle.class);

	public Rectangle(@Value("10.0") double width, @Value("5.0") double height) {
		this.width = width;
		this.height = height;
	}

	@Override
	public double getArea() {
		log.info("Calculating the are of a rectangle with with {} and height", width, height);
		return width * height;
	}

}

Se crearon las siguientes clases:

  • Figure : Clase abstracta que será padre de las demás clases, define el método abstracto double getArea()
  • Circle : Define el atributo radio y utiliza la anotación @Value para asignarle el valor de 10.0
  • Square : Define el atributo lado y utiliza la anotación @Value para asignarle el valor de 10.0
  • Rectangle : Define los atributos ancho y alto y utiliza la anotación @Value para asignarles el valor de 10.0 y 5.0.

Las 3 implementaciones Circle, Square y Rectangle implementan el método getArea() definido en la clase Figure.

Inyectando los beans en una referencia de tipo List

Una vez que definimos los beans con sus valores el siguiente paso será inyectarlos en una referencia de tipo List, veamos la siguiente clase:

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * @author raidentrance
 *
 */
@Component
public class AreaCalculator {

	@Autowired
	private List<Figure> figures;

	public double getSumOfAreas() {
		double totalArea = 0.0;
		for (Figure figure : figures) {
			totalArea += import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * @author raidentrance
 *
 */
@Component
public class AreaCalculator {

	@Autowired
	private List<Figure> figures;

	public double getSumOfAreas() {
		double totalArea = 0.0;
		for (Figure figure : figures) {
			totalArea += figure.getArea();
		}
		return totalArea;
	}
}
.getArea();
		}
		return totalArea;
	}
}

Como se puede ver la clase AreaCalculator tiene un método que suma el área de todas las figuras, para esto debe inyectar un List<Figure>, con esto Spring inyectará todas las implementaciones de Figure.

Ejecutando la aplicación

El último paso será modificar la clase aplicación para ejecutar el método getSumOfAreas() como se muestra en la siguiente clase:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

import com.devs4j.lists.AreaCalculator;

@SpringBootApplication
public class Devs4jSpringCoreApplication {

	public static void main(String[] args) {
		ConfigurableApplicationContext applicationContext = SpringApplication.run(Devs4jSpringCoreApplication.class,
				args);

		AreaCalculator areaCalculator = applicationContext.getBean(AreaCalculator.class);
		System.out.println(areaCalculator.getSumOfAreas());
	}

}

Al ejecutarlo la salida será la siguiente :

2019-02-07 12:01:50.366  INFO 96221 --- [           main] com.devs4j.Devs4jSpringCoreApplication   : Started Devs4jSpringCoreApplication in 1.224 seconds (JVM running for 1.501)
 2019-02-07 12:01:50.368  INFO 96221 --- [           main] com.devs4j.lists.Circle                  : Calculating the are of a circle with radius 10.0
 2019-02-07 12:01:50.369  INFO 96221 --- [           main] com.devs4j.lists.Rectangle               : Calculating the are of a rectangle with with 10.0 and height
 2019-02-07 12:01:50.369  INFO 96221 --- [           main] com.devs4j.lists.Square                  : Calculating the are of a square with side 10.0
 1136.960440108936

Para estar al pendiente sobre nuestro contenido nuevo síguenos en nuestras redes sociales https://www.facebook.com/devs4j/ y https://twitter.com/devs4j.

Autor: Alejandro Agapito Bautista
Twitter: @raidentrance
Contacto:raidentrance@gmail.com

Spring framework 5 : Definiendo un perfil por defecto


En el post anterior aprendimos como crear e utilizar perfiles utilizando spring, ahora toca el turno de aprender como definir un perfil por defecto.

Definiendo los servicios a utilizar

/**
 * 
 * @author raidentrance
 *
 */
public interface EnvironmentService {

	String getEnvironmentName();

}
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;

/**
 * @author raidentrance
 *
 */
@Service
@Profile({"dev","default"})
public class DevEnvironmentService implements EnvironmentService {
	
	@Override
	public String getEnvironmentName(){
		return "dev";
	}
	
}
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;

/**
 * @author raidentrance
 *
 */
@Service
@Profile("qa")
public class QaEnvironmentService implements EnvironmentService {

	@Override
	public String getEnvironmentName() {
		return "qa";
	}

}
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;

/**
 * @author raidentrance
 *
 */
@Service
@Profile("prod")
public class ProdEnvironmentService implements EnvironmentService{

	@Override
	public String getEnvironmentName() {
		return "prod";
	}

}

Como vemos tenemos una interfaz llamada EnvironmentService.java la cual cuenta con 3 implementaciones DevEnvironmentService.java, QaEnvironmentService.java y ProdEnvironmentService.java, estos servicios están anotados con @Profile indicando el perfil de acuerdo al cual se crearán.

Solo tenemos una particularidad en la clase DevEnvironmentService podemos ver que la anotación se ve del siguiente modo:

.....
@Profile({"dev","default"})
public class DevEnvironmentService implements EnvironmentService {
...

Esto significa que el bean se activará con el perfil de dev o si no se especifica ningún otro perfil.

Ejecutando la aplicación

Para probar la aplicación ejecutaremos la siguiente clase:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

import com.devs4j.datasource.EnvironmentService;

@SpringBootApplication
public class SpringExampleApplication {

	public static void main(String[] args) {
		ConfigurableApplicationContext applicationContext = SpringApplication.run(SpringExampleApplication.class, args);
		EnvironmentService service = applicationContext.getBean(EnvironmentService.class);
		System.out.printf("Active environment %s \n", service.getEnvironmentName());
	}

}

Como podemos ver no especificamos ningún perfil en el archivo application.properties, y a pesar de eso al ejecutar nuestra aplicación veremos la siguiente salida:

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.1.2.RELEASE)

2019-01-30 21:58:20.406  INFO 16889 --- [           main] com.devs4j.SpringExampleApplication      : Starting SpringExampleApplication on MacBook-Pro-de-Alejandro.local with PID 16889 (/Users/raidentrance/Documents/workspace-sts-3.8.3.RELEASE/spring-example/target/classes started by raidentrance in /Users/raidentrance/Documents/workspace-sts-3.8.3.RELEASE/spring-example)
2019-01-30 21:58:20.409  INFO 16889 --- [           main] com.devs4j.SpringExampleApplication      : No active profile set, falling back to default profiles: default
2019-01-30 21:58:21.069  INFO 16889 --- [           main] com.devs4j.SpringExampleApplication      : Started SpringExampleApplication in 1.04 seconds (JVM running for 1.438)
Active environment dev 

Esto nos muestra que el perfil que se activó fue el perfil por defecto.

Para estar al pendiente sobre nuestro contenido nuevo síguenos en nuestras redes sociales  https://www.facebook.com/devs4j/ y https://twitter.com/devs4j.

Autor: Alejandro Agapito Bautista
Twitter: @raidentrance
Contacto:raidentrance@gmail.com