Una vez que entendimos como funciona dependency injection e inversion of control el siguiente paso es aprender a utilizarlo con Spring framework.
Crear un Service de ejemplo
El primer paso será crear un service de ejemplo, a continuación se muestra la interfaz y su implementación:
MathService.java
/**
*
* @author raidentrance
*
*/
public interface MathService {
/**
* Receives a set of numbers and return the sum
* @param values
* @return
*/
double sum(double... values);
}
MathServiceImpl.java
import org.springframework.stereotype.Service;
/**
* @author raidentrance
*
*/
@Service
public class MathServiceImpl implements MathService {
@Override
public double sum(double... values) {
double sum = 0.0;
for (double value : values) {
sum += value;
}
return sum;
}
}
Como vemos la clase MathServiceImpl esta marcada con la anotación @Service, esta nos permitirá indicarle a spring que debe crear una instancia de esta y mantenerla dentro de su contexto.
Accediendo al objeto creado
Una vez que creamos nuestras clases MathService y MathServiceImpl el siguiente paso será utilizarlas en nuestro código, para esto haremos lo siguiente dentro de nuestra clase de aplicación:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import com.devs4j.service.MathService;
import com.devs4j.service.MathServiceImpl;
@SpringBootApplication
public class Devs4jSpringCoreApplication {
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = SpringApplication.run(Devs4jSpringCoreApplication.class,
args);
/**
* Get bean by name
*/
MathService bean = (MathService) applicationContext.getBean("mathServiceImpl");
System.out.println(bean.sum(1.1, 2.3, 4.4));
/**
* Get bean by type
*/
MathService bean2 = applicationContext.getBean(MathServiceImpl.class);
System.out.println(bean2.sum(1, 2.3, 4));
/**
* Get bean by name and type
*/
MathService bean3 = applicationContext.getBean("mathServiceImpl", MathServiceImpl.class);
System.out.println(bean3.sum(1, 2.3, 4));
}
}
Como podemos ver al iniciar la aplicación Spring framework devolverá un objeto de tipo ConfigurableApplicationContext el cual nos permitirá acceder a los beans que existen dentro del contexto de Spring, para hacerlo podremos alguno de los siguientes métodos:
- getBean(String name) : Devuelve el objeto que tenga el nombre que se pasa como parámetro, por default el nombre del bean será el mismo al de la clase pero iniciando con minúsculas, si se desea cambiar se puede poner el nuevo nombre dentro de la anotación @Service(«nuevoNombreDelBean»).
- getBean(Class<T> requiredType) : Devuelve un objeto del tipo que se esta solicitando.
- getBean(String name, Class<T> requiredType) :Devuelve un objeto del tipo que se esta solicitando con el nombre que se está solicitando.
Algunas de las siguientes excepciones se pueden generar al utilizar los métodos anteriores:
- NoUniqueBeanDefinitionException : En caso de que existan más de un bean del mismo tipo.
- NoSuchBeanDefinitionException : En caso de que no se encuentre el bean que se está solicitando.
- BeansException : En caso de que no se pueda crear el bean solicitado.
- BeanNotOfRequiredTypeException : En caso de que el bean con el nombre solicitado no sea del tipo especificado.
Inyectando beans en otros beans
Una vez que ya sabemos como agregar un bean al contexto de spring y como obtenerlo, el siguiente paso es como hacer dependencias entre ellos, existen 3 formas de hacerlo, veamos una por una inyectando el servicio MathService que creamos previamente en la siguiente interfaz.
/**
*
* @author raidentrance
*
*/
public interface ComplexCalculatorService {
/**
* Calculates the avergae based on a set of double values
*
* @param values
* @return
*/
double average(double... values);
}
Inyección por constructor
La primera forma que veremos será inyección por constructor:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @author raidentrance
*
*/
@Service
public class ComplexCalculatorServiceImpl implements ComplexCalculatorService {
private MathService mathService;
@Autowired
public ComplexCalculatorServiceImpl(MathService mathService) {
this.mathService = mathService;
}
@Override
public double average(double... values) {
double result = mathService.sum(values);
return result / values.length;
}
}
De acuerdo a lo anterior podemos notar que:
- La clase en la que inyectaremos el service se debe encontrar en el contexto de spring, por esto ComplexCalculatorServiceImpl tiene la anotación @Service
- La inyección de dependencias se hace a través del constructor, es por esto que le colocamos la anotación @Autowired en el
Inyección por setter
La siguiente forma de hacerlo será inyección por setter:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @author raidentrance
*
*/
@Service
public class ComplexCalculatorServiceImpl implements ComplexCalculatorService {
private MathService mathService;
@Autowired
public void setMathService(MathService mathService) {
this.mathService = mathService;
}
@Override
public double average(double... values) {
double result = mathService.sum(values);
return result / values.length;
}
}
A diferencia de la inyección por constructor en la inyección por setter la anotación @Autowired se colocará el el método setter.
Inyección por atributo
La siguiente forma de inyectar un atributo es a través de inyección por atributos:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @author raidentrance
*
*/
@Service
public class ComplexCalculatorServiceImpl implements ComplexCalculatorService {
@Autowired
private MathService mathService;
@Override
public double average(double... values) {
double result = mathService.sum(values);
return result / values.length;
}
}
Como podemos ver haciendo inyección por atributo la clase luce más simple y limpia, el problema de utilizar este método de inyección es que en el se utiliza reflection dado que el atributo es privado y no se puede acceder desde fuera de la clase.
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