Spring framework 5 : AOP Before 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 Before 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().

Before Advice

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

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

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

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

	@Before("execution(* com.devs4j.service.HelloWorldService.print*(..))")
	public void logBefore(JoinPoint joinPoint) {
		log.info("Before advice 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 @Before nos permite utilizar un Before 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 Before advice example

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:35:26.624  INFO 71997 --- [           main] com.devs4j.aop.BeforeAdviceExample       : Before advice example
2019-03-01 10:35:26.632  INFO 71997 --- [           main] com.devs4j.service.HelloWorldService     : Hello world

Como vemos se imprimió el mensaje Before advice example antes de Hello world, esto nos indica que el aspecto que utiliza before advice se ejecuta antes 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 : Bean Aware interfaces


Aware interface significa en español interfaz consciente, este tipo de interfaces actúan como listeners para ciertos eventos que suceden en Spring framework.

Las siguientes son las Aware interfaces en Spring framework:

Aware interfaceRecurso objetivo
BeanNameAwareEl nombre del bean de las instancias configuradas
en el contenedor
BeanFactoryAwareEl bean factory actual a través del cual se invoca el
contenedor
ApplicationContextAwareEl application context actual a través del cual
puedes invocar el contenedor
MessageSourceAwareEl message source a través del cual se pueden
resolver mensajes
ApplicationeventPublisherAwareEl even publisher a través del cual se pueden publicar eventos de la aplicación
ResourceLoaderAwareEl resource loader a través del cual puedes cargar recursos externos
EnvironmentAwareLa instancia de Environment asociada con el ApplicationContext

Las Aware Interfaces definen métodos setters los cuales se invocan por Spring después de que las propiedades de Spring se asignaron pero antes de que los método callback (postConstruct y preDestroy) se invoquen.

Ejemplo utilizando BeanNameAware

Para entender como funcionan las aware interfaces crearemos el siguiente bean:

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

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

@Component
public class UserService implements BeanNameAware {

	@Value("fakeUser")
	private String user;

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

	@PostConstruct
	public void init() {
		log.info("Post construct callback {}",user);
	}

	@Override
	public void setBeanName(String name) {
		log.info("Being aware of {} {}", name,user);
	}

	@PreDestroy
	public void destroy() {
		log.info("Pre destroy callback");
	}
}

Al ejecutar la aplicación tendremos la siguiente salida:

2019-02-14 16:01:55.319  INFO 16934 --- [           main] com.devs4j.Devs4jSpringCoreApplication   : Starting Devs4jSpringCoreApplication on m-C02RV1WXG8WP with PID 16934 (/Users/maagapi/Documents/workspaces/devs4j/devs4j-spring-core/target/classes started by maagapi in /Users/maagapi/Documents/workspaces/devs4j/devs4j-spring-core)
2019-02-14 16:01:55.321  INFO 16934 --- [           main] com.devs4j.Devs4jSpringCoreApplication   : No active profile set, falling back to default profiles: default
2019-02-14 16:01:55.686  INFO 16934 --- [           main] com.devs4j.aware.UserService             : Being aware of userService fakeUser
2019-02-14 16:01:55.687  INFO 16934 --- [           main] com.devs4j.aware.UserService             : Post construct callback fakeUser
2019-02-14 16:01:55.833  INFO 16934 --- [           main] com.devs4j.Devs4jSpringCoreApplication   : Started Devs4jSpringCoreApplication in 0.816 seconds (JVM running for 1.11)
2019-02-14 16:01:55.836  INFO 16934 --- [       Thread-2] com.devs4j.aware.UserService             : Pre destroy callback

Este tipo de interfaces se utilizan para logging o para realizar integraciones con código legado, en la mayoría de los casos debemos evitar utilizarlas debido a que acoplan nuestra aplicación a Spring framework.

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