Anotaciones de Spring: Anotaciones del Core Framework

A

Introducción

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 @Beanes 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 @Beananotació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 @Requiredanotació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 @Autowiredanotació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 @Componentclases etiquetadas (incluidos los derivados como @Service, @Controllery @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 ProductServiceclase, separándola de ProductControllersi 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 @Servicebean anotado, pero hablaremos de eso más adelante.

También hay otros casos de uso para la @Autowiredanotación.

@Autowired en Setters

Muy similar a la @Requiredanotación, también podemos usar @Autowireden 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 @Autowiredanotació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 requiredbandera 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 @Qualifieranotació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 @ComponentScananotació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 @Configurationanotación.

Por ejemplo, tenemos un com.Pharos.sh.controllerpaquete 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 @ComponentScananotació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 basePackageque contiene todos nuestros componentes, como com.Pharos.sh. Aunque en algunos casos querríamos incluir múltiples basePackageso 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 basePackageses 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 @Lazyanotación.

Se puede usar en un nivel de clase anotado como @Componento 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 @Configurationclase como @Lazy:

@Lazy
@Configuration
public class AppConfig {
    // some code
}

En este caso, todos los beans definidos dentro AppConfigtambién se inicializarán de forma diferida.

@Configuración

La @Configurationanotación está a nivel de clase y le dice a Spring que esta clase contiene uno o más @Beanmé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 @Valueanotació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 @Valueanotació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.

La @Valueanotació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 booleans y ints:

@Value("true")
private boolean accepted;

@Value("53")
private int userId;

Propiedades del entorno de lectura

Digamos que, entre otras propiedades, nuestro application.propertiesarchivo 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_nameno 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 @Valueanotació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 @Valuemé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 @DependsOnanotació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 @Componento @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 FirstBeanse encuentra antes del segundo y tercero, hemos comentado que depende de la creación del SecondBeany ThirdBeanpara que funcione correctamente. Al hacer esto, Spring definirá primero esos dos y luego FirstBean.

@Primario

La @Primaryanotación se usa a menudo junto con la Qualifieranotació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 @Autowiredanotación elija entre los beans calificados.

Sin embargo, esta vez, no necesitamos agregar la @Qualifieranotación a la @Autowiredanotación ya que se ha declarado el bean primario / predeterminado:

@Controller
public class CompanyController {
    @Autowired
    private Employee employee;
}

Esto creará una instancia de un Developerbean.

@Alcance

La @Scopeanotación se aplica a nivel de bean y define su visibilidad / ciclo de vida. Si se aplica junto con la @Componentanotación, define el alcance de las instancias del tipo anotado. Si se usa en un @Beanmé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 singletonalcance, la aplicación del alcance del prototipo garantiza una newinstancia del bean anotado cada vez que lo solicitamos.

@Bean
@Scope("prototype")
public Developer developer() {
    return new Developer();  
}

Solicitud

El requestalcance 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 @RequestScopeque incluye el proxy por defecto.

Sesión

Muy similar al requestalcance, el sessionalcance 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 applicationalcance funciona de manera similar al singletonalcance. El applicationciclo 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 applicationtiene 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 websocketalcance, vinculamos el ciclo de vida de nuestro bean al ciclo de vida de la WebSocketsesió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.

.

About the author

Ramiro de la Vega

Bienvenido a Pharos.sh

Soy Ramiro de la Vega, Estadounidense con raíces Españolas. Empecé a programar hace casi 20 años cuando era muy jovencito.

Espero que en mi web encuentres la inspiración y ayuda que necesitas para adentrarte en el fantástico mundo de la programación y conseguir tus objetivos por difíciles que sean.

Add comment

Sobre mi

Últimos Post

Etiquetas

Esta web utiliza cookies propias para su correcto funcionamiento. Al hacer clic en el botón Aceptar, aceptas el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Más información
Privacidad