ApplicationContextAware and BeanNameAware
- The Aware interface has the feel of the listener, callback, or observer design patterns.
- Aware interface, which is a super interface to the two.The xxxAware interface is a common pattern used within the Spring framework.
- They are typically used to allow a Spring managed bean to be given an object (via the interfaces setXxx method) at Spring bootstrap time.During bootstrapping, Spring will examine each bean to determine if it implements any of the xxxAware interfaces. When one is found, it invokes the interface method, providing the piece of information that is being asked for.
In Spring Bean Lifecycle from creation to Destruction is managed by Spring Container. There may be scenarios where we would want to access bean created by spring container from non spring managed class. The beans created by spring container is available in ApplicationContext. Whenever there are any changes to the bean it would be updated in applicationcontext.
By implementing ApplicationContextAware in the bean which should be accessed outside and calling the setApplicationContext when new ClassPathXmlApplicationContext is called from outside class we can have access to bean from context.
SpringBeans.xml
<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-3.0.xsd"> <bean id="helloBean" class="com.mkyong.core.HelloWorld"> <property name="name" value="Mugil" /> </bean> </beans>
HelloWorld.java – Spring Bean
public class HelloWorld implements ApplicationContextAware { private String name; private ApplicationContext objContext = null; public void setName(String name) { this.name = name; } public void printHello() { System.out.println("Spring 3 : Hello ! " + name); } public void setApplicationContext(ApplicationContext arg0) throws BeansException { this.objContext = arg0; System.out.println("Called when Object to new ClassPathXmlApplicationContext('springbeans.xml') is created"); } }
App.java
public class App { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("SpringBeans.xml"); HelloWorld obj = (HelloWorld) context.getBean("helloBean"); obj.printHello(); } }
Output
Called when Object to new ClassPathXmlApplicationContext('springbeans.xml') is created Spring 3 : Hello ! Mugil
When spring instantiates beans, it looks for a couple of interfaces like ApplicationContextAware and InitializingBean. If they are found, the methods are invoked.
Class<?> beanClass = beanDefinition.getClass(); Object bean = beanClass.newInstance(); if (bean instanceof ApplicationContextAware) { ((ApplicationContextAware) bean).setApplicationContext(ctx); }
In newer version it may be better to use annotations, rather than implementing spring-specific interfaces
@Inject // or @Autowired private ApplicationContext ctx
When BeanPostProcessor implement Object to execute the postProcessBeforeInitialization method,for example ApplicationContextAwareProcessor that added before.
private void invokeAwareInterfaces(Object bean) { if (bean instanceof Aware) { if (bean instanceof EnvironmentAware) { ((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment()); } if (bean instanceof EmbeddedValueResolverAware) { ((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver( new EmbeddedValueResolver(this.applicationContext.getBeanFactory())); } if (bean instanceof ResourceLoaderAware) { ((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext); } if (bean instanceof ApplicationEventPublisherAware) { ((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext); } if (bean instanceof MessageSourceAware) { ((MessageSourceAware) bean).setMessageSource(this.applicationContext); } if (bean instanceof ApplicationContextAware) { ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext); } } }
When it is Invoked
Shape.java
//Application System out would be Printed ApplicationContext objContext = new ClassPathXmlApplicationContext("spring1.xml"); //this.strTriangle would be printed BeanFactory objBeanFactory = new XmlBeanFactory(new FileSystemResource("spring.xml")); Triangle objTriangle2 = (Triangle)objBeanFactory.getBean("triangleName");
BeanFactoryAware
BeanFactory is used for BeanFactoryAware whereas ApplicationContextAware is used for ApplicationContext.Note that the ApplicationContext interface is a subclass of BeanFactory, and provides additional methods on top of the basic BeanFactory interface.
BeanNameAware Interface
Bean implementing this interface can get its name defined in the Spring container
One possible area of use could be if your building on/ extending the spring framework and would like to acquire the bean name for logging purposes/wiring them etc.
MyBeanName.java
public class MyBeanName implements BeanNameAware { @Override public void setBeanName(String beanName) { System.out.println(beanName); } }
Config.java
@Configuration public class Config { @Bean(name = "myCustomBeanName") public MyBeanName getMyBeanName() { return new MyBeanName(); } }
myCustomBeanName
- beanName property represents the bean id registered in the Spring container.When a new bean is given a name in the spring container and if you want to access the name then BeanNameAware should be used
- In the above example when the code is run it outputs myCustomBeanName which is the name offered to bean by container at runtime.
- If no name is given its going to print name of the method – getMyBeanName as bean name
If you require access to the additional features available on an ApplicationContext? If so, then you should of course use ApplicationContextAware. If not, BeanFactoryAware will be sufficient.Amongst many other things, an ApplicationContext has additional methods for inspecting the beans e.g. containsBeanDefinition, getBeanDefinitionCount, getBeanDefinitionNames, getBeanNamesForType, getBeansOfType that may be useful to you and which are not available on BeanFactory
we should avoid using any of the Aware interfaces, unless we need them. Implementing these interfaces will couple the code to the Spring framework.
————————————————————————————————————————————————————
Inheriting Bean Definition
spring.xml
<beans> <bean id="parentTriangle" class="com.mugil.shapes.Triangle"> <property name="pointA"> <ref bean="pointA"/> </property> </bean> <bean id="triangleId" class="com.mugil.shapes.Triangle" parent="parentTriangle"> <property name="pointB" ref="pointB"/> <property name="pointC" ref="pointC"/> </bean> <beans>
Above the bean parentTriangle is inherited by the child bean triangleId
spring.xml
Bean definition can also be made as abstract by using abstract=”true” like one below
<beans> <bean id="parentTriangle" class="com.mugil.shapes.Triangle" abstract="true"> <property name="pointA"> <ref bean="pointA"/> </property> </bean> </beans>
Managing Lifecycle of Bean
- Note the object for Context objContext is referred using AbstractApplicationContext not ApplicationContext
- registerShutdownHook() registers a hook which gets called at the end of application for cleanup
Shape.java
AbstractApplicationContext objContext = new ClassPathXmlApplicationContext("spring1.xml"); objContext.registerShutdownHook();
Triangle.java
public class Triangle implements InitializingBean, DisposableBean { @Override public void destroy() throws Exception { System.out.println("DisposableBean Called"); } @Override public void afterPropertiesSet() throws Exception { System.out.println("InitializingBean Called"); } }
We can all initialize the methods which should be called for initialization in spring.xml as below
spring.xml
<bean id="triangleId" class="com.mugil.shapes.Triangle" init-method="myInit" destroy-method="myDestroy"> <property name="pointA" ref="pointA"/> <property name="pointB" ref="pointB"/> <property name="pointC" ref="pointC"/> </bean>
Triangle.java
public class Triangle implements InitializingBean, DisposableBean { public void myInit() { System.out.println("Custom Init Method"); } public void myDestroy() { System.out.println("Custom Destroy Method"); } }
Priority of method call when it is defined by using XML and Interface implementation
- Init method of XML will be called
- Custom Init method of Interface will be called
- Destroy method of XML will be called
- Custom Destroy method of Interface will be called
Bean Post Processor
- Will work before and after bean initialization
- Works only when called using application initialization of bean. Does not work with setter initialization
- called for every initialization of parent and child bean in class
Bean Post Processor
<beans> <bean id="triangleId" class="com.mugil.shapes.Triangle"> <property name="pointA" ref="pointA"/> <property name="pointB" ref="pointB"/> <property name="pointC" ref="pointC"/> </bean> <bean id="pointA" class="com.mugil.shapes.Point"> <property name="x" value="-20"/> <property name="y" value="0"/> </bean> <bean id="pointB" class="com.mugil.shapes.Point"> <property name="x" value="0"/> <property name="y" value="0"/> </bean> <bean id="pointC" class="com.mugil.shapes.Point"> <property name="x" value="20"/> <property name="y" value="0"/> </bean> <bean class="com.mugil.shapes.BeanInitialization"/> </beans>
BeanInitialization.java
public class BeanInitialization implements BeanPostProcessor { @Override public Object postProcessAfterInitialization(Object obj, String objName) throws BeansException { System.out.println("After Initialization of " + objName); return obj; } @Override public Object postProcessBeforeInitialization(Object obj, String objName) throws BeansException { System.out.println("Before Initialization of " + objName); return obj; } }
So the above code runs four times for bean initialization of pointA,pointB,pointC and Triangle
shape.java
public class Shape { public static void main(String[] args) { ApplicationContext objContext = new ClassPathXmlApplicationContext("spring1.xml"); Triangle objTriangle2 = (Triangle)objContext.getBean("triangleId"); . . . }
BeanFactoryPostProcessor initialization happens before the beans gets initialized in the bean factory.
BeanInitialization2.java
public class BeanInitialization2 implements BeanFactoryPostProcessor { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory arg0) throws BeansException { System.out.println("This is Bean factory Post Processor"); } }
spring.xml
<beans> <bean class="com.mugil.shapes.BeanInitialization2"/> </beans>