Friday, 1 April 2011

Moving to the Spring 3 BeanFactory

My previous blog on Updating to Spring 3 covered a few points about upgrading existing projects to use the Spring 3 libraries. This included Maven Pom file changes, highlighted links to Spring resources and documented the Spring 3 schema declaration.

Since then, I’ve gone through a whole bunch of Spring 2.x source code to update the Spring api calls from 2.x to 3, and can report that the biggest change was in updating the getBean(...) call as defined in the BeanFactory class.

In Spring 2.x, the getBean(...) method was defined as:

  Object getBean(String name) throws BeansException

All of which meant that you needed to explicitly cast your getBean(...) call, to change the object it returned from Object to your class:

    ApplicationContext ctx = new FileSystemXmlApplicationContext(
       
"example.xml");

    MyBean one =
(MyBean) ctx.getBean("MyBean1");

As most people know, the main problem with casting objects in this way is that that there isn’t any type checking on the returned class until run-time.

This has been improved in Spring 3 to use Java Generics:

  <T> T getBean(String name, Class<T> requiredType) throws BeansException

The upshot of this change is that you get compile time checking of your code. Using this change, your getBean(..) call changes to:


    ApplicationContext ctx = new FileSystemXmlApplicationContext(
       
"example4.xml");

    MyBean one = ctx.getBean
("MyBean1", MyBean.class);

There is one fly in the ointment however, in that as you’ve still got a XML declaration file for your beans, then if you get the class name wrong in that file, you’ll get one of the following exceptions, which means that Spring still needs to do some run-time type checking.

First possible example exception:

Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'MyBean1' defined in file [/Java_Projects2/Tips/marin-tips-spring-3/src/main/resources/example4_ProxyFactoryBean.xml]: Error setting property values; nested exception is org.springframework.beans.NotWritablePropertyException: Invalid property 'dependency' of bean class [example_4_aop.declarative_aop_proxyfactorybean.Example4_ProxyFactoryBean]: Bean property 'dependency' is not writable or has an invalid setter method. Does the parameter type of the setter match the return type of the getter?
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1341)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1067)
:
:
Caused by: org.springframework.beans.NotWritablePropertyException: Invalid property 'dependency' of bean class [example_4_aop.declarative_aop_proxyfactorybean.Example4_ProxyFactoryBean]: Bean property 'dependency' is not writable or has an invalid setter method. Does the parameter type of the setter match the return type of the getter?
at org.springframework.beans.BeanWrapperImpl.setPropertyValue(BeanWrapperImpl.java:1012)
at org.springframework.beans.BeanWrapperImpl.setPropertyValue(BeanWrapperImpl.java:857)

Another possible example exception:

Exception in thread "main" org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'MyBean1' must be of type [example_4_aop.declarative_aop_proxyfactorybean.MyBean], but was actually of type [example_4_aop.declarative_aop_proxyfactorybean.Example4_ProxyFactoryBean]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:348)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193)

Finally, note that the Spring 2.x call still exists in the BeanFactory class, so this update is optional.

No comments: