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

Around Advice

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

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
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 AroundAdviceExample {
	private static final Logger log = LoggerFactory.getLogger(AroundAdviceExample.class);

	@Around("execution(* com.devs4j.service.HelloWorldService.*(..))")
	public Object logAround(ProceedingJoinPoint proceedingJoinPoint) {
		log.info("before advice example");
		Object result = null;
		try {
			result = proceedingJoinPoint.proceed();
		} catch (Throwable e) {
			log.info("There is an exception ", e);
		}
		log.info("after advice example");
		return result;
	}
}

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 @Around nos permite utilizar un Around 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, después ejecuta el método y al final imprime after advice example
  • Los around advices interceptan la petición y deciden si ejecutar o no, esto nos permite hacer algo antes de la ejecución, después ejecutar o no el método y al final nos permite de igual modo realizar una acció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-29 09:52:40.934  INFO 75349 --- [           main] com.devs4j.Devs4jSpringAopApplication    : Started Devs4jSpringAopApplication in 0.952 seconds (JVM running for 1.265)
2019-03-29 09:52:40.938  INFO 75349 --- [           main] com.devs4j.aop.AroundAdviceExample       : before advice example
2019-03-29 09:52:40.947  INFO 75349 --- [           main] com.devs4j.service.HelloWorldService     : Hello world
2019-03-29 09:52:40.947  INFO 75349 --- [           main] com.devs4j.aop.AroundAdviceExample       : after advice example

Como vemos se imprimió el mensaje before advice example después se imprimió Hello world y al final after advice example esto nos indica que el aspecto que utiliza around advice se ejecuta antes y permite realizar acciones después del método print.

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

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 los scopes (prototype y singleton) utilizando la anotación @Scope


El scope de un bean define su ciclo de vida, en los posts anteriores hemos creado beans pero no hemos definido su scope, al hacer esto Spring le asigna uno por defecto, a continuación se muestran los scopes disponibles:

ScopeDescripción
SingletonCrea una sola instancia del bean por contenedor de Spring
PrototypeCrea una nueva instancia cada vez que se solicita
RequestCrea una nueva instancia por cada petición HTTP,
solo se puede utilizar en una aplicación web
SessionCrea una nueva instancia por cada sesión HTTP
ApplicationCrea una nueva instancia por cada ServletContext

En este post nos enfocaremos en los scopes Singleton y Prototype.

Scope singleton

El scope singleton es el scope por defecto en spring, veamos el siguiente bean:

import org.springframework.stereotype.Component;

/**
 * @author raidentrance
 *
 */
@Component
public class SingletonScopeBean {
}

Ahora veamos la siguiente clase aplicación:

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

import com.devs4j.scopes.SingletonScopeBean;

@SpringBootApplication
public class SpringExampleApplication {

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

		SingletonScopeBean singletonBean = applicationContext.getBean(SingletonScopeBean.class);

		SingletonScopeBean secondSingletonBean = applicationContext.getBean(SingletonScopeBean.class);

		System.out.println(singletonBean.equals(secondSingletonBean));
	}

}

La salida será la siguiente:

true

Podemos ver que tenemos 2 beans singletonBean y secondSingletonBean y al llamar el método equals la salida es true, indicando que es el mismo objeto.

Scope prototype

El scope prototype indica que se creará un bean nuevo cada que se mande llamar, por esto re utilizaremos el bean anterior y solo cambiaremos su scope como se muestra a continuación:

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

@Component
@Scope("prototype")
public class PrototypeScopeBean {

}

Re utilizaremos la clase aplicación:

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

import com.devs4j.scopes.PrototypeScopeBean;

@SpringBootApplication
public class SpringExampleApplication {

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

		PrototypeScopeBean prototypeBean = applicationContext.getBean(PrototypeScopeBean.class);

		PrototypeScopeBean secondPrototypeBean = applicationContext.getBean(PrototypeScopeBean.class);

		System.out.println(prototypeBean.equals(secondPrototypeBean));
	}

}

La salida será la siguiente:

false

Esto se debe a que cada vez que se mande llamar el método getBean se creará un bean nuevo, es por esto que al mandar llamar al método equals el resultado será false.

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 @Primary


En el ejemplo anterior vimos como utilizar la anotación @Qualifier y como nos ayuda cuando tenemos multiples implementaciones de una interfaz. El problema de esa solución es cuando se utiliza @Autowired sin especificar el qualifier dado que Spring falla con el siguiente error:

Field userService in com.devs4j.qualifier.UserController required a single bean, but 2 were found

Esto se resuelve fácilmente utilizando un qualifier o utilizando la anotación @Primary, esta anotación nos permite indicar el bean default a inyectar en caso de que no se especifique ningún qualifier, veamos el siguiente ejemplo:

UserService.java


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

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

UserServiceDatabaseImpl.java


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service;

/**
 * @author raidentrance
 *
 */
@Service
@Primary
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;
	}

}

UserServiceActiveDirectoryImpl.java


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

}

Como podemos ver tenemos las siguientes clases:

  • UserService : Interfaz que cuenta con 2 implementaciones
  • UserServiceDatabaseImpl : Implementación de UserService que utiliza la anotación @Primary
  • UserServiceActiveDirectoryImpl : Implementación de UserService

Una vez que definimos nuestra interfaz y sus implementaciones el siguiente paso será utilizarlo en otro bean, veamos el bean UserController :

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

Como podemos ver es posible inyectar UserService sin especificar ningún qualifier sin recibir ningún error, esto es gracias a que definimos la anotación @Primary en la clase UserServiceDatabaseImpl.

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