Introducción
Contenido
El Spring Framework es un marco muy robusto, lanzado en 2002. Sus características principales se puede aplicar a las aplicaciones de Java convencionales o extenderse a aplicaciones web modernas, complejas.
Dado que se actualiza constantemente y sigue nuevos paradigmas de arquitectura y programación, ofrece soporte para muchos otros marcos que trabajan de la mano con él.
Con una gama tan amplia de funcionalidades, es normal que nos presente algunas anotaciones nuevas, que son una parte clave del desarrollo de aplicaciones Spring.
La configuración de Spring es totalmente personalizable, lo que originalmente se hizo a través de archivos de configuración XML. Sin embargo, este enfoque se ha vuelto obsoleto y la mayoría de las personas hoy en día recurren a la configuración de anotaciones.
Dicho esto, esta serie de artículos tiene como objetivo desentrañar las opciones que usted, como desarrollador, tiene para configurar y usar el marco Spring:
- Anotaciones de Spring Framework: @RequestMapping y sus variantes
- Spring Annotations: anotaciones principales
- Spring Annotations: anotaciones de Spring Cloud
- Spring Annotations: Prueba de anotaciones
Nota : Este artículo asume que está familiarizado con el principio de Inversión de Control de Spring.
Anotaciones principales
Echemos un vistazo a las anotaciones centrales que componen casi todas las aplicaciones Spring:
@Frijol
A @Bean
es un objeto troncal en Spring Framework. Todo se reduce a JavaBeans: clases que encapsulan objetos en uno solo. Son un tipo de POJO (Plain Old Java Object).
Todos los JavaBeans deben ser Serializable
, todos los campos deben ser privados, todos los campos deben tener setters y getters, debe haber un constructor sin argumentos y los campos son accedidos exclusivamente por el constructor o los métodos getter / setter:
public class Developer implements java.io.Serializable {
private int id;
private String name;
public Developer() {}
public void setId(int id) {this.id = id;}
public int getId() {return id;}
public void setName(String name) {this.name = name;}
public String getName() {return name;}
}
En términos de Spring, los beans son instanciados y administrados por Spring IoC Container. Son simplemente instancias de objetos administradas por Spring.
Para que Spring sepa qué instancias de objetos debe administrar, simplemente marcamos los métodos en los que las instanciamos con la @Bean
anotación.
Cuando se encuentre este método, se ejecutará y el valor devuelto se guardará dentro de BeanFactory
:
@Configuration
public class ConfigurationClass {
@Bean
public Developer developer() {
return new Developer();
}
}
Esto es lo mismo que usar el antiguo enfoque XML para registrar un bean:
<beans>
<bean name="developer" class="com.Pharos.sh.Developer"/>
</beans>
Ahora, para inyectar este bean como una dependencia en otro bean, simplemente tenemos otro bean que llama al método del bean desarrollador:
@Configuration
public class ConfigurationClass() {
@Bean
public Manager manager() {
return new Manager(developer());
}
@Bean
public Developer developer() {
return new Developer();
}
}
@Necesario
La @Required
anotación se utiliza en constructores y métodos de establecimiento. Como sugiere el nombre, le dice a Spring que estos campos son necesarios para que el bean se inicialice correctamente.
Si los campos no se completan en el momento de la configuración, el bean no se inicializará, lo que dará como resultado una excepción y la aplicación no podrá compilarse:
public class Developer implements java.io.Serializable {
private int id;
private String name;
public Developer() {}
@Required
public void setId(int id) {
this.id = id;
}
public int getId() {
return id;
}
@Required
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
Para completar un campo en tiempo de configuración como este, asignamos los nombres de propiedad a través de XML:
<bean class="com.Pharos.sh.Develope>
<property name="name" value="David"/>
</bean>
@Autowired
La @Autowired
anotación se utiliza para un mayor control sobre la inyección de dependencia. Se usa para conectar un bean a otro sin instanciar el anterior.
Nuevamente, en lugar de conectar las dependencias a través de XML, que era engorroso, simplemente marcamos nuestras dependencias como @Autowired
. Basado en nuestra clase base, donde se encuentran todos nuestros componentes, Spring hace todo el cableado por nosotros.
Para declarar el paquete base de nuestros componentes, simplemente podemos agregar una etiqueta al archivo de contexto de nuestra aplicación:
<context:component-scan base-package="com.Pharos.sh.basePackage"/>
Todas las @Component
clases etiquetadas (incluidos los derivados como @Service
, @Controller
y @Repository
) se registrarán como beans que son elegibles para autowiring.
@Autowired en propiedades
En lugar de la instanciación explícita e imperativa:
public class ProductController {
private ProductService productService = new ProductService();
public void someMethod() {
List<Product> productList = productService.getProductList();
}
}
Usamos un enfoque declarativo:
public class ProductController {
@Autowired
private ProductService productService;
public void someMethod() {
List<Product> productList = productService.getProductList();
}
}
En esta implementación, nunca creamos realmente una instancia de la ProductService
clase, separándola de ProductController
si deseamos probarla.
Por supuesto, para conectar automáticamente un campo, debe estar registrado como un bean en el contenedor Spring IoC. En nuestro caso, es un @Service
bean anotado, pero hablaremos de eso más adelante.
También hay otros casos de uso para la @Autowired
anotación.
@Autowired en Setters
Muy similar a la @Required
anotación, también podemos usar @Autowired
en setters:
public class ProductController {
private ProductService productService;
@Autowired
public void setProductService(ProductService productService) {
this.productService = productService;
}
}
Al conectar automáticamente un establecedor como este, no es necesario completarlo a través de XML.
Esta es la llamada inyección de dependencia basada en setter .
@Autowired en constructores
La @Autowired
anotación también se puede utilizar en constructores:
public class ProductService {
private ProductDao productDao;
@Autowired
public ProductService(ProductDao productDao) {
this.productDao = productDao;
}
}
Esta es la llamada inyección de dependencia basada en constructor .
La bandera requerida
Al marcar un bean como @Autowired
, Spring espera que esté disponible al construir las otras dependencias. Si no, seremos recibidos con una excepción y una compilación fallida.
Si no puede garantizar que el bean estará disponible, o si no siempre es necesario, puede usar la required
bandera para marcarlo como opcional:
public class ProductController {
@Autowired(required = false)
private ProductService productService;
}
De esta forma, si el bean de servicio del producto no está disponible, todo funcionará sin problemas.
@Calificatorio
La @Qualifier
anotación se usa para aclarar los casos en los que nos gustaría conectar automáticamente más de un bean del mismo tipo.
Por ejemplo, en una empresa, lo más probable es que tengamos más de un empleado, y cada empleado tiene su puesto respectivo: desarrollador, desarrollador líder, gerente, CEO, etc.
@Component
public class Developer implements Employee {}
@Component
public class Manager implements Employee {}
Si tuviéramos que conectar automáticamente un empleado, sería ambiguo en cuanto a qué bean queremos conectar automáticamente:
@Controller
public class CompanyController {
@Autowired
private Employee employee;
}
Nos recibiría un error:
org.springframework.beans.factory.NoSuchBeanDefinitionException:
No unique bean of type [com.Pharos.sh.employee] is defined:
expected single matching bean but found 2: [developer, manager]
Para evitar tal situación, agregamos calificadores:
@Component
@Qualifier("developer")
public class Developer implements Employee {}
@Component
@Qualifier("manager")
public class Manager implements Employee {}
Y cuando se conecta automáticamente:
@Controller
public class CompanyController {
@Autowired
@Qualifier("developer")
private Employee employee;
}
Esto aclara qué bean nos gustaría conectar automáticamente y el código se ejecuta bien.
@ComponentScan
Una anotación crucial para Spring es la @ComponentScan
anotación. Especifica qué paquetes contienen clases que están anotadas. De esa manera, Spring sabe qué clases necesita administrar y siempre se usa junto con la @Configuration
anotación.
Por ejemplo, tenemos un com.Pharos.sh.controller
paquete que contiene todos nuestros controladores donde cada clase está anotada @Controller
. Para que Spring sepa que este paquete contiene componentes que necesitan administración, usamos la @ComponentScan
anotación y agregamos el paquete.
De lo contrario, tendríamos que registrar cada bean individualmente, lo que sería engorroso e imposible de escalar.
En muchos casos, simplemente definimos uno basePackage
que contiene todos nuestros componentes, como com.Pharos.sh
. Aunque en algunos casos querríamos incluir múltiples basePackages
o basePackageClasses
:
@Configuration
@ComponentScan(basePackage = "com.Pharos.sh")
public class SomeApplication {
// some code
}
Si nos gustaría definir varios paquetes base:
@Configuration
@ComponentScan(basePackage = {"com.package1", "com.package2})
public class SomeApplication {
// some code
}
Una alternativa de tipo seguro para basePackages
es basePackageClasses
:
@Configuration
@ComponentScan(basePackageClasses = Developer.class)
public class SomeApplication {
// some code
}
Nota : Si no se define ningún paquete base, el paquete en el que se encuentra la clase se utilizará como paquete base.
@Perezoso
Por defecto, los beans y componentes se inicializan con entusiasmo. Si quisiéramos cambiar ese comportamiento, podemos hacerlo mediante la @Lazy
anotación.
Se puede usar en un nivel de clase anotado como @Component
o en un nivel de método anotado como @Bean
.
Si se anota, el componente / bean no se inicializará hasta que otro bean lo haga referencia explícitamente y sea necesario para que la aplicación se ejecute sin problemas:
@Lazy
@Bean
class SomeResource {}
También podríamos marcar una @Configuration
clase como @Lazy
:
@Lazy
@Configuration
public class AppConfig {
// some code
}
En este caso, todos los beans definidos dentro AppConfig
también se inicializarán de forma diferida.
@Configuración
La @Configuration
anotación está a nivel de clase y le dice a Spring que esta clase contiene uno o más @Bean
métodos y puede ser procesada por el contenedor Spring para generar definiciones de frijoles.
Esta es una de las razones por las que los desarrolladores pudieron dejar de usar la configuración basada en XML y la simplicidad de la anotación hace que la configuración basada en Java sea preferible.
@Configuration
public class AppConfig {
@Bean
public SomeBean someBean() {
// Instantiation, configuration, returning the bean
}
@Valor
La @Value
anotación tiene bastantes casos de uso en Spring y garantiza un artículo para sí misma. Intentaré ser breve y cubrir los casos de uso más comunes y obvios en este caso.
Puede utilizarse para:
- Asignar valores predeterminados a los campos
- Lectura de variables de entorno
- Uso de expresiones de Spring Expression Language (SpEL)
- Valores predeterminados para parámetros si se usan dentro de un método / constructor
Dicho esto, repasemos todos estos casos de uso uno por uno.
Valores de campo predeterminados
Si desea asignar un valor predeterminado a un campo, la @Value
anotación es bastante sencilla:
@Value("Hello World!")
private String helloString;
Aunque no creamos esta cadena ni le asignamos un valor explícitamente, lo hemos hecho a través de la anotación.
Te puede interesar:¿Java «pasa por referencia» o «pasa por valor»?La @Value
anotación está pensada para usarse con Strings. Si intenta aplicarlo a otro tipo, funcionará solo si Spring puede convertir fácilmente entre los dos, como boolean
s y int
s:
@Value("true")
private boolean accepted;
@Value("53")
private int userId;
Propiedades del entorno de lectura
Digamos que, entre otras propiedades, nuestro application.properties
archivo contiene algunas variables de entorno:
sa.website_name = Stack Abuse
Por ejemplo, leamos esta propiedad y la asignamos a un String en nuestra clase de configuración. Para hacer esto, también necesitamos definir la fuente de la propiedad:
@PropertySource("classpath:application.properties")
@Configuration
public class AppConfig {
@Value("${sa.website_name}")
private String websiteName;
}
En términos generales, ${...}
se utiliza como marcador de posición de propiedad en Spring. Probablemente ya esté familiarizado con esto si ha incursionado en las tecnologías Spring.
Si la propiedad no está disponible o definida, podríamos tener un problema. En este caso, podemos definir valores predeterminados para los marcadores de posición en caso de que no estén definidos correctamente:
@PropertySource("classpath:application.properties")
@Configuration
public class AppConfig {
@Value("${sa.website_name}:Backup Value")
private String websiteName;
}
De esta forma, si sa.website_name
no existe, el valor asignado al String será Backup Value
.
Usando SPEL
Similar a la sintaxis del marcador de posición, Spring Expression Language (SpEL) usa la #{...}
sintaxis para almacenar expresiones:
@Value("#{systemProperties['java.home']}")
private String someValue;
Si decidimos incluir algunas propiedades que podrían no estar disponibles, nuevamente estaríamos en un problema. Para evitar estos casos, también podemos definir valores de «copia de seguridad» predeterminados para los SpEL:
@Value("#{systemProperties['unknownproperty'] ?: 'Backup Value'}")
private String someValue;
Valores de parámetros predeterminados
Si se aplica a un método, la @Value
anotación asignará el valor predeterminado a todos los parámetros del método:
@Value("Hello")
public String hello(String str1, String str2) {
return str1 + str2;
}
Este método imprimiría:
HelloHello
Por otro lado, si aplicamos el @Value
método tanto a un método como a un parámetro, al parámetro se le asignará el nuevo valor:
@Value("Hello")
public String hello(String str1, @Value("World") String str2) {
return str1 + str2;
}
La salida en este caso sería:
HelloWorld
@DependsOn
Si un bean depende de otros beans para una correcta instanciación, Spring puede garantizar que todos los beans de los que depende se crearán antes que él. Sin embargo, necesitamos especificar cuáles usando la @DependsOn
anotación.
La anotación acepta una matriz de cadenas que corresponden a los nombres de los beans en cuestión. Esto significa que puede pasar cualquier nombre de bean válido como argumento, siempre que esté debidamente anotado con una anotación @Component
o @Bean
.
@Configuration
public class AppConfig {
@Bean("firstBean")
@DependsOn(value = {"secondBean", "thirdBean"})
public FirstBean firstBean() {
return new FirstBean();
}
@Bean("secondBean")
public SecondBean secondBean() {
return new SecondBean();
}
@Bean("thirdBean")
public ThirdBean thirdBean() {
return new ThirdBean();
}
}
Aunque FirstBean
se encuentra antes del segundo y tercero, hemos comentado que depende de la creación del SecondBean
y ThirdBean
para que funcione correctamente. Al hacer esto, Spring definirá primero esos dos y luego FirstBean
.
@Primario
La @Primary
anotación se usa a menudo junto con la Qualifier
anotación. Se utiliza para definir el bean «predeterminado» para el cableado automático cuando no hay más información disponible.
Da prioridad al bean anotado, si hay más de un bean del mismo tipo, como su nombre lo indica:
@Component
@Qualifier("developer")
@Primary
public class Developer implements Employee {}
@Component
@Qualifier("manager")
public class Manager implements Employee {}
Este es el mismo problema que encontramos en la parte anterior del artículo, donde definimos un calificador para permitir que la @Autowired
anotación elija entre los beans calificados.
Sin embargo, esta vez, no necesitamos agregar la @Qualifier
anotación a la @Autowired
anotación ya que se ha declarado el bean primario / predeterminado:
@Controller
public class CompanyController {
@Autowired
private Employee employee;
}
Esto creará una instancia de un Developer
bean.
@Alcance
La @Scope
anotación se aplica a nivel de bean y define su visibilidad / ciclo de vida. Si se aplica junto con la @Component
anotación, define el alcance de las instancias del tipo anotado. Si se usa en un @Bean
método, el alcance se aplica a la instancia devuelta.
Hay dos ámbitos básicos, con otros cuatro para aplicaciones compatibles con la web:
- único
- prototipo
- solicitud
- sesión
- solicitud
- websocket
Alcance Singleton
Si no se utiliza ningún otro nombre de ámbito, el valor predeterminado es singleton . Un singleton
ámbito garantiza solo una instancia de la instancia devuelta del método anotado. El objeto se guardará en el contenedor Spring y se almacenará en caché, lo que permitirá su uso en cualquier lugar de la aplicación:
@Bean
@Scope("singleton")
public CompanyCEO companyCEO() {
return new CompanyCEO();
}
Prototipo
Lo opuesto al singleton
alcance, la aplicación del alcance del prototipo garantiza una new
instancia del bean anotado cada vez que lo solicitamos.
@Bean
@Scope("prototype")
public Developer developer() {
return new Developer();
}
Solicitud
El request
alcance garantiza la instanciación de un solo bean para cada solicitud HTTP:
// This method will be called on every HTTP request
@Bean
@Scope("request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public SetupClass someSetup() {
// Run some setup on each http request
}
Una alternativa sería utilizar la anotación 4.3 @RequestScope
que incluye el proxy por defecto.
Sesión
Muy similar al request
alcance, el session
alcance instanciará el bean anotado con un ciclo de vida dependiente de la sesión HTTP.
// This method will be called on every HTTP session
@Bean
@Scope("session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public SetupClass someSetup() {
// Run some setup on each http session
}
Solicitud
El application
alcance funciona de manera similar al singleton
alcance. El application
ciclo de vida de un bean con ámbito depende de la aplicación, o mejor dicho, del ServletContext
.
La principal diferencia entre esos dos es el hecho de que application
tiene un alcance más amplio en el sentido de que puede expandirse a otras aplicaciones que se ejecutan en el mismo ServletContext
.
@Scope("application")
@Component
public class Application {}
Nuevamente, a partir de 4.3, puede reemplazar esta anotación con @ApplicationScope
.
WebSocket
Si usamos el websocket
alcance, vinculamos el ciclo de vida de nuestro bean al ciclo de vida de la WebSocket
sesión de.
La primera vez que se llama, se crea una instancia del bean y se almacena para su uso posterior dentro de la misma sesión:
// This method will be called on every websocket session
@Bean
@Scope("websocket", proxyMode = ScopedProxyMode.TARGET_CLASS)
public SetupClass someSetup() {
// Run some setup on each websocket session
}
Conclusión
El marco Spring es un marco poderoso y robusto que realmente cambió el juego cuando se trata de desarrollar aplicaciones web. Entre su gran cantidad de proyectos, es una buena idea comenzar con el marco central y desarrollarlo.
El marco central nos presenta varias anotaciones que hacen nuestra vida más fácil y productiva. Manejar estas anotaciones es una necesidad para todos los desarrolladores de Java / Spring.
.