En el artículo anterior , expliqué brevemente qué es Spring y qué son los beans y el contexto. Ahora es el momento de probarlo. Lo haré con IntelliJ IDEA Enterprise Edition. Pero todos mis ejemplos también deberían funcionar en la edición gratuita IntelliJ IDEA Community Edition. En las capturas de pantalla, si ve que tengo una ventana que usted no tiene, no se preocupe, no es importante para este proyecto :) Primero, cree un proyecto Maven vacío. Mostré cómo hacer esto en el artículo en este enlace . Lea hasta las palabras " Es hora de convertir nuestro proyecto Maven en un proyecto web ". Después de eso, el artículo muestra cómo hacer un proyecto web, pero no lo necesitamos en este momento. En el src/principal/javacarpeta, cree un paquete (en mi caso, lo llamé " )
en.codegym.info.fatfaggy.animals
. Puede llamarlo como quiera. Simplemente no olvide reemplazar el nombre de mi paquete con el nombre de su paquete en todos los lugares correctos. Ahora cree la Main
clase y agregue un método
public static void main(String[] args) {
...
}
Después de eso, abra el archivo pom.xml y agregue la dependencies
sección. Ahora vaya al repositorio de Maven y busque el contexto de Spring para la última versión estable. Poner lo que encontramos en la dependencies
sección. Describí este proceso con más detalle en este otro artículo de CodeGym (ver la sección titulada " Conectando dependencias en Maven "). Luego, Maven mismo encontrará y descargará las dependencias necesarias. Al final, debería obtener algo como esto: En la ventana de la izquierda, puede ver la estructura del proyecto con el paquete y la Main
clase. La ventana del medio muestra cómo me ve pom.xml. También agregué propiedadessección a la misma. Esta sección le dice a Maven qué versión de Java estoy usando en mis archivos fuente y qué versión compilar. Esto es solo para que IDEA no me advierta que estoy usando una versión anterior de Java. Esto es opcional :) La ventana de la derecha deja en claro que aunque solo conectamos el módulo de contexto de resorte, automáticamente extrajo los módulos de núcleo de resorte, frijoles de resorte, aop de resorte y expresión de resorte. Podríamos haber conectado cada módulo por separado, escribiendo una dependencia para cada módulo con la versión explícita en el archivo pom.xml, pero por ahora estamos contentos con las cosas como están. Ahora cree el entities
paquete y cree 3 clases en él: Cat
, Dog
, Parrot
. Vamos a ponerle un nombre a cada animal (private String name
- puede codificar algunos valores allí). Los getters/setters son públicos. Ahora pasamos a la Main
clase y el main()
método, y escribimos algo como esto:
public static void main(String[] args) {
// Create an empty Spring context that will look for its own beans based on the annotations in the specified package
ApplicationContext context =
new AnnotationConfigApplicationContext("en.codegym.info.fatfaggy.animals.entities");
Cat cat = context.getBean(Cat.class);
Dog dog = (Dog) context.getBean("dog");
Parrot parrot = context.getBean("parrot-polly", Parrot.class);
System.out.println(cat.getName());
System.out.println(dog.getName());
System.out.println(parrot.getName());
}
Primero, creamos un objeto de contexto, diciéndole al constructor en qué paquete buscar para encontrar beans. En otras palabras, Spring revisará este paquete e intentará encontrar clases marcadas con anotaciones especiales que indiquen que son beans. Luego crea los objetos de estas clases y los pone en contexto. Después de eso, obtenemos un gato de este contexto. Llamamos al objeto de contexto para que nos dé un bean (objeto), indicando la clase del objeto que queremos (por cierto, también podemos especificar interfaces, no solo clases). Después de eso, Spring devuelve un objeto de la clase solicitada, que luego guardamos en una variable. A continuación, le pedimos a Spring que nos consiga un frijol llamado "perro". Cuando Spring crea unDog
objeto, le da al objeto un nombre estándar (a menos que al bean creado se le haya asignado un nombre explícitamente), que es el nombre de la clase pero con una letra minúscula inicial. En este caso, nuestra clase se llama Dog
, por lo que el nombre del bean es "perro". Si necesitáramos un BufferedReader
objeto allí, Spring lo llamaría "bufferedReader". Y debido a que Java no puede estar 100% seguro de qué clase queremos, devuelve un Object
objeto, que luego convertimos manualmente al tipo deseado, es decirDog
. La opción donde se indica explícitamente la clase es más conveniente. La tercera opción es obtener un bean por nombre de clase y por nombre de bean. Es posible que el contexto tenga varios beans de una sola clase. Para indicar el frijol en particular que necesitamos, indicamos su nombre. Debido a que aquí también indicamos explícitamente la clase, ya no tenemos que realizar un lanzamiento. ¡IMPORTANTE!Si Spring encuentra varios beans que coinciden con nuestros requisitos, entonces no puede determinar qué bean darnos, por lo que generará una excepción. En consecuencia, para evitar esta situación, debe tratar de ser lo más específico posible al decirle a Spring qué bean necesita. Si Spring busca en su contexto y no encuentra un solo bean que coincida con nuestros requisitos, también generará una excepción. Finalmente, simplemente mostramos los nombres de nuestros animales para verificar que realmente obtuvimos los objetos que necesitamos. Pero si ejecutamos el programa ahora, veremos que Spring no está contento: no puede encontrar los animales que necesitamos en su contexto. Esto se debe a que no ha creado estos beans. Como dije anteriormente, cuando Spring escanea clases, busca sus propias anotaciones de Spring. Y si Spring no encuentra estas anotaciones, entonces no No creo que estas clases correspondan a beans que necesita crear. Arreglar esto simplemente requiere agregar el@Component
anotación delante de cada una de nuestras clases de animales.
@Component
public class Cat {
private String name = "Oscar";
...
}
Pero hay más Si necesitamos decirle explícitamente a Spring que el bean para esta clase debe tener un nombre específico, indicamos el nombre entre paréntesis después de la anotación. Por ejemplo, para decirle a Spring que le dé el nombre " parrot-polly
" al frijol loro, que es el nombre que usaremos para obtener este loro en el main
método, deberíamos hacer algo como esto:
@Component("parrot-polly")
public class Parrot {
private String name = "Polly";
...
}
Este es el objetivo de la configuración automática . Escribes tus clases, las marcas con las anotaciones necesarias y le dices a Spring el paquete que tiene tus clases. Este es el paquete que ejecutará Framework para encontrar anotaciones y crear objetos de estas clases. Por cierto, Spring no solo busca @Component
anotaciones, sino también todas las demás anotaciones que heredan esta. Por ejemplo, @Controller
, @RestController
, @Service
, @Repository
y más, que presentaremos en futuros artículos. Ahora intentaremos hacer lo mismo usando la configuración basada en Java . Para comenzar, elimine el@Component
anotaciones de nuestras clases. Para hacer las cosas más desafiantes, imagine que no escribimos estas clases, por lo que no podemos modificarlas fácilmente, lo que significa que no podemos agregar anotaciones. Es como si estas clases estuvieran empaquetadas en alguna biblioteca. En este caso, no hay forma de que editemos estas clases para que Spring las reconozca. ¡Pero necesitamos objetos de estas clases! Aquí necesitamos una configuración basada en Java para crear los objetos. Para comenzar, cree un paquete con un nombre como configs
. En este paquete, cree una clase ordinaria de Java, algo así como MyConfig
, y márquela con la @Configuration
anotación.
@Configuration
public class MyConfig {
}
Ahora necesitamos modificar el main()
método, cambiando la forma en que creamos el contexto. Podemos indicar explícitamente qué clase tiene nuestra configuración:
ApplicationContext context =
new AnnotationConfigApplicationContext(MyConfig.class);
Si tenemos varias clases diferentes que crean beans y queremos conectar varias de ellas simultáneamente, simplemente las indicamos todas allí, separadas por comas:
ApplicationContext context =
new AnnotationConfigApplicationContext(MyConfig.class, MyAnotherConfig.class);
Y si tenemos demasiados y queremos conectarlos todos a la vez, simplemente indicamos el nombre del paquete que los contiene:
ApplicationContext context =
new AnnotationConfigApplicationContext("en.codegym.info.fatfaggy.animals.configs");
En este caso, Spring revisará el paquete y encontrará todas las clases marcadas con la @Configuration
anotación. Bueno, y si tenemos un programa realmente grande donde las configuraciones se dividen en diferentes paquetes, entonces simplemente indicamos una lista delimitada por comas de nombres de los paquetes que contienen las configuraciones:
ApplicationContext context =
new AnnotationConfigApplicationContext("en.codegym.info.fatfaggy.animals.database.configs",
"en.codegym.info.fatfaggy.animals.root.configs",
"en.codegym.info.fatfaggy.animals.web.configs");
O el nombre de un paquete que es común para todos ellos:
ApplicationContext context =
new AnnotationConfigApplicationContext("en.codegym.info.fatfaggy.animals");
Puede hacerlo como quiera, pero me parece que la primera opción, que simplemente indica una clase con las configuraciones, se adaptará mejor a nuestro programa. Al crear un contexto, Spring busca clases marcadas con la @Configuration
anotación y creará sus propios objetos de estas clases. Intenta llamar a los métodos marcados con la @Bean
anotación, lo que significa que estos métodos devuelven beans (objetos) que Spring agregará al contexto. Y ahora crearemos beans para un gato, un perro y un loro en nuestra clase con una configuración basada en Java. Esto es bastante simple de hacer:
@Bean
public Cat getCat() {
return new Cat();
}
Aquí creamos manualmente nuestro gato y se lo entregamos a Spring, que luego sostiene nuestro objeto en su contexto. Como no le dimos explícitamente un nombre a nuestro bean, Spring le dará el mismo nombre que el nombre del método. En nuestro caso, el frijol gato se llamará " getCat
". Pero debido a que usamos la clase, no el nombre, para obtener el frijol gato en el main
método, el nombre del frijol no es importante para nosotros. Del mismo modo, cree un dog bean, teniendo en cuenta que Spring le dará el nombre del método al bean. Para nombrar explícitamente nuestro frijol loro, simplemente indicamos su nombre entre paréntesis después de la @Bean
anotación:
@Bean("parrot-polly")
public Object weNeedMoreParrots() {
return new Parrot();
}
Como puede ver, aquí indiqué un Object
tipo de devolución y le di al método un nombre arbitrario. Esto no afecta el nombre del bean, porque especificamos explícitamente el nombre aquí. Aún así, es mejor indicar un valor de retorno y un nombre de método más o menos significativos. Haga esto por la única razón de hacerse un favor cuando vuelva a abrir el proyecto en un año. :) Ahora considere la situación en la que necesitamos un bean para crear otro bean . Por ejemplo, supongamos que queremos que el nombre del gato en el frijol gato sea el nombre del loro más la cadena "-asesino". ¡Ningún problema!
@Bean
public Cat getCat(Parrot parrot) {
Cat cat = new Cat();
cat.setName(parrot.getName() + "-killer");
return cat;
}
Aquí Spring verá que para crear este bean, el marco debe pasar el frijol loro creado anteriormente. En consecuencia, organizará la cadena necesaria de llamadas a métodos: primero, se llama al método de creación de loros y luego el marco pasa el nuevo loro al método de creación de gatos. Aquí es donde entra en juego la inyección de dependencia : Spring mismo pasa el frijol loro requerido a nuestro método. Si IDEA se molesta por la parrot
variable, no olvide cambiar el tipo de retorno del método de creación de loros de Object
a Parrot
. Además, la configuración basada en Java le permite ejecutar absolutamente cualquier código Javaen sus métodos de creación de frijoles. Realmente puede hacer cualquier cosa: crear otros objetos auxiliares, llamar a cualquier otro método, incluso aquellos que no están marcados con anotaciones Spring, crear bucles, condiciones booleanas, ¡lo que se le ocurra! No todo esto es posible con la configuración automática, y menos con la configuración XML. Ahora consideremos un problema que es un poco más divertido. Polimorfismo e interfaces :) Crearemos una WeekDay
interfaz y crearemos 7 clases que implementen esta interfaz: Monday
, Tuesday
, Wednesday
, Thursday
, Friday
, Saturday
, Sunday
. Le daremos a la interfaz un String getWeekDayName()
método, que devolverá el nombre del día de la semana para la clase correspondiente. En otras palabras, la Monday
clase devolverá "Monday
", etc. Al iniciar la aplicación, supongamos que nuestra tarea es colocar un bean correspondiente al día actual de la semana en el contexto. No beans para todas las clases que implementan la interfaz, solo el bean que necesitamos. WeekDay
Puede haz eso de esta manera:
@Bean
public WeekDay getDay() {
DayOfWeek dayOfWeek = LocalDate.now().getDayOfWeek();
switch (dayOfWeek) {
case MONDAY: return new Monday();
case TUESDAY: return new Tuesday();
case WEDNESDAY: return new Wednesday();
case THURSDAY: return new Thursday();
case FRIDAY: return new Friday();
case SATURDAY: return new Saturday();
default: return new Sunday();
}
}
Aquí el tipo de devolución es nuestra interfaz. El método devuelve un objeto de buena fe de una de las clases que implementan la interfaz, según el día de la semana actual. Ahora podemos hacer lo siguiente en el main()
método:
WeekDay weekDay = context.getBean(WeekDay.class);
System.out.println("Today is " + weekDay.getWeekDayName() + "!");
Para mí, el programa me dice que es domingo :) Confío en que si ejecuto el programa mañana, el contexto contendrá un objeto completamente diferente. Tenga en cuenta que estamos obteniendo el bean simplemente usando la interfaz: context.getBean(WeekDay.class)
. Spring buscará en su contexto el bean que implementa la interfaz y lo devolverá. Luego resulta que nuestra WeekDay
variable termina con un objeto domingo, y el concepto familiar de polimorfismo se aplica cuando trabajamos con esta variable. :) Ahora unas pocas palabras sobre el enfoque combinado , en el que algunos beans son creados automáticamente por Spring, algunos escaneando paquetes en busca de clases con la @Component
anotación, y otros por configuración basada en Java. Mientras consideramos esto, volveremos a la versión original, donde Cat
, Dog
, yParrot
las clases se marcaron con la @Component
anotación. Supongamos que queremos crear frijoles para nuestros animales haciendo que Spring escanee automáticamente el entities
paquete, pero también queremos crear un frijol con el día de la semana, como acabamos de hacer. Todo lo que necesita hacer es agregar la @ComponentScan
anotación a nivel de la MyConfig
clase, que indicamos al crear el contexto en main()
, e indicar entre paréntesis el paquete que necesita escanearse y crear beans de las clases necesarias automáticamente:
@Configuration
@ComponentScan("en.codegym.info.fatfaggy.animals.entities")
public class MyConfig {
@Bean
public WeekDay getDay() {
DayOfWeek dayOfWeek = LocalDate.now().getDayOfWeek();
switch (dayOfWeek) {
case MONDAY: return new Monday();
case TUESDAY: return new Tuesday();
case WEDNESDAY: return new Wednesday();
case THURSDAY: return new Thursday();
case FRIDAY: return new Friday();
case SATURDAY: return new Saturday();
default: return new Sunday();
}
}
}
Al crear el contexto, Spring ve que necesita procesar la MyConfig
clase. Entra en la clase y ve que necesita escanear el en.codegym.info.fatfaggy.animals.entities
paquete " " y crear beans de esas clases, después de lo cual ejecuta el método MyConfig
de la clase getDay()
y agrega un WeekDay
bean al contexto. En el main()
método, ahora tenemos acceso a todos los frijoles que necesitamos: tanto objetos de animales como un frijol con el día de la semana. Si alguna vez necesita hacer que Spring también recoja algunos archivos de configuración XML, puede hacer su propia búsqueda en la web para encontrar una explicación :) Resumen:
- Intenta usar la configuración automática
- Durante la configuración automática, indique el nombre del paquete que contiene las clases cuyos beans se deben crear
- Estas clases están marcadas con la
@Component
anotación - Spring ejecuta todas estas clases, crea objetos y los pone en contexto;
- Si la configuración automática no nos conviene por alguna razón, usamos la configuración basada en Java
- En este caso, creamos una clase ordinaria de Java cuyos métodos devuelven los objetos que necesitamos. Marcamos esta clase con la
@Configuration
anotación si vamos a escanear todo el paquete en lugar de indicar una clase específica con la configuración al crear el contexto - Los métodos de esta clase que devuelven beans están marcados con la
@Bean
anotación
@ComponentScan
anotación.
GO TO FULL VERSION