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 : 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

Spring framework 5 : Uso de perfiles en Spring


Cuando trabajamos con Spring framework nos encontraremos con momentos en los que tenemos que activar o desactivar beans de acuerdo a ciertas circunstancias, por ejemplo diferentes ambientes utilizan diferentes bases de datos, diferentes países utilizan diferentes idiomas, etc.

Definición de servicios

Para mostrar el funcionamiento de los perfiles en Spring crearemos los siguientes servicios de spring de ejemplo:

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

	String getEnvironmentName();

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

/**
 * @author raidentrance
 *
 */
@Service
@Profile("dev")
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.

Utilizando el Bean

Una vez que se definieron los beans el siguiente paso será utilizarlos, para esto utilizaremos el siguiente código:


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());
	}

}

Al ejecutar el código anterior tendremos la siguiente salida:

Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.devs4j.datasource.EnvironmentService' available

Como vemos el error nos dice que no asignamos un qualifier al obtener la referencia de EnvironmentService, en lugar de hacerlo lo que haremos será definir un perfil de spring.

Definiendo el perfil

Como recordamos del primer post https://devs4j.com/2019/01/29/spring-framework-5-creando-un-proyecto-de-spring/ existe un archivo llamado application.properties, en el cual podremos definir el perfil bajo el cual se ejecutará nuestra aplicación, para hacerlo le agregaremos la siguiente línea:

application.properties

spring.profiles.active=dev

La línea anterior indica que el perfil que se activará será el perfil de desarrollo, una vez hecho esto ejecutaremos de nuevo nuestra aplicación y veremos lo siguiente:

2019-01-30 21:23:47.416  INFO 16798 --- [           main] com.devs4j.SpringExampleApplication      : Starting SpringExampleApplication on MacBook-Pro-de-Alejandro.local with PID 16798 (/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:23:47.423  INFO 16798 --- [           main] com.devs4j.SpringExampleApplication      : The following profiles are active: dev
2019-01-30 21:23:48.158  INFO 16798 --- [           main] com.devs4j.SpringExampleApplication      : Started SpringExampleApplication in 1.253 seconds (JVM running for 1.641)
Active environment dev 

Como vemos el perfil se activó correctamente y al final nos imprime Active environment dev.

¿Qué pasa si inyecto el bean utilizando su implementación?

Como vimos activamos el perfil de dev y por esto al obtener una referencia de tipo EnvironmentService.java obtuvimos una de tipo DevEnvironmentService.java, pero, ¿Qué pasa si hacemos lo siguiente? :

ProdEnvironmentService prodService = applicationContext.getBean(ProdEnvironmentService.class);

Como vemos el perfil activo es dev, pero estamos tratando de obtener el bean del tipo ProdEnvironmentService.java. Al hacerlo nos podemos dar cuenta que obtendremos el siguiente error:

Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.devs4j.datasource.ProdEnvironmentService' available

Esto es debido a que el bean no se creó por spring y no se agregó al contexto dado que no tiene el perfil indicado.

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 la anotación @Qualifier


En el post anterior vimos como utilizar dependency injection Spring framework 5 : Dependency Injection utilizando Spring ahora es turno de analizar el uso de la anotación @Qualifier y como nos ayuda al momento de utilizar Spring framework.

Definiendo los servicios de spring

En el post anterior vimos como funciona Dependency Injection y como utilizarlo entre diferentes Beans, ahora veamos el siguiente problema para entender el funcionamiento de la anotación @Qualifier :

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

	public boolean authenticate(String username, String password);
}

Como vemos tenemos una interfaz llamada User service con el método authenticate el cual recibe username y password y devuelve un boolean indicando si se logro autenticar exitosamente.

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

/**
 * @author raidentrance
 *
 */
@Service
public class UserServiceDatabaseImpl implements UserService {

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

	@Override
	public boolean authenticate(String username, String password) {
		log.info("authenticating against the database");
		return false;
	}

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

/**
 * @author raidentrance
 *
 */
@Service
public class UserServiceActiveDirectoryImpl implements UserService {

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

	@Override
	public boolean authenticate(String username, String password) {
		log.info("authenticating against the active directory ");
		return false;
	}

}

Ahora podemos ver que se tienen 2 implementaciones, una llamada UserServiceDatabaseImpl.java y la otra UserServiceActiveDirectoryImpl.java una puede autenticar utilizando una base de datos y la otra a través de active directory, para este ejemplo simplemente escribiremos en el log para no sobre complicar el ejemplo.

Utilizando los servicios de spring

Una vez que tenemos los dos servicios definidos el siguiente paso será utilizarlos, veamos la siguiente implementación:


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

/**
 * @author raidentrance
 *
 */
@Controller
public class UserController {
	
	@Autowired
	private  UserService userService;
	
	public boolean authenticate(String username, String password) {
		return userService.authenticate(username, password);
	}
}

Si ejecutamos la aplicación utilizando la implementación de UserController anterior tendremos el siguiente error:

Field userService in com.devs4j.qualifier.UserController required a single bean, but 2 were found:
	- userServiceActiveDirectoryImpl: defined in file [/Users/maagapi/Documents/workspaces/devs4j/devs4j-spring-core/target/classes/com/devs4j/qualifier/UserServiceActiveDirectoryImpl.class]
	- userServiceDatabaseImpl: defined in file [/Users/maagapi/Documents/workspaces/devs4j/devs4j-spring-core/target/classes/com/devs4j/qualifier/UserServiceDatabaseImpl.class]

Esto se debe a que utilizamos la interfaz como referencia al bean pero existen 2 implementaciones, como vemos se hace inyección por tipo así que spring no tiene forma de saber que bean se inyectará en el atributo userService;

Para resolver el problema anterior debemos modificar la clase UserController.java como se muestra a continuación:


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;

/**
 * @author raidentrance
 *
 */
@Controller
public class UserController {
	
	@Autowired
	@Qualifier("userServiceDatabaseImpl")
	private  UserService userService;
	
	public boolean authenticate(String username, String password) {
		return userService.authenticate(username, password);
	}
}

Como vemos la anotación @Qualifier nos permite especificar el nombre del bean que se va a inyectar en el atributo.

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 : Conceptos básicos sobre DI (Dependency Injection)


En el post anterior se explicó como crear un proyecto de Spring framework 5 desde cero https://devs4j.com/2019/01/28/spring-framework-5-creando-un-proyecto-de-spring, ahora toca el turno de hablar sobre inyección de dependencias en Spring framework 5.

Introducción

La definición más simple de Dependency Injection es cuando una un objeto necesita una dependencia y esta es inyectada por otro objeto, la clase que necesita la dependencia no es responsable sobre la creación del objeto.

Veamos el siguiente ejemplo:

Las clases UserDao y RoleDao necesitan un objeto de tipo Datasource, si no se utilizara inyección de dependencias, ambas clases deberían crear su propia instancia del objeto Datasource y si en el futuro es necesario actualizar la contraseña de la base de datos o incluso el motor de base de datos ese cambio se debería hacer en ambos Daos, lo cual reduce la mantenibilidad de la aplicación.

Haciendo uso de Dependency Injection un componente puede crear el objeto de tipo Datasource solo una vez e inyectarlo en todos los componentes que lo necesiten, esto nos ayuda a que los objetos de tipo UserDao y RoleDao no tengan la necesidad de crear los objetos a la base de datos, crear conexiones o cerrarlas.

Tipos de dependency injection

Los siguientes son los tipos de inyección de dependencias en Spring:

  • Por constructor : Las dependencias de la clase se inyectarán a través del constructor
  • Por métodos setters : Las dependencias de la clase se inyectarán a través de métodos setter
  • Por atributos de la clase : Las dependencias de la clase se inyectarán haciendo uso del api de reflection, esto debido a que un atributo de una clase puede ser privado y esto hace que no se pueda acceder desde afuera.

Se puede utilizar inyección de dependencias haciendo uso de interfaces o de clases concretas, es preferible hacerlo a través de interfaces porque esto permite:

  • Decidir la implementación que se inyectará en tiempo de ejecución
  • Seguir los principios de diseño SOLID
  • Haces el código más fácil de probar

En el ejemplo anterior hablamos sobre las clases UserDao y RoleDao utilizando una referencia de tipo Datasource, la cual es una interfaz, esto me permite que no importe el tipo de datasource que yo utilice la clase UserDao y RoleDao funcionarán correctamente.

Inversion of control

Inversion of control es una técnica que permite inyectar dependencias en tiempo de ejecución, esto significa que las dependencias no serán inyectadas de forma predeterminada todo el tiempo sino que pueden variar dependiendo del contexto de ejecución.

Algunas veces las personas confunden Inversion of control con Dependency Injection, la diferencia es que dependency injection se refiere a la composición y estructura de las clases e Inversion of control se refiere más al ambiente de ejecución del código.

En este post hablamos sobre la teoría de dependency injection, en el siguiente veremos como implementarlo utilizando Spring framework 5. 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