Sunday, March 23, 2014

Configuring Beans in Spring Framework.

This is the first post in the Configuring Spring Series.

Beans in Spring framework are proxies implementation of your classes. And following the Inversion of Control way of doing things, they are managed by the Spring framework container and made available for you to use.

Meaning you basically write your classes like you would normally do when programming in Java. After which, you then need to go through a process of configuration that would guide Spring regarding how your classes would be transformed into managed beans which you can then use to form your business logic.

There are different ways to have this configuration set up. And this post, first in the series of many, that would cover some of the configuration tasks in Spring, covers how to configure Spring to take your classes and have it turned into beans. This process is normally referred to as Bean wiring.

We would look at
  1. Configuration via XML
  2. Configuration via Java Classes: JavaConfig
  3. Configuration via @Component and @Autowired Annotation


1. Configuration via XML

Basically as the name says, your instructions about which classes get turned into Spring beans and how (its dependency, scope etc) are written in XML.

This, for a long time, was the only way to wire up beans in Spring framework.

You basically have an xml file that contains the instructions. e.g

<beans
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xsi:schemalocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="mybean" class="com.myapp.MyBean" ></bean>

</beans>

Where mybean is the bean id and myBean.class the Java class.

Since this post is not a detailed explanation of the anatomy of a spring XML configuration file,
see Bean Overview from the official Spring framework reference for a more detailed coverage of how and what goes into a bean definition in the XML configuration.

There is one salient thing I would like to take note of though. Which is: schema based configuration that allows the usage of namespaces.

Hiding of Bean Definition via Namespaces
A lot of features in Spring can be enabled by usage of dedicated namespaces in your spring xml definition file. This works by adding the appropriate scheme definition in the top section of your XML.

What these namespaces do is basically hide the necessary actual bean definition. That is, it is some sort of short code.

Instead of explicitly defining the necessary beans needed to make a particular feature work, you only define the necessary namespace and the beans would be automatically resolved into the required bean definitions at startup.

As anyone who is venturing into using Spring framework would quickly discover, you see the usage of these namespaces a lot. They are XML tags, but don't take the normal form for wiring bean.

For example the context scheme provides various useful namespaces. Let us look at <property-placeholder></property-placeholder> as an illustration.

The is used to map values from a Java property file into placeholders in your XML configuration.

First thing needed is to enable the context schema.

<beans
xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xsi:schemalocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

<!-- <bean/> definitions here -->

</beans>

If you then have your standard Java properties file (say db.properties)

jdbc.driverClassName=org.hsqldb.jdbcDriver
jdbc.url=jdbc:hsqldb:hsql://production:9002
jdbc.username=admin
jdbc.password=adminpass

You can use these properties in your XML configuration:

<bean class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" id="dataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>

And have the placeholders resolved into values in the properites file, but only if you have defined the bean responsible for getting this resolution done. Thus your complete configuration might look like this:

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="locations">
         <value>classpath:com/foo/db.properties</value>
   </property>
</bean>

<bean class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" id="dataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>


Where a bean of org.springframework.beans.factory.config.PropertyPlaceholderConfigurer is defined and configured with the location of the db.properties file.

This works. But with namespaces, you can replace the verbose bean definition with:
<context:property-placeholder location="classpath:com/foo/db.properties"> </context:property-placeholder>

so the new blob of configuration might look like this:

<context:property-placeholder location="classpath:com/foo/jdbc.properties"></context:property-placeholder>

<bean class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" id="dataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>

You have effectually replaced the explicit bean definition with <context:property-placeholder/> namespace.

And the reason why a bean can modify another bean definition (in this case, swapping the place holders with values from property files) is due to the fact that every Spring application goes through a start up phase in which certain pre/post processing can take place. In this example, the processing occurs after the Bean definition loading phase. That is after the XML file is parsed and the bean definitions are outlined.

There is what are called BeanFactoryPostProcessors which can intercept the bean definition and modify them. The org.springframework.beans.factory.config.PropertyPlaceholderConfigurer is a class that implements such BeanFactoryPostProcessor interface.

2. Configuration via Java Classes: JavaConfig

Just as the name says, you define your bean definition using normal everyday Java programming language syntax. Since this post is not about the details of wiring up beans (but an overview of the configuration options for bean wiring) you can follow up with the Spring JavaConfig reference guide.

One thing to take note of though, is that the @Configuration annotation is used to specify a Java Class as a configuration class which consist of bean configurations. But to use this in Spring 3, you need to add CGLIB library as dependency.


3. Configuration via @Component and @Autowired Annotation

Beans configuration is not just about specifying the Java class that would be used to create the bean, but also specifying its dependency.

<bean class="MySpringApp.FirstBean" id="firstBean"> </bean>

<bean class="MySpringApp.SecondBean" id="secondBean">
<property name="dependency" ref="firstBean"></property>
</bean>

secondBean is a bean defined to have dependency on firstBean. Same configuration in JavaConfig would look like:

@Configuration
class appConfig {

@Bean FirstBean firstBean() {
 return new FirstBean();
}

@Bean SecondBean secondBean() {
 SecondBean secondBean = new SecondBean();
 secondBean.setDependency(firstBean());
 return secondBean;
}

}

This process of defining a bean and specifying dependency can also be done via the usage of @Component and @Autowired annotations. For this to work you have to fundamentally change the way the beans are defined. With both XML and JavaConfig, you have a file that contains all the bean definitions; that is: a single configuration file maps to various beans. But in other to have @Component and @Autowired working, you would have each Java class file mapped into a single corresponding bean. For this we use Spring stereotype annotations which are a bunch of annotations that indicates what kind a class or method is. For our example we use the @Component annotation which is the parent annotation for all other annotation in the stereotype annotation hierarchy. Other Spring Stereotype includes: @Service (indicates service classes), @Repository (indicates data access classes), @Controller (indicates web classes- springMVC), @Configuration (indicates Java configuration)

So to get this one Java class, one Java bean going, annotate the Java class with @Component.

@Component
class SpecialClass {
 public SpecialClass() {}
}

With this, a bean with id specialClass (note the capitalisation) would be created. But before we go ahead to see how @Autowired comes into the picture, its worth mentioning how exactly the Java file with a @Component annotation get turned into a bean? How is it found?

Remember the namespaces earlier mentioned? There is a namespace that is used to activate a functionality in Spring framework where, when given a base package, Spring would scan files in the package hierarchy for files with specific annotations and act on them depending on the kind of annotations found. So for instance files that are scanned and found to have @Component annotations would get turned into beans.

The namespace that activates this functionality is <context:component-scan/> so it's usage might look thus:

<context:component-scan base-package="MySpringApp"></context:component-scan>

with this, the Java classes in MySpringApp package that are annotated with @Component would be found and turned into beans.

How does @Autowired come into play?

@Autowired is used for specifying the dependency injection. It can be used when the bean is defined via @Component or via the other ways for defining your bean. So our firstBean, secondBean example would look thus:

@Component
class FirstBean() {

}

and

@Component
class SecondBean() {

private FirstClass myDependency;

@Autowired
public void setDependency(FirstClass fc) {
this.myDependency = fc;
}

}

Here the dependency is @Autowired via method-injection. It could has easily be set via field injection too.

@Component
class SecondBean() {

@Autowired
private FirstClass myDependency;

}

Retrieving Instantiated Beans
We have seen how beans can be wired in Spring. The next thing to quickly briefly mention is how you get to retrieve the wired beans in your application.

When your beans are instantiated, they are done in what is referred to as an ApplicationContext . From the ApplicatioContext you can then retrieve the beans for use.

There are various ways you can create an ApplicationContext that would instantiate your bean using your bean definition.

1. ClassPathXmlApplicationContext

public static void main(String[] args) {
  ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
  HelloService helloService1 = (HelloService) context.getBean("helloServiceBean"); // retrieving via Id casting needed
  HelloService helloService2 = context.getBean("helloServiceBean", HelloService.class); // retrieving via Id casting not needed
  HelloService helloService3 = context.getBean(HelloService.class); //casting not needed
}

where spring-config.xml is the XML configuration and it must be in a classpath location:

<beans
xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xsi:schemalocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<bean class="org.myapp.HelloService" id="helloServiceBean"></bean>
</beans>

2. FileSystemXmlApplicationContext

This is similar to ClassPathXmlApplicationContext just that XML configuration is retrieved by specifying the file path, instead of relying on classpath.

...
ApplicationContext context = new FileSystemXmlApplicationContext("file:/path/to/spring-config.xml");
...

3. WebApplicationContextUtils

The WebApplicationContextUtils performs the same functionality as FileSystemXmlApplicationContext and ClassPathXmlApplicationContext but in a web context. So you would use it in a JSP file or in a Servlet.


...
private void requestHandler(HttpSessionEvent sessionEvent){
 
   HttpSession session = sessionEvent.getSession();
   ApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(session.getServletContext());
   HelloService helloService = context.getBean(HelloService.class);

}
...

As you can see, the location of the XML file (spring-config.xml) is not specified. So how does the WebApplicationContextUtils gets the configuration?

The WebApplicationContextUtils class contains convenience methods for retrieving configuration for a given ServletContext. But how is it that the WebApplicationContextUtils.getWebApplicationContext method can get the configuration? Because it is already specified in the servletContext. And how does this configuration get specified in servletContext? via configurations in the web.xml

so for the line of code found above to work. i.e.

ApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(session.getServletContext());

Then the web.xml would have to look thus:

<web-app version="2.4" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/j2ee" xsi:schemalocation="http://java.sun.com/xml/ns/j2ee
 http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

<display-name>Spring MVC Application</display-name>


<context-param>
  <param-name>contextConfigLocation</param-name>
  <param-value>/WEB-INF/spring-config.xml</param-value>
</context-param>

<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>

<servlet>
<servlet-name>helloServlet</servlet-name>
<servlet-class>com.springapp.mvc.helloServlet</servlet-class>
</servlet>

<servlet-mapping>
<servlet-name>helloServlet</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>

</web-app>


The important tags for us in the snippet above are the <context-param></context-param> and <listener-class></listener-class> The <context-param></context-param> is used to specify configuration that would be available to all servlets. i.e the servletContext In our case it is the bean configuration file. The <listener-class></listener-class> on the other hand, specifies the listener class that would be notified when the application is started (and destroyed) and used to pick the configuration specified in the <context-param></context-param>. This is how the bean initialisation is done and made available such that WebApplicationContextUtils.getWebApplicationContext(session.getServletContext()) can be used to retrieve it.

4.JavaConfigApplicationContextationContext and AnnotationConfigApplicationContext

You use JavaConfigApplicationContextationContext (or AnnotationConfigApplicationContext) when your bean definition is done via Java Class instead of XML. if you have a Java class with bean definitions named JavaConfig.java You load this into the application context via AnnotationConfigApplicationContext

ApplicationContext context = new JavaConfigApplicationContextationContext(JavaConfig.class);
 HelloService helloService = context.getBean(HelloService.class);

No comments: