viernes, 26 de agosto de 2011

Spring Beans - IoC Container & Beans

Este tutorial pretende explicar el funcionamiento del IoC Container de Spring y el uso de los beans.

Temas a tratar

  • BeanFactory/ApplicationContext
  • Instantiating beans
    • Via constructor
    • Via static factory
  • Inyecting dependencies
    • Via constuctor
    • Via setterm methods
    Scope beans
  • Idref element
  • References to others beans 
    • bean
    • local
  • Depends-on element
  • Lazy instantiated beans
  • Scope beans
    • Singleton
    • Prototype
    • Singleton beans with prototype-bean dependencies
    • Others scopes

BeanFactory/ApplicationContext

Two of the most fundamental and important packages in Spring are the org.springframework.beans and org.springframework.context packages. Code in these packages provides the basis for Spring's Inversion of Control (alternately called Dependency Injection) features

The BeanFactory is the actual IoC container which instantiates, configures, and manages a number of beans.

A BeanFactory is represented by the interface org.springframework.beans.factory.BeanFactory, for which there are multiple implementations.

The most commonly used simple BeanFactory implementation is org.springframework.beans.factory.xml.XmlBeanFactory (XML-based configuration)

Ej.

Resource res = new FileSystemResource("beans.xml");
BeanFactory factory = new XmlBeanFactory(res);


O bien..


ApplicationContext context = new ClassPathXmlApplicationContext(
        new String[] {"beans.xml", "daos.xml"});


// an ApplicationContext is also a BeanFactory (via inheritance)
BeanFactory factory = context;

Muchas veces hablar del BeanFactory y del ApplicationContext es lo mismo.


The default behavior for ApplicationContext implementations is to eagerly pre-instantiate all singleton beans at startup. Pre-instantiation means that an ApplicationContext will eagerly create and configure all of its singleton beans as part of its initialization process.
Beans

Bean definitions inside a XmlBeanFactory) are represented as BeanDefinition object

Every bean has one or more ids (also called identifiers, or names; these terms refer to the same thing). These ids must be unique within the container the bean is hosted in. A bean will almost always have only one id, but if a bean has more than one id, the extra ones can essentially be considered aliases.
When using XML-based configuration metadata, you use the 'id' or 'name' attributes to specify the bean identifier(s)


Instantiating beans

If you are using XML-based configuration metadata, you can specify the type (or class) of object that is to be instantiated using the 'class' attribute of the <bean/> element

1. Instantiation using a constructor (sin argumentos)

Depending on what type of IoC you are going to use for that specific bean, you may need a default (empty) constructor.

        En caso que la clase BrandManager no tenga un constructor sin argumentos.

Ej.

....
....
<bean id="brandManager" class="springmvc.manager.BrandManager"/>
....
....

public class BrandManager {

private String a;

public BrandManager(String a) {
super();
this.a = a;
}

A la hora de inicializar el framework (startup de la aplicacion) vamos a tener el siguiente error:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'brandManager' defined in ServletContext resource [/WEB-INF/SpringHelloWorld-servlet.xml]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [springmvc.manager.BrandManager]: No default constructor found; nested exception is java.lang.NoSuchMethodException: springmvc.manager.BrandManager.<init>()
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:881)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:837)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:440)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory$1.run(AbstractAutowireCapableBeanFactory.java:409)
at java.security.AccessController.doPrivileged(Native Method)
....
....

Ahora bien, en caso que creamos un constructor publico sin argumentos

Ej.

public class BrandManager {

public BrandManager(String a) {

System.out.println("BrandManager construct...");
}
         ....
         ....

         }

A la hora de inicializar el framework (startup de la aplicacion) en la traza, vamos a encontrar lo siguiente:

....
....
Aug 26, 2011 9:54:26 AM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@12884e0: defining beans [brandManager,carManager,/hello_world.html,/car_list.html,/new_car.html,viewResolver]; root of factory hierarchy
BrandManager construct...
Aug 26, 2011 9:54:26 AM org.springframework.web.servlet.FrameworkServlet initServletBean
....
....

2.  Instantiation using a static factory method

When defining a bean which is to be created using a static factory method, along with the class attribute which specifies the class containing the static factory method, another attribute named factory-method is needed to specify the name of the factory method itself.
Ej.

....
....
<bean id="productManager" class="springmvc.manager.ProductManager" 
 factory-method="createInstance"/>
....
....  

public class ProductManager {


public static ProductManager createInstance(){

System.out.println("ProductManager -> createInstance...");
return new ProductManager();
}
...
...

}

Importante: Tiene que ser static y tiene que retornar algo el metodo sino no funciona.


Injecting dependencies

1. Constructor Injection

Constructor argument resolution matching occurs using the argument's type. If there is no potential for ambiguity in the constructor arguments of a bean definition, then the order in which the constructor arguments are defined in a bean definition is the order in which those arguments will be supplied to the appropriate constructor when it is being instantiated

Ej.

...
...
<bean id="itemManager" class="springmvc.manager.ItemManager">
<constructor-arg>
<bean class="springmvc.manager.ProductManager"/>
</constructor-arg>
</bean>
...
...

public class ItemManager {

private ProductManager productManager;


public ItemManager(ProductManager productManager) {

System.out.println("ItemManager -> Constructor..");
this.productManager = productManager;
productManager.showProduct();
}

...
...

}

Ahora mostraremos otro ejemplo cuando los tipos de datos son tipos de datos simples

Ej.
...
...
<bean id="categoryManager" class="springmvc.manager.CategoryManager">
<constructor-arg type="int" value="1098"/>
<constructor-arg type="java.lang.String" value="Hola Mundo"/>
</bean>
...
...

o lo que seria lo mismo pero con indices en lugar de tipos

<bean id="categoryManager" class="springmvc.manager.CategoryManager">
  <constructor-arg index="0" value="1098"/>
  <constructor-arg index="1" value="Hola Mundo"/>
</bean>

public class CategoryManager {

private int count;
private String name;


public CategoryManager(int count, String name) {

System.out.println("CategoryManager --> Contruct...");
System.out.println("Count " + count);
System.out.println("Name " + name);
this.count = count;
this.name = name;
}
...
...

}

2. Setter Injection

Setter-based DI is realized by calling setter methods on your beans after invoking a no-argument constructor or no-argument static factory method to instantiate your bean.
Ej.

...
...
<bean name="/car_list.html" class="springmvc.web.CarListController">
<property name="cm" ref="carManager"/>
</bean>
...
...


public class CarListController implements Controller {

// IoC 
private CarManager cm;

public CarManager getCm() {
return cm;
}

public void setCm(CarManager cm) {
this.cm = cm;
}
...
...

}


The idref element

Ambos ejemplos son lo mismo:

Ej 1

....
....
<bean id="theTargetBean" class="..."/>


<bean id="theClientBean" class="...">
    <property name="targetName">
        <idref bean="theTargetBean" />
    </property>
</bean>
....
....

Ej 2

....
....
<bean id="theTargetBean" class="..." />


<bean id="theClientBean" class="...">
    <property name="targetName" value="theTargetBean" />
</bean>
....
....

The main reason the first form is preferable to the second is that using the idref tag allows the container to validate at deployment time 
that the referenced, named bean actually exists. In the second variation, no validation is performed on the value that is passed to the 
'targetName' property of the 'theTargetBean' bean


References to other beans (collaborators)

The ref element is the final element allowed inside a <constructor-arg/> or <property/> definition element. It is used to set the value of the specified property to be a reference to another bean managed by the container (a collaborator)

Specifying the target bean by using the bean attribute of the <ref/> tag is the most general form, and will allow creating a reference to any bean in the same container (whether or not in the same XML file)

Ej 1.

<ref bean="someBean"/>

Ej 2.

....
....
<bean name="/car_list.html" class="springmvc.web.CarListController">
<property name="cm">
<ref bean="carManager"/>
</property>
</bean>
....
....


o bien, lo siguiente es lo mismo

....
....
<bean name="/car_list.html" class="springmvc.web.CarListController">
<property name="cm" ref="carManager"/>
</bean>
....
....


Specifying the target bean by using the local attribute leverages the ability of the XML parser to validate XML id references within the same file.
The XML parser will issue an error if no matching element is found in the same file

Ej 3.

<ref local="someBean"/>


Using depends-on

For most situations, the fact that a bean is a dependency of another is expressed by the fact that one bean is set as a property of another. This is typically accomplished with the <ref/> element in XML-based  configuration metadata. For the relatively infrequent situations where dependencies between beans are less direct (for example, when a static initializer in a class needs to be triggered, such as database driver registration), the 'depends-on' attribute may be used to explicitly force one or more beans to be initialized before the bean using this element is initialized. Find below an example of using the 'depends-on' attribute to express a dependency on a single bean.

Ej.

....
....
<bean id="beanOne" class="ExampleBean" depends-on="manager"/>


<bean id="manager" class="ManagerBean" />
....
....



Lazily-instantiated beans

The default behavior for ApplicationContext implementations is to eagerly pre-instantiate all singleton beans at startup. Pre-instantiation means that an ApplicationContext will eagerly create and configure all of its singleton beans as part of its initialization process. Generally this is a good thing, because it means that any errors in the configuration or in the surrounding environment will be discovered immediately.
However, there are times when this behavior is not what is wanted. If you do not want a singleton bean to be pre-instantiated when using an ApplicationContext, you can selectively control this by marking a bean definition as lazy-initialized. A lazily-initialized bean indicates to the IoC container whether or not a bean instance should be created at startup or when it is first requested

Ej 1

...
...
<bean id="carManager" class="springmvc.manager.CarManager" lazy-init="true"/>
...
...
...
...
<bean name="/car_list.html" class="springmvc.web.CarListController">
<!-- IoC CarManager -->
<property name="cm" ref="carManager"/>
</bean>
...
...

En este caso, por mas que el bean carManager tenga el lazy-init en true, este bean se va a pre inicializar de todas maneras ya que tiene que ser inyectado por el container en el CarListController

Ahora bien, veamos otro ejemplo

Ej 2

...
...
<bean id="carManager" class="springmvc.manager.CarManager" lazy-init="true"/>
...
...

Si ahora nadie usa como dependencia al bean carManager, este no sera pre inicializado hasta la hora de usarlo.


Scope beans

Beans can be defined to be deployed in one of a number of scopes: out of the box, the Spring Framework supports exactly five scopes (of which three are available only if you are using a web-aware ApplicationContext).

The scopes supported out of the box are listed below:


1. singleton: Scopes a single bean definition to a single object instance per Spring IoC container. When a bean is a singleton, only one shared instance of the bean will be managed, and all requests for beans with an id or ids matching that bean definition will result in that one specific bean instance being returned by the Spring container


Ej

<bean id="accountService" class="com.foo.DefaultAccountService"/>

<!-- the following is equivalent, though redundant (singleton scope is the default); using spring-beans-2.0.dtd -->
<bean id="accountService" class="com.foo.DefaultAccountService" scope="singleton"/>

<!-- the following is equivalent and preserved for backward compatibility in spring-beans.dtd -->
<bean id="accountService" class="com.foo.DefaultAccountService" singleton="true"/>


2. prototype: Scopes a single bean definition to any number of object instances. The non-singleton, prototype scope of bean deployment results in the creation of a new bean instance every time a request for that specific bean is made


Ej.

<!-- using spring-beans-2.0.dtd -->
<bean id="accountService" class="com.foo.DefaultAccountService" scope="prototype"/>

<!-- the following is equivalent and preserved for backward compatibility in spring-beans.dtd -->
<bean id="accountService" class="com.foo.DefaultAccountService" singleton="false"/>


Singleton beans with prototype-bean dependencies

When using singleton-scoped beans that have dependencies on beans that are scoped as prototypes, please be aware that dependencies are resolved at instantiation time. This means that if you dependency inject a prototype-scoped bean into a singleton-scoped bean, a brand new prototype bean will be instantiated and then dependency injected into the singleton bean... but that is all. That exact same prototype instance will be the sole instance that is ever supplied to the singleton-scoped bean, which is fine if that is what you want.

However, sometimes what you actually want is for the singleton-scoped bean to be able to acquire a brand new instance of the prototype-scoped bean again and again and again at runtime. In that case it is no use just dependency injecting a prototype-scoped bean into your singleton bean, because as explained above, that only happens once when the Spring container is instantiating the singleton bean and resolving and injecting its dependencies.

The other scopes, namely request, session, and global session are for use only in web-based applications:


3. request: Scopes a single bean definition to the lifecycle of a single HTTP request; that is each and every HTTP request will have its own instance of a bean created off the back of a single bean definition. Only valid in the context of a web-aware Spring ApplicationContext.

4. session: Scopes a single bean definition to the lifecycle of a HTTP Session. Only valid in the context of a web-aware Spring ApplicationContext.

5. global session: Scopes a single bean definition to the lifecycle of a global HTTP Session. Typically only valid when used in a portlet context. Only valid in the context of a web-aware Spring ApplicationContext.





Bibliografia

http://static.springsource.org/spring/docs/2.5.x/reference/beans.html



jueves, 25 de agosto de 2011

Spring MVC III - IoC (Inyección de dependencias)

En esta 3er entrega de Spring MVC vamos a mostrar un ejemplo muy muy simple de la IoC.
Para poder leer este tutorial es necesario haber leído anteriormente la 2da parte de este tutorial http://java-all-frameworks.blogspot.com/2011/08/spring-mvc-ii-form-processing.html

SpringHelloWorld-servlet.xml


<?xml version="1.0" encoding="UTF-8"?>


<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<bean id="brandManager" class="springmvc.manager.BrandManager"/>

<bean id="carManager" class="springmvc.manager.CarManager"/>

<bean name="/hello_world.html" class="springmvc.web.HelloWorldController"/>

<bean name="/car_list.html" class="springmvc.web.CarListController">
<!-- IoC CarManager -->
<property name="cm" ref="carManager"/>
</bean>


<bean name="/new_car.html" class="springmvc.web.CarNewController">
<property name="commandClass" value="springmvc.model.Car" />
<property name="formView" value="car_new" />
<property name="successView" value="car_list.html"/>
<property name="validator">
        <bean class="springmvc.validator.CarValidator"/>
                </property>
                <!-- IoC BrandManager -->
                <property name="bm" ref="brandManager"/>
</bean>


<bean id="viewResolver" class="org.springframework.web.servlet.view.
                                   InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.
                                   servlet.view.JstlView" />
<property name="prefix" value="/jsp/" />
<property name="suffix" value=".jsp" />
</bean>


</beans>


En rojo vemos como se modifica el xml y se agrega la inyeccion de dependencias. Es decir, a cada controller vamos a inyectarle cada manager para poder utilizarlos sin la necesidad de hacer new xxx().

CarListController.java

public class CarListController implements Controller {

// IoC 
private CarManager cm;
       
        ...
        ...
        ...

        public CarManager getCm() {
return cm;
}

public void setCm(CarManager cm) {
this.cm = cm;
}

}

CarNewController.java


public class CarNewController  extends SimpleFormController{
List<Brand> brandList = new ArrayList<Brand>();
List<Car> carList = new ArrayList<Car>();
// IoC 
private BrandManager bm;

        ...
        ...
        ...



        public BrandManager getBm() {
return bm;
}

public void setBm(BrandManager bm) {
this.bm = bm;
}
}

Esta forma de inyección permite que la creación de los managers sea del tipo Singleton lo cual evita tener múltiples instancias del manager

Descargar el codigo completo

Por favor, haga clic aqui para empezar la descarga

Spring MVC II - Form Processing & Validations

Este tutorial pretende explicar el procesamiento de formularios mediante Spring MVC de forma muy simple.
Antes de leer este tutorial, es necesario leer http://java-all-frameworks.blogspot.com/2011/08/spring-mvc-helloworld-en-5-minutos.html para poder entender el codigo que mostraremos a continuación.

El controlador va a tener dos roles:
  • Va a inicializar el form
  • Va a procesar el form



Front Controller

El controlador va a extender de SimpleFormController (Clase util para inicializar y procesar formularios mediante Spring MVC)

Command

Por otra parte, vamos a tener un objeto Command que actuara como el ActionForm de Struts pero con la diferencia que no va a tener que extender de nada. Lo que hace posible que se trabaje directamente con la clase del modelo (Ej, vamos a trabajar con la clase Car)

SpringHelloWorld-servlet.xml

<bean name="/new_car.html" class="springmvc.web.CarNewController">
<property name="commandClass" value="springmvc.model.Car" />
<property name="formView" value="car_new" />
<property name="successView" value="car_list.html"/>
<property name="validator">
        <bean class="springmvc.validator.CarValidator"/>
        </property>
</bean>

Mediante la property commandClass vamos a definir el form/modelo/dto (en este caso vamos a utilizar directamente el objeto del modelo (Car).
Mediante esto, vamos a poder utilizar directamente los campos (properties) del objeto Car en la jsp para que se efectué automáticamente el binding entre lo que el usuario va a ingresar por pantalla (jsp) y como va a llegar estos valores al controller.

Mediante la property formView vamos a definir cual sera la JSP que se mostrara como formulario.

Mediante la property successView vamos a definir cual sera la JSP que se mostrara cuando presionamos submit en el formulario.

Mediante la property validator vamos a definir cual sera la clase que interceptara el submit y efectuara las validaciones pertinentes. En este caso, sera CarValidator.

CarNewController.java

package springmvc.web;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;

import org.springframework.web.bind.ServletRequestDataBinder;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.SimpleFormController;
import org.springframework.web.servlet.view.RedirectView;

import springmvc.model.Brand;
import springmvc.model.Car;

/**
 * Struts controllers extend Action whereas Spring controllers extend/implement a 
 * Controller class/interface. 
 * There are many of them you can choose from. The most basic is Controller, 
 * When it comes to process a form, SimpleFormController is generally used.
 * 
 * A form controller has two roles: initialize the form's initial values 
 * and process/persist the car 
 */
public class CarNewController  extends SimpleFormController{
List<Brand> brandList = new ArrayList<Brand>();
List<Car> carList = new ArrayList<Car>();

/**
*  Initialize the Command used to init the form
*  
*  A Command object is used to store the form values: it's equivalent to a 
*  Struts Action Form. However, it doesn't have to extend nor 
         *  implement anything. 
*  So it's even possible to directly use the model class! 
         * (Car in our example)
*  
*  Se va a llamar la primera vez q se ingresa al form como 
         * cuando presionamos submit.
*/
@Override
protected Object formBackingObject(HttpServletRequest request) 
                                             throws Exception {
System.out.println("formBackingObject()");
// Inicializamos los valores que queremos que aparezcan 
                // por primera vez al ingresar al formulario
Car defaultCar = new Car();
defaultCar.setModel("new model");
defaultCar.setPrice(new BigDecimal(15000));
return defaultCar;
}

/**
*  Set the view attributes (using a Map)
*  These are called after:
*   a. initBinder()
*   b. onSubmit()
*  
*  Se va a llamar solamente la primera vez q si ingrese al formulario.
*/
@Override
protected Map referenceData(HttpServletRequest request) throws Exception {
System.out.println("referenceData()");
// Inicializamos los valores que vamos a mostrar en 
                // cada campo del formulario
Map<Object, Object> dataMap = new HashMap<Object, Object>();
dataMap.put("brandList", getBrandList());
return dataMap;
}

/**
* This method prevent Spring to do some bindings and so them by 
         * ourselves if needed. 
* Here we used the brand id parameter to set the actual Brand
* Se va a llamar la primera vez q se ingresa al form como cuando 
         * presionamos submit.
*/
@Override
protected void initBinder(HttpServletRequest request, 
                                 ServletRequestDataBinder binder) 
                                            throws Exception {
System.out.println("initBinder()");
binder.setDisallowedFields(new String[] {"brand"});
String brand = request.getParameter("brand");
System.out.println("brand input: " + brand);
String model = request.getParameter("model");
System.out.println("model input: " + model);
String price = request.getParameter("price");
System.out.println("price input: " + price);

Car car = (Car)binder.getTarget();
// set car's brand from request parameter brand id
Long brandId = null;
try {
brandId = Long.parseLong(brand);
} catch (Exception e) {}
if (brandId != null) {
Brand brandSelected = getBrandById(brandId);
car.setBrand(brandSelected);
}    
}

/**
* This method has the main code. In this case, we used the command object, 
* which is a Car, to create a new Car.
* Solo se va a llamar cuando presionamos submit.
*/
@Override
public ModelAndView onSubmit(Object command) throws ServletException {
System.out.println("onSubmit() INICIO");
Car car = (Car)command;
System.out.println("******* Nuevo Auto ********"); 
System.out.println("CAR " + car);
System.out.println("******* Nuevo Auto ********");
carList.add(car);
System.out.println("******* Listado de Autos INICIO ********");
for (Car c : carList) {
System.out.println("Car " + c);
}
System.out.println("******* Listado de Autos FIN ********");
System.out.println("onSubmit() FIN");
return new ModelAndView(new RedirectView(getSuccessView()));
}
/********************************************/
/*********** private methods ***************/
/********************************************/
private List<Brand> getBrandList(){
brandList.clear();
Brand b1 = new Brand();
b1.setCountry("Argentina");
b1.setId(10l);
b1.setName("Brand 1");
Brand b2 = new Brand();
b2.setCountry("Brasil");
b2.setId(20l);
b2.setName("Brand 2");
brandList.add(b1);
brandList.add(b2);
return brandList;
}
private Brand getBrandById(long id){

for (Brand brand : brandList) {
if (brand.getId().equals(id)){
return brand;
}
}
return null;
}
}


CarValidator.java

package springmvc.validator;

import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;

import springmvc.model.Car;

public class CarValidator implements Validator {

public void validate(Object obj, Errors errors) {

Car car = (Car) obj;

ValidationUtils.rejectIfEmptyOrWhitespace(
                       errors, "model", "field.required", "Required field");
ValidationUtils.rejectIfEmptyOrWhitespace(
                       errors, "price", "field.required", "Required field");

if ( !errors.hasFieldErrors("price")) {
if (car.getPrice().intValue() == 0)
errors.rejectValue("price", "not_zero", 
                                        "Can't be free!");
}
}

/**
* This method declare classes supported by this validator
*/
@Override
public boolean supports(Class aClass) {

return Car.class.equals(aClass);
}

}

car_new.jsp


<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>

<html>


<head>
  <title>New Sponsor</title>
  <style>
    .error {
    color: red;
    }
  </style>  
</head>


<body>
<h1>New Car</h1>

<form:form method="post">

Brand<br />
<form:select path="brand">
  <form:options items="${brandList}" itemLabel="name" itemValue="id" />
</form:select>
<br /><br />

Model <form:errors path="model" cssClass="error"/><br />
<form:input path="model"/><br /><br />

Price <form:errors path="price" cssClass="error"/><br />
<form:input path="price"/><br /><br />

<input type="submit" value="Submit">

</form:form>
</body>
</html>


Url del proyecto

Proyecto completo en el eclipse


Descargar ejemplo completo

Para descargar el código completo, por favor haga clic aqui