Showing posts with label BeanPostProcessor. Show all posts
Showing posts with label BeanPostProcessor. Show all posts
Friday, August 9, 2013
Injecting Test Doubles in Spring using Mockito and BeanPostProcessors
I'm pretty sure that if you have ever used Spring and are familliar with unit testing, you have encountered a problem related to injecting mocks / spies (Test Doubles) in the Spring's application context which you wouldn't want to modify. This article presents an approach how to solve this issue using Spring's components.
Monday, October 22, 2012
Spring BeanPostProcessor for a specified type
I was recently having a discussion how can one use a BeanPostProcessor to execute some logic for a specified class.
Looking at the Javadoc for the BeanPostProcessor one can read that:
Factory hook that allows for custom modification of new bean instances, e.g. checking for marker interfaces or wrapping them with proxies.So how can one create in an easy way a BeanPostProcessor for a precise type without creating a cascade of ifs or instance ofs? This is my concept of solving this problem - perhaps you know an easier one? :)
SomeService.java
package pl.grzejszczak.marcin.postprocessor;
public interface SomeService {
void methodA();
void methodB();
}
SomeServiceImpl.java
package pl.grzejszczak.marcin.postprocessor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SomeServiceImpl implements SomeService {
private static final Logger LOGGER = LoggerFactory.getLogger(SomeServiceImpl.class);
public SomeServiceImpl() {
LOGGER.debug("SomeServiceImpl - I'm created!");
}
private void afterInit() {
LOGGER.debug("SomeServiceImpl - After init!");
}
private void destroyMethod() {
LOGGER.debug("SomeServiceImpl - Destroy Method!");
}
@Override
public void methodA() {
LOGGER.debug("SomeServiceImpl - Method A executed");
}
@Override
public void methodB() {
LOGGER.debug("SomeServiceImpl - Method B executed");
}
}
SomeOtherService.java
package pl.grzejszczak.marcin.postprocessor;
public interface SomeOtherService {
void methodC();
void methodD();
}
SomeOtherServiceImpl.java
package pl.grzejszczak.marcin.postprocessor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SomeOtherServiceImpl implements SomeOtherService {
private static final Logger LOGGER = LoggerFactory.getLogger(SomeOtherServiceImpl.class);
public SomeOtherServiceImpl() {
LOGGER.debug("SomeOtherServiceImpl - I'm created!");
}
private void afterInit() {
LOGGER.debug("SomeOtherServiceImpl - After init!");
}
private void destroyMethod() {
LOGGER.debug("SomeOtherServiceImpl - Destroy Method!");
}
@Override
public void methodC() {
LOGGER.debug("SomeOtherServiceImpl - Method C executed");
}
@Override
public void methodD() {
LOGGER.debug("SomeOtherServiceImpl - Method D executed");
}
}
AbstractBeanPostProcessor.java
package pl.grzejszczak.marcin.postprocessor;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public abstract class AbstractBeanPostProcessor<T> implements BeanPostProcessor {
private Class<T> clazz;
public AbstractBeanPostProcessor(Class<T> clazz) {
this.clazz = clazz;
}
@Override
public Object postProcessAfterInitialization(Object bean, String name) throws BeansException {
checkConditions();
if (clazz.isAssignableFrom(bean.getClass())) {
doAfter();
}
return bean;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String name) throws BeansException {
checkConditions();
if (clazz.isAssignableFrom(bean.getClass())) {
doBefore();
}
return bean;
}
private void checkConditions() {
if (clazz == null) {
throw new NullArgumentException("Provide the interface for the post processor");
}
}
public abstract void doBefore();
public abstract void doAfter();
}
SomeServicePostProcessor.java
package pl.grzejszczak.marcin.postprocessor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
@Component
public class SomeServicePostProcessor extends AbstractBeanPostProcessor<SomeService> {
private static final Logger LOGGER = LoggerFactory.getLogger(SomeServicePostProcessor.class);
public SomeServicePostProcessor() {
super(SomeService.class);
}
@Override
public void doBefore() {
LOGGER.info("BEFORE it's init method has been executed but AFTER SomeServiceImpl has been instantiated I would like to do sth...");
}
@Override
public void doAfter() {
LOGGER.info("AFTER SomeServiceImpl has executed its init method I would like to do sth more...");
}
}
SpringMain.java
package pl.grzejszczak.marcin.postprocessor;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringMain {
public static void main(String[] args) {
ConfigurableApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
SomeService someService = context.getBean(SomeService.class);
someService.methodA();
someService.methodB();
SomeOtherService someOtherService = context.getBean(SomeOtherService.class);
someOtherService.methodC();
someOtherService.methodD();
context.close();
}
}
ApplicationContext.xml
<bean class="pl.grzejszczak.marcin.postprocessor.SomeServiceImpl" destroy-method="destroyMethod" init-method="afterInit"/> <bean class="pl.grzejszczak.marcin.postprocessor.SomeOtherServiceImpl" destroy-method="destroyMethod" init-method="afterInit"/> <bean class="pl.grzejszczak.marcin.postprocessor.SomeServicePostProcessor"/>
Logs
2012-10-23 00:20:38,863 INFO [main] org.springframework.context.support.ClassPathXmlApplicationContext:495 Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@28d11816: startup date [Tue Oct 23 00:20:38 CEST 2012]; root of context hierarchy 2012-10-23 00:20:38,956 INFO [main] org.springframework.beans.factory.xml.XmlBeanDefinitionReader:315 Loading XML bean definitions from class path resource [applicationContext.xml] 2012-10-23 00:20:39,213 INFO [main] org.springframework.beans.factory.support.DefaultListableBeanFactory:557 Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@6bb4469: defining beans [pl.grzejszczak.marcin.postprocessor.SomeServiceImpl#0,pl.grzejszczak.marcin.postprocessor.SomeOtherServiceImpl#0,pl.grzejszczak.marcin.postprocessor.SomeServicePostProcessor#0]; root of factory hierarchy 2012-10-23 00:20:39,214 DEBUG [main] pl.grzejszczak.marcin.postprocessor.SomeServiceImpl:10 SomeServiceImpl - I'm created! 2012-10-23 00:20:39,215 INFO [main] pl.grzejszczak.marcin.postprocessor.SomeServicePostProcessor:18 BEFORE its init method has been executed but AFTER SomeServiceImpl has been instantiated I would like to do sth... 2012-10-23 00:20:39,216 DEBUG [main] pl.grzejszczak.marcin.postprocessor.SomeServiceImpl:14 SomeServiceImpl - After init! 2012-10-23 00:20:39,216 INFO [main] pl.grzejszczak.marcin.postprocessor.SomeServicePostProcessor:23 AFTER SomeServiceImpl has executed its init method I would like to do sth more... 2012-10-23 00:20:39,220 DEBUG [main] pl.grzejszczak.marcin.postprocessor.SomeOtherServiceImpl:10 SomeOtherServiceImpl - I'm created! 2012-10-23 00:20:39,221 DEBUG [main] pl.grzejszczak.marcin.postprocessor.SomeOtherServiceImpl:14 SomeOtherServiceImpl - After init! 2012-10-23 00:20:39,225 DEBUG [main] pl.grzejszczak.marcin.postprocessor.SomeServiceImpl:23 SomeServiceImpl - Method A executed 2012-10-23 00:20:39,241 DEBUG [main] pl.grzejszczak.marcin.postprocessor.SomeServiceImpl:28 SomeServiceImpl - Method B executed 2012-10-23 00:20:39,242 DEBUG [main] pl.grzejszczak.marcin.postprocessor.SomeOtherServiceImpl:23 SomeOtherServiceImpl - Method C executed 2012-10-23 00:20:39,242 DEBUG [main] pl.grzejszczak.marcin.postprocessor.SomeOtherServiceImpl:28 SomeOtherServiceImpl - Method D executed 2012-10-23 00:20:39,242 INFO [main] org.springframework.context.support.ClassPathXmlApplicationContext:1020 Closing org.springframework.context.support.ClassPathXmlApplicationContext@28d11816: startup date [Tue Oct 23 00:20:38 CEST 2012]; root of context hierarchy 2012-10-23 00:20:39,243 INFO [main] org.springframework.beans.factory.support.DefaultListableBeanFactory:433 Destroying singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@6bb4469: defining beans [pl.grzejszczak.marcin.postprocessor.SomeServiceImpl#0,pl.grzejszczak.marcin.postprocessor.SomeOtherServiceImpl#0,pl.grzejszczak.marcin.postprocessor.SomeServicePostProcessor#0]; root of factory hierarchy 2012-10-23 00:20:39,244 DEBUG [main] pl.grzejszczak.marcin.postprocessor.SomeOtherServiceImpl:18 SomeOtherServiceImpl - Destroy Method! 2012-10-23 00:20:39,245 DEBUG [main] pl.grzejszczak.marcin.postprocessor.SomeServiceImpl:18 SomeServiceImpl - Destroy Method!As you can see it is quite easy, using generics and the BeanPostProcessor, to specify certain behavors for a given type (generics and constructor of SomeServiceImpl) or a group of types of classes (generics and constructor of SomeService).
Subscribe to:
Posts (Atom)