A. A Spring Bean represents a POJO (Plain Old Java Object) performing useful operation(s). All Spring Beans reside within a Spring IOC Container. The Spring Framework hides most of the complex infrastructure and the communication that happens between the Spring Container and the Spring Beans. The state-chart diagram below highlights these communications.
Q. What is a BeanFactory?
A. The BeanFactory is the actual container which instantiates, configures, and manages a number of beans. These beans typically collaborate with one another, and thus have dependencies between themselves.
Q. How would you go about wiring up the spring managed bean dependencies?
A. In general, the dependencies are wired via the Spring config file. For example, the MyBeanService depends on MyBeanDao, and MyBean depends on MyBeanService, etc via either constructor or setter injection.
Here is the big picture of the collaborating classes and interfaces.
Here are the libraries ( highlighted in blue rectangle) that needs to be in the classpath.The commons-logging is used by the spring-beans-xx.jar.
STEP 1: Firstly define the relevant interfaces. Coding to interfaces is a good design parctice that loosely couples your classes.
package test;
public interface MyBeanDao {
abstract void daoMethod();
}
package test;
public interface MyBeanService {
abstract void serviceMethod();
}
Now, define the concrete implementation for the above interfaces.
package test;
public class MyBeanDaoImpl implements MyBeanDao {
@Override
public void daoMethod() {
System.out.println("dao method invoked ...");
}
}
package test;
public class MyBeanServiceImpl implements MyBeanService {
private MyBeanDao beanDao;
@Override
public void serviceMethod() {
System.out.println("Service method invoked...");
beanDao.daoMethod();
}
public void setBeanDao(MyBeanDao beanDao) {
System.out.println("setter injection .....");
this.beanDao = beanDao;
}
}
Next, define the MyBean class that makes use of the MyBeanService and the
package test;
public class MyBean {
private MyBeanService beanService;
public MyBean(MyBeanService beanService) {
System.out.println("Constructor injection...");
this.beanService = beanService;
}
public void testMethod(){
System.out.println("My bean method invoked ....");
beanService.serviceMethod();
}
}
STEP 2: Wire up the beans and the dependencies via an IOC container like Spring. The beans.xml file is shown below.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.5.xsd">
<bean id="myBeanDao" class="test.MyBeanDaoImpl"/>
<bean id="myBeanService" class="test.MyBeanServiceImpl">
<!-- setter injection of dao into service -->
<property name="beanDao" ref="myBeanDao" />
</bean>
<bean id="myBean" class="test.MyBean">
<!-- constructor injection of service into mybean -->
<constructor-arg><ref bean="myBeanService" /></constructor-arg>
</bean>
</beans>
As you can see, the MyBeanDao is injected via the setter injection into MyBeanService, and the MyBeanService is injected into MyBean via the constructor injection.
Q. How do you bootstrap the initial bean?
A.
STEP 3: The TestSpring class is the client class that makes use of the MyBean. In order to access the initial entry point into your application, which in this case is "MyBean", it needs to be bootstrapped via a BeanFactory implementation class. It can be done a number of ways as demonstrated below.
Using the "ClassPathXmlApplicationContext"
package test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestSpring {
public static void main(String[] args) {
ClassPathXmlApplicationContext appContext = new ClassPathXmlApplicationContext(
new String[] {"test/beans.xml"});
BeanFactory factory = (BeanFactory) appContext;
MyBean bean = (MyBean)factory.getBean("myBean");
bean.testMethod();
}
}
Using the "FileSystemResource"
package test;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
public class TestSpring {
public static void main(String[] args) {
Resource res = new FileSystemResource("bin/test/beans.xml");
XmlBeanFactory factory = new XmlBeanFactory(res);
MyBean bean = (MyBean)factory.getBean("myBean");
bean.testMethod();
}
}
Using the "ClassPathResource"
package test;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
public class TestSpring {
public static void main(String[] args) {
ClassPathResource res = new ClassPathResource("test/beans.xml");
XmlBeanFactory factory = new XmlBeanFactory(res);
MyBean bean = (MyBean)factory.getBean("myBean");
bean.testMethod();
}
}
STEP 4: Run the TestSpring as a stand alone application, which prints the following.
setter injection .....
Constructor injection...
My bean method invoked ....
Service method invoked...
dao method invoked ...
When MyBean bean = (MyBean)factory.getBean("myBean"); is executed, the dependent beans are created and wired up by the IOC container. You can further extend this by providing the necessary hooks to further enhance your understanding.
Q. What would you do if it’s not practical (or impossible) to wire up your entire application into the Spring framework, but you still need a Spring loaded bean in order to perform a task?
A. For example,
- an auto generated web service client class! But you do want to use the dependency injection feature of Spring to get some of the other beans injected in to this class.
- A legacy code that needs to make use of a Spring bean.
The ApplicationContextAware interface provided by Spring allows you to wire some java classes which are unable (or you don’t want it) to be wired to the Spring application context.
STEP 1: The ApplicationContextAware interface makes sense when an object requires access to a set of collaborating beans.
package test;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public class MyServiceFactory implements ApplicationContextAware {
private ApplicationContext context;
public void testMethod2(){
System.out.println("Test method2 invoked ....");
}
@Override
public void setApplicationContext(ApplicationContext ctx)
throws BeansException {
System.out.println("setting application context ...");
this.context = ctx;
}
public MyBeanService getInstance(String accessCode) {
//.....some logic
MyBeanService beanService = (MyBeanService) context.getBean("myBeanService");
return beanService;
}
}
STEP 2: The beans2.xml file. The MyServiceFactory is not wired up.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.5.xsd">
<bean id="myBeanDao" class="test.MyBeanDaoImpl"/>
<bean id="myBeanService" class="test.MyBeanServiceImpl">
<!-- setter injection of dao into service -->
<property name="beanDao" ref="myBeanDao" />
</bean>
<!-- No DI wiring -->
<bean id="myServiceFactory" class="test.MyServiceFactory" />
</beans>
STEP 3: Finally, the client code that makes use of the MyServiceFactory class.
package test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestSpring2 {
public static void main(String[] args) {
ClassPathXmlApplicationContext appContext = new ClassPathXmlApplicationContext(
new String[] {"test/beans2.xml"});
BeanFactory factory = (BeanFactory) appContext;
MyServiceFactory servicefactory = (MyServiceFactory)factory.getBean("myServiceFactory");
MyBeanService service = servicefactory.getInstance("111");
service.serviceMethod();
}
}
The output will be:
29/11/2011 3:59:29 PM org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@145e044: startup date [Tue Nov 29 15:59:29 EST 2011]; root of context hierarchy
29/11/2011 3:59:29 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [test/beans2.xml]
29/11/2011 3:59:29 PM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@1a42792: defining beans [myBeanDao,myBeanService,myServiceFactory]; root of factory hierarchy
setter injection .....
setting application context ...
Service method invoked...
dao method invoked ...
Q. How would you create an application context from a web application?
A. As opposed to the BeanFactory, which will often be created programmatically, ApplicationContexts can be created declaratively using a ContextLoader. You can register an ApplicationContext using the ContextLoaderListener as shown below in the web.xml file. The Spring context listener provides more flexibility in terms of how an application is wired together. It uses the application's Spring configuration to determine what object to instantiate and loads the objects into the application context used by the servlet container.
<web-app>
.....
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
....
</web-app>
By default, it looks for a file named applicationContext.xml file in WEB-INF folder. But, you can configure the org.springframework.web.context.ContextLoaderListener class to use a context parameter called contextConfigLocation to determine the location of the Spring configuration file. The context parameter is configured using the context-parameter element. The context-param element has two children that specify parameters and their values. The param-name element specifies the parameter's name. The param-value element specifies the parameter's value.
<web-app>
...
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>WEB-INF/beans.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
...
</web-app>
Note: There will be only one ServletContext for each web application. ServletContext will be created while deploying the application. Once the ServletContext is created, it will be used by all the servlets and jsp files in the same application. ServletContext is also called as the application scope variables in the web application scenario. The ContextLoaderListener is in the spring-web-xxx.jar. It is quite handy to check the Spring API for org.springframework.web.context.support.XmlWebApplicationContext class that describes this.
For a WebApplicationContext that reads in a different bean definition format, you could define your own implementation, and define your implementation with the "contextClass" init parameter.
Here is another example with multiple Spring files.
<webapp>
..
<context-param>
<param-name>contextClass</param-name>
<param-value>com.myapp.MyappXmlWebApplicationContext</param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath*:/com/myapp/transactionContext.xml
classpath*:/com/myapp/daoContext.xml
classpath*:/com/myapp/override-daoContext${my.env}.xml
/WEB-INF/webservice-interceptor-config.xml
/WEB-INF/webservice-config.xml
classpath*:/cxf.xml
</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
The contextClass can be defined something like.
package com.myapp;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.web.context.support.ServletContextResourcePatternResolver;
import org.springframework.web.context.support.XmlWebApplicationContext;
public class MyappXmlWebApplicationContext extends XmlWebApplicationContext {
private static final String MY_ENV = "my.env";
private static final String MY_ENV_PLACEHOLDER = "\\$\\{" + MY_ENV + "\\}";
protected ResourcePatternResolver getResourcePatternResolver() {
return new PatchedResourcePatternResolver(this);
}
private static class PatchedResourcePatternResolver extends ServletContextResourcePatternResolver {
public PatchedResourcePatternResolver(ResourceLoader resourceLoader) {
super(resourceLoader);
}
public Resource[] getResources(String locationPattern) throws IOException {
locationPattern = locationPattern.replaceAll(MY_ENV_PLACEHOLDER, System.getProperty(MY_ENV, ""));
Resource[] resources = super.getResources(locationPattern);
if (0 < locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()).length()) {
return resources;
}
Collection<Resource> filteredResources = new ArrayList<Resource>();
for (int i = 0; i < resources.length; i++) {
Resource resource = resources[i];
if (!resource.getURL().getProtocol().equals("jar")) {
filteredResources.add(resource);
}
}
return (Resource[]) filteredResources.toArray(new Resource[filteredResources.size()]);
}
}
}
More Spring Interview Questions and Answers
- Spring Interview Questions and Answers: Overview
- Spring Interview Questions and Answers: read properties file
- Spring Interview Questions and Answers: Hibernate integration