Lo siguiente es una configuración para un "HelloWorld" muy simple pero que nos permitirá ver como Spring inyecta el código usando la configuración establecida en el IoC Container. La configuración la establecemos en un fichero xml (beans.xml), que deberá estar en el classpath de nuestro proyecto, podemos establecer el nombre que queramos no tiene que ser applicationContext.xml.
tenemos la configuración xml para usar los tags establecidos en el esquema en cuestión. El caso anterior es una de las configuraciones más simples posible. Una vez que la tenemos establecida en el
necesitamos instanciar el mismo. Lo haremos de la siguiente manera:
1
| ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
|
Es en ese momento(
linea 1) cuando creamos una instancia del
Application Context presente en nuestro
IoC Container. Baste decir que a pesar de que dicha instancia la creamos a partir de
ClassPathXmlApplicationContext existen varias vías para crear una instancia de nuestro Application Context, por ejemplo:
FileSystemXmlApplicationContext vía usada para cargar ficheros XML de configuración desde el fileSystem o incluso desde URLs para el caso de las aplicaciones web se podría usar XmlWebApplicationContext.
Una vez que tenemos una instancia del
IoC Container, en la
linea 1 del código anterior ya podemos obtener cualquier
bean de los declarados(
lineas 6,12 ó 18) en el fichero de configuración(
beans.xml).
1
2
3
4
5
| //"helloWorld" es el id del bean declarado en el IoC Container
//El cast es obviamente la clase identificada con el bean,declarado en
//el IoC container
HelloWorld helloWorld = (HelloWorld) context.getBean("helloWorld");
|
Toda vez que ya tenemos los beans podemos usarlos para la funcionalidad que necesitemos. Un detalle a destacar es que podríamos cargar en nuestro contexto varios ficheros de configuración. Imaginemos que queremos dividir los ficheros de configuración agrupándolos por determinada funcionalidad.Pues ubicando los ficheros en el classpath del proyecto cargaremos dicha configuración en
IoC Container. La manera de cargar los ficheros de configuración sería la siguiente:
1
2
3
| ApplicationContext context =
new ClassPathXmlApplicationContext(new String[] {"beans.xml",
"beans2.xml","beans3.xml"});
|
Una manera más profesional podría ser crear un solo fichero de configuración que importe a su vez todos los ficheros de configuración que tengan los beans separados por cada funcionalidad. Un ejemplo claro podría ser el fichero de configuración siguiente :
1
2
3
4
5
6
7
8
9
10
11
| <beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<import resource="beans.xml" />
<import resource="beans2.xml" />
<import resource="beans3.xml" />
</beans>
|
Donde además solo será necesaria una instancia de
IoC Container para luego obtener el bean que necesitemos y sus correspondientes funcionalidades.
El resto del ciclo de vida hasta la carga de cada bean en cuestión sería del mismo modo que hemos explicado anteriormente.
Esta es la esencia de Spring, a partir de este punto tenemos una serie de aspectos a tener en cuenta que dependerá de cuanto seamos capaces de profundizar respecto a nuestras necesidades :
- podemos establecer propiedades,en los beans declarados en los ficheros de configuración,para luego inyectarlas:
1
2
3
4
5
6
7
8
| <bean id="dataSource" class="com.ldg.spring.BasicDataSource">
<property name="driverClassName" value="org.apache.derby.jdbc.ClientDriver"/>
<property name="url" value="jdbc:derby://localhost:1527/vehicle;create=true"/>
<property name="username" value="app"/>
<property name="password" value="app"/>
<property name="initialSize" value="2"/>
<property name="maxActive" value="5"/>
</bean>
|
- podemos inicializar objetos a través de un constructor desde el IoC container:
1
2
3
4
5
| <bean id="constructorCourseConstructor" class="com.ldg.spring.ConstructorCourseWithConstructor">
<constructor-arg value="30" />
<constructor-arg value="A" />
<constructor-arg value="100000" />
</bean>
|
- podemos incluso tener un bean parent con una funcionalidad y luego un bean hijo que herede esa funcionalidad del parent y que además tenga otras funcionalidades:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| <!--ver la ref suffixes-->
<bean id="baseConstructor" abstract="true">
<property name="initial" value="100000" />
</bean>
<bean id="baseSpringConstructor" abstract="true" parent="baseConstructor"
class="com.ldg.spring.sequence.SequenceGenerator">
<property name="suffixes">
<ref local="setsuffixes" />
</property>
</bean>
<util:set id="setsuffixes">
<value>5</value>
<value>10</value>
<value>20</value>
</util:set>
|
En el ejemplo anterior
bean parent:
baseConstructor bean hijo: baseSpringConstructor ,la linea 14 nos indica una colección que podrá ser inyectada en el bean,en este caso ha sido un Set pero podría ser un List o Maps u otra colección. Pueden ser incluso definidas stand alone collections, como por ejemplos beans que pueden ser un set o un map o una lista. Lo dejamos modo de cultura para que el lector investigue o haga googling en la gran cantidad de documentación que existe de Spring.
Gran parte de la configuración, independientemente de que puede ser hecha de un modo declarativo en el
IoC Container, puede también ser combinada con anotaciones. Por ejemplo si queremos que una propiedad tenga que ser obligatoriamente evaluada podemos anotar sus métodos setter con
@Required. Como un detalle, para que las anotaciones sean reconocidas tiene que estar un tag en el fichero de configuración tal y como indica la siguiente porción del fichero de configuración.
Hasta este punto del tutorial era necesario establecer los métodos setter en cada una de las propiedades para que tuviera lugar la inyección de código teniendo en cuenta lo establecido los ficheros de configuración. Es justo señalar que esta tarea era bastante engorrosa, unido a ser muy propicia para la generación de errores en aquellos desarrolladores que no programamos con Ides de desarrollo ni Plugins. Como solución tenemos el
Auto-Wiring,
una opción que nos permite entre otras cosas estar el NO tener que establecer una infinidad de referencias en el fichero de configuración para que luego puedan ser inyectadas en el código.
Es este un tema que analizaremos a fondo sobre todo porque lo utilizaremos en todo momento.Es importante entender el porqué de las cosas,podríamos trabajar sin
Auto-Wiring en SPRING pero veamos que implica:
De manera general cuando un Bean necesita una referencia de otro esta referencia puede ser establecida especificando la referencia explícitamente:
<bean id="sequenceGenerator"
class="com.ldg.spring.sequence.SequenceGenerator">
<property name="prefixGenerator" ref="datePrefixGenerator" />
</bean>
Lo mismo se aplicaría si por ejemplo nuestro bean tuviera un constructor:
1
| <constructor-arg ref="datePrefixGenerator" />
|
Imaginemos en un fichero de configuración de una aplicación real, en el que existen un gran número de referencias de un
bean en otros ,sería mejor en ese caso que el Ioc Container pueda referenciar los Beans automáticamente y nos ahorraría los problemas de configurar el contenedor manualmente. De todo lo anterior se deriva esta necesidad de implementar el
Auto-Wiring, que puede ser hecho también de
manera declarativa o a través de
anotaciones.
Para aligerar este breve tutorial haremos una breve referencia al modo
Auto-Wiring de modo declarativo pero nos centraremos en el
Auto-Wiring a través de anotaciones.
|
Distintas vías para realizar autowire de modo declarativo |
1
2
3
4
5
6
7
8
9
10
11
| <beans ...>
<bean id="sequenceGenerator"
class="com.ldg.spring.sequence.SequenceGenerator"
autowire="byType">
<property name="initial" value="100000" />
<property name="suffix" value="A" />
</bean>
<bean id="datePrefixGenerator" class="com.ldg.spring.sequence.DatePrefixGenerator">
&alt;property name="pattern" value="yyyyMMdd" />
</bean>
</beans>
|
El ejemplo de configuración anterior es un
autowire byType y en él Spring intentará hacer un wire con un bean que sea del mismo tipo o como es lógico del tipo de una interfaz por el bean implementada.En
com.ldg.spring.sequence.SequenceGenerator tendrán que existir variable(s) de tipo
com.ldg.spring.sequence.DatePrefixGenerator y en
com.ldg.spring.sequence.DatePrefixGenerator tendrán que existir getter y setter de las correspondientes propiedades definidas en el bean.
El término usado propiamente hablando sería
datePrefixGenerator será wired (enlazado,conectado,inyectado) automáticamente. Esto tiene desventajas en el caso que existieran más beans que fuesen del mismo tipo,en ese caso Spring no sabría con que bean hacer el wire. Un análisis que en su conjunto escapa al alcance de este articulo, si el lector quiere profundizar puede ir a la documentación de Spring, sobre todo dado el caso que tenga un marcado interés por el autowire de modo declarativo, tal y como indica el anterior esquema.
Autowire a través de anotaciones. Ya conocemos lo que es un autowire ,ahora veremos como definirlo a través de las anotaciones: @Autowire,Spring ó @Resource ,Java 1.5+. :
para que tengan efecto estas anotaciones en el código hemos de registrar una instancia de AutowiredAnnotationBeanPostProcessor en el IoC container lo cual puede ser hecho de la siguiente forma:
<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor">
ó de un modo más simple podría ser incluyendo el elemento
context:annotation-config en el fichero de configuración de nuestro bean:
1
2
3
4
5
6
7
| <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:annotation-config />
...
</beans>
|
La anotación @Autowired puede ser aplicada a una propiedad en particular para que Spring la inyecte. En la siguientes lineas de código anotamos el método setter de una propiedad con @Autowired y a su vez Spring intentará inyectar un bean cuyo tipo es compatible con el tipo de la propiedad en cuestión.
1
2
3
4
5
6
7
8
9
| package com.ldg.spring.sequence;
import org.springframework.beans.factory.annotation.Autowired;
public class SequenceGenerator {
...
@Autowired
public void setPrefixGenerator(PrefixGenerator prefixGenerator) {
this.prefixGenerator = prefixGenerator;
}
}
|
Dado el caso que tengamos un bean como el siguiente definido en el IoC container,será inyectada la propiedad indicada en el código anterior:
1
2
3
4
5
6
7
8
9
10
11
| <beans ...>
...
<bean id="sequenceGenerator"
class="com.ldg.spring.sequence.SequenceGenerator">
<property name="initial" value="100000" />
<property name="suffix" value="A" />
</bean>
<bean id="datePrefixGenerator" class="com.ldg.spring.sequence.DatePrefixGenerator">
<property name="pattern" value="yyyyMMdd" />
</bean>
</beans>
|
Todas las propiedades anotadas con
@Autowired han de ser enlazadas (wired), dado el caso que Spring no encuentre un bean a inyectar se lanzará una exception ya que todas las propiedades anotadas con
Autowired son
requeridas.Otra cosa bien distinta es el hecho de que le indiquemos que no es requerida
@Autowired(required = false) y en ese caso sino encuentra un bean que inyectar dejará la variable si setear.
Existen varías vías para realizar el proceso de autowire incluso sin necesidad de establecer
@Autowire a los métodos setters en cuestión.Todas estas distintas vías escapan este articulo a pesar de que podríamos tratarlo en versiones futuras.
Como último aspecto de este articulo trataremos el
Escanear Componentes desde el classpath. Ello con lo que hemos visto hasta ahora puede ser hecho en Spring de un modo más rudimentario declarando cada uno de los componentes en el fichero de configuración del Ioc Container. Existe una vía para hacer esto de una manera mas eficiente que es a través del
component scanning. El cual automáticamente escanea ,detecta e instancia aquellos componentes que estén en nuestro classpath y que tengan determinadas anotaciones. La principal anotación para que Spring pueda reconocer los componentes es @Component , a partir de este estereotipo encontraremos otros más específicos que serán tratados en posteriores entradas:
1
2
3
4
5
6
7
8
9
| package com.ldg.spring.sequence;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class SequenceService {
@Autowired
private SequenceDao sequenceDao;
...
}
|
1
2
3
| ...
SequenceService sequenceService = (SequenceService) context.getBean("sequenceService");
...
|
1
2
3
4
5
6
| <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:component-scan base-package="com.ldg.spring.sequence" />
</beans>
|
Respecto al código anterior ,donde quiera que encuentre una declaración de un objeto del tipo SequenceDao, que pertenezca al package "
com.ldg.spring.sequence", lo inyectará en dicha variable ó lo que podríamos decir,que será inicializado el objeto.Para reconocer el objeto y poder registrarlo necesita la anotación
@Component o algunos de sus estereotipos (
@Repository, @Service, @Controller o cualquier otra anotación personalizada por el desarrollador) y para poder realizar la inyección necesitará el
@Autowire.