Showing posts with label ApplicationContext. Show all posts
Showing posts with label ApplicationContext. Show all posts

Tuesday, January 15, 2013

Drools integration with Spring vs manual rules creation



Hi!

 Often in your work you can come across with issues related to business logic. Let us assume that you have dozens of rules (for the time being in our project we have more than 50 and I used to work on a project where we had hundreds of those rules) that you have to implement, divide into some classes, subclasses, abstractions and of course unit test. This can be difficult and timeconsuming to both write and support. There are many ways of dealing with this problem and I will show you one of them - JBoss Drools.

Drools is a library specifically created for such purposes like implementing rules. As presented in Wikipedia:
"Drools is a rule engine implementaion based on Charles Forgy's Rete Algorithm tailored for the Java language." 
It contains a rule engine that can process rules wirtten using the Drools language (you can also provide rules in Excel spreadsheets! - perfect for Business side to support and maintain).

In the following example we will take a look at the way one can integrate JBoss Drools with Spring and an example of solving a similar problem without Drools.


Saturday, October 27, 2012

Spring AOP in security - controlling creation of UI components via aspects



The following post will show how in one of the projects that I took part in we used Spring's AOP to introduce some security related functionalities. The concept was such that in order for the user to see some UI components he needed to have a certain level of security privillages. If that requirement was not met then the UIComponent was not presented. Let's take a look at the project structure:


Then there were also the aopApplicationContext.xml :

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:context="http://www.springframework.org/schema/context"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:aop="http://www.springframework.org/schema/aop"
 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.1.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

 <aop:aspectj-autoproxy />
 <context:annotation-config />
 <context:component-scan base-package="pl.grzejszczak.marcin.aop">
  <context:exclude-filter type="annotation" expression="org.aspectj.lang.annotation.Aspect"/>
 </context:component-scan>
 <bean class="pl.grzejszczak.marcin.aop.interceptor.SecurityInterceptor" factory-method="aspectOf"/> 

</beans>

Now let's take a look at the most interesting lines of the Spring's application context.

First we have all the required schemas - I don't think that this needs to be explained in more depth.
Then we have:
<aop:aspectj-autoproxy/>


which enables the @AspectJ support.

Next there is the

<context:annotation-config />
<context:component-scan base-package="pl.grzejszczak.marcin.aop">
    <context:exclude-filter type="annotation" expression="org.aspectj.lang.annotation.Aspect"/>
</context:component-scan>

first we are turning on Spring configuration via annotations. Then deliberatly we exclude aspects from being initialized as beans by Spring itself. Why? Because...

<bean class="pl.grzejszczak.marcin.aop.interceptor.SecurityInterceptor" factory-method="aspectOf"/>

we want to create the aspect by ourselves and provide the factory-method="aspectOf" . By doing so our aspect will be included in the autowiring process of our beans - thus all the fields annotated with the @Autowired annotation will get the beans injected.

Now let's move on to the code:

UserServiceImpl.java

package pl.grzejszczak.marcin.aop.service;

import org.springframework.stereotype.Service;

import pl.grzejszczak.marcin.aop.type.Role;
import pl.grzejszczak.marcin.aop.user.UserHolder;

@Service
public class UserServiceImpl implements UserService {
 private UserHolder userHolder;

 @Override
 public UserHolder getCurrentUser() {
  return userHolder;
 }

 @Override
 public void setCurrentUser(UserHolder userHolder) {
  this.userHolder = userHolder;
 }

 @Override
 public Role getUserRole() {
  if (userHolder == null) {
   return null;
  }
  return userHolder.getUserRole();
 }
}

The class UserServiceImpl is immitating a service that would get the current user information from the db or from the current application context.

UserHolder.java

package pl.grzejszczak.marcin.aop.user;

import pl.grzejszczak.marcin.aop.type.Role;

public class UserHolder {
 private Role userRole;

 public UserHolder(Role userRole) {
  this.userRole = userRole;
 }

 public Role getUserRole() {
  return userRole;
 }

 public void setUserRole(Role userRole) {
  this.userRole = userRole;
 }
}


This is a simple holder class that holds information about current user Role.

Role.java

package pl.grzejszczak.marcin.aop.type;


public enum Role {
 ADMIN("ADM"), WRITER("WRT"), GUEST("GST");

 private String name;

 private Role(String name) {
  this.name = name;
 }

 public static Role getRoleByName(String name) {

  for (Role role : Role.values()) {

   if (role.name.equals(name)) {
    return role;
   }
  }

  throw new IllegalArgumentException("No such role exists [" + name + "]");
 }

 public String getName() {
  return this.name;
 }

 @Override
 public String toString() {
  return name;
 }
}

Role is an enum that defines a role for a person being an Admin, Writer or a Guest.

UIComponent.java

package pl.grzejszczak.marcin.aop.ui;

public abstract class UIComponent {
 protected String componentName;

 protected String getComponentName() {
  return componentName;
 }

}

An abstraction over concrete implementations of some UI components.

SomeComponentForAdminAndGuest.java

package pl.grzejszczak.marcin.aop.ui;

import pl.grzejszczak.marcin.aop.annotation.SecurityAnnotation;
import pl.grzejszczak.marcin.aop.type.Role;

@SecurityAnnotation(allowedRole = { Role.ADMIN, Role.GUEST })
public class SomeComponentForAdminAndGuest extends UIComponent {

 public SomeComponentForAdminAndGuest() {
  this.componentName = "SomeComponentForAdmin";
 }

 public static UIComponent getComponent() {
  return new SomeComponentForAdminAndGuest();
 }
}

This component is an example of a UI component extention that can be seen only by users who have roles of Admin or Guest.

SecurityAnnotation.java

package pl.grzejszczak.marcin.aop.annotation;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

import pl.grzejszczak.marcin.aop.type.Role;

@Retention(RetentionPolicy.RUNTIME)
public @interface SecurityAnnotation {
 Role[] allowedRole();
}


Annotation that defines a roles that can have this component created.

UIFactoryImpl.java

package pl.grzejszczak.marcin.aop.ui;

import org.apache.commons.lang.NullArgumentException;
import org.springframework.stereotype.Component;

@Component
public class UIFactoryImpl implements UIFactory {

 @Override
 public UIComponent createComponent(Class<? extends UIComponent> componentClass) throws Exception {
  if (componentClass == null) {
   throw new NullArgumentException("Provide class for the component");
  }
  return (UIComponent) Class.forName(componentClass.getName()).newInstance();
 }
}


A factory class that given the class of an object that extends UIComponent returns a new instance of the given UIComponent.

SecurityInterceptor.java


package pl.grzejszczak.marcin.aop.interceptor;

import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.util.Arrays;
import java.util.List;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import pl.grzejszczak.marcin.aop.annotation.SecurityAnnotation;
import pl.grzejszczak.marcin.aop.service.UserService;
import pl.grzejszczak.marcin.aop.type.Role;
import pl.grzejszczak.marcin.aop.ui.UIComponent;

@Aspect
public class SecurityInterceptor {
 private static final Logger LOGGER = LoggerFactory.getLogger(SecurityInterceptor.class);

 public SecurityInterceptor() {
  LOGGER.debug("Security Interceptor created");
 }

 @Autowired
 private UserService userService;

 @Pointcut("execution(pl.grzejszczak.marcin.aop.ui.UIComponent pl.grzejszczak.marcin.aop.ui.UIFactory.createComponent(..))")
 private void getComponent(ProceedingJoinPoint thisJoinPoint) {
 }

 @Around("getComponent(thisJoinPoint)")
 public UIComponent checkSecurity(ProceedingJoinPoint thisJoinPoint) throws Throwable {
  LOGGER.info("Intercepting creation of a component");

  Object[] arguments = thisJoinPoint.getArgs();
  if (arguments.length == 0) {
   return null;
  }

  Annotation annotation = checkTheAnnotation(arguments);
  boolean securityAnnotationPresent = (annotation != null);

  if (securityAnnotationPresent) {
   boolean userHasRole = verifyRole(annotation);
   if (!userHasRole) {
    LOGGER.info("Current user doesn't have permission to have this component created");
    return null;
   }
  }
  LOGGER.info("Current user has required permissions for creating a component");
  return (UIComponent) thisJoinPoint.proceed();
 }

 /**
  * Basing on the method's argument check if the class is annotataed with
  * {@link SecurityAnnotation}
  * 
  * @param arguments
  * @return
  */
 private Annotation checkTheAnnotation(Object[] arguments) {
  Object concreteClass = arguments[0];
  LOGGER.info("Argument's class - [{}]", new Object[] { arguments });
  AnnotatedElement annotatedElement = (AnnotatedElement) concreteClass;
  Annotation annotation = annotatedElement.getAnnotation(SecurityAnnotation.class);
  LOGGER.info("Annotation present - [{}]", new Object[] { annotation });
  return annotation;
 }

 /**
  * The function verifies if the current user has sufficient privilages to
  * have the component built
  * 
  * @param annotation
  * @return
  */
 private boolean verifyRole(Annotation annotation) {
  LOGGER.info("Security annotation is present so checking if the user can use it");
  SecurityAnnotation annotationRule = (SecurityAnnotation) annotation;
  List<Role> requiredRolesList = Arrays.asList(annotationRule.allowedRole());
  Role userRole = userService.getUserRole();
  return requiredRolesList.contains(userRole);
 }
}


This is the aspect defined at the pointcut of executing a function createComponent of the UIFactory interface. Inside the Around advice there is the logic that first checks what kind of an argument has been passed to the method createComponent (for example SomeComponentForAdminAndGuest.class). Next it is checking if this class is annotated with SecurityAnnotation and if that is the case it checks what kind of Roles are required to have the component created. Afterwards it checks if the current user (from UserService to UserHolder's Roles) has the required role to present the component. If that is the case thisJoinPoint.proceed() is called which in effect returns the object of the class that extends UIComponent.

Now let's test it - here comes the SpringJUnit4ClassRunner

AopTest.java

package pl.grzejszczak.marcin.aop;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import pl.grzejszczak.marcin.aop.service.UserService;
import pl.grzejszczak.marcin.aop.type.Role;
import pl.grzejszczak.marcin.aop.ui.SomeComponentForAdmin;
import pl.grzejszczak.marcin.aop.ui.SomeComponentForAdminAndGuest;
import pl.grzejszczak.marcin.aop.ui.SomeComponentForGuest;
import pl.grzejszczak.marcin.aop.ui.SomeComponentForWriter;
import pl.grzejszczak.marcin.aop.ui.UIFactory;
import pl.grzejszczak.marcin.aop.user.UserHolder;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:aopApplicationContext.xml" })
public class AopTest {

 @Autowired
 private UIFactory uiFactory;

 @Autowired
 private UserService userService;

 @Test
 public void adminTest() throws Exception {
  userService.setCurrentUser(new UserHolder(Role.ADMIN));
  Assert.assertNotNull(uiFactory.createComponent(SomeComponentForAdmin.class));
  Assert.assertNotNull(uiFactory.createComponent(SomeComponentForAdminAndGuest.class));
  Assert.assertNull(uiFactory.createComponent(SomeComponentForGuest.class));
  Assert.assertNull(uiFactory.createComponent(SomeComponentForWriter.class));
 }
}


And the logs:

pl.grzejszczak.marcin.aop.interceptor.SecurityInterceptor:26 Security Interceptor created
pl.grzejszczak.marcin.aop.interceptor.SecurityInterceptor:38 Intercepting creation of a component
pl.grzejszczak.marcin.aop.interceptor.SecurityInterceptor:48 Argument's class - [[class pl.grzejszczak.marcin.aop.ui.SomeComponentForAdmin]]
pl.grzejszczak.marcin.aop.interceptor.SecurityInterceptor:54 Annotation present - [@pl.grzejszczak.marcin.aop.annotation.SecurityAnnotation(allowedRole=[ADM])]
pl.grzejszczak.marcin.aop.interceptor.SecurityInterceptor:57 Security annotation is present so checking if the user can use it
pl.grzejszczak.marcin.aop.interceptor.SecurityInterceptor:70 Current user has required permissions for creating a component
pl.grzejszczak.marcin.aop.interceptor.SecurityInterceptor:38 Intercepting creation of a component
pl.grzejszczak.marcin.aop.interceptor.SecurityInterceptor:48 Argument's class - [[class pl.grzejszczak.marcin.aop.ui.SomeComponentForAdminAndGuest]]
pl.grzejszczak.marcin.aop.interceptor.SecurityInterceptor:54 Annotation present - [@pl.grzejszczak.marcin.aop.annotation.SecurityAnnotation(allowedRole=[ADM, GST])]
pl.grzejszczak.marcin.aop.interceptor.SecurityInterceptor:57 Security annotation is present so checking if the user can use it
pl.grzejszczak.marcin.aop.interceptor.SecurityInterceptor:70 Current user has required permissions for creating a component
pl.grzejszczak.marcin.aop.interceptor.SecurityInterceptor:38 Intercepting creation of a component
pl.grzejszczak.marcin.aop.interceptor.SecurityInterceptor:48 Argument's class - [[class pl.grzejszczak.marcin.aop.ui.SomeComponentForGuest]]
pl.grzejszczak.marcin.aop.interceptor.SecurityInterceptor:54 Annotation present - [@pl.grzejszczak.marcin.aop.annotation.SecurityAnnotation(allowedRole=[GST])]
pl.grzejszczak.marcin.aop.interceptor.SecurityInterceptor:57 Security annotation is present so checking if the user can use it
pl.grzejszczak.marcin.aop.interceptor.SecurityInterceptor:66 Current user doesn't have permission to have this component created
pl.grzejszczak.marcin.aop.interceptor.SecurityInterceptor:38 Intercepting creation of a component
pl.grzejszczak.marcin.aop.interceptor.SecurityInterceptor:48 Argument's class - [[class pl.grzejszczak.marcin.aop.ui.SomeComponentForWriter]]
pl.grzejszczak.marcin.aop.interceptor.SecurityInterceptor:54 Annotation present - [@pl.grzejszczak.marcin.aop.annotation.SecurityAnnotation(allowedRole=[WRT])]
pl.grzejszczak.marcin.aop.interceptor.SecurityInterceptor:57 Security annotation is present so checking if the user can use it
pl.grzejszczak.marcin.aop.interceptor.SecurityInterceptor:66 Current user doesn't have permission to have this component created

The unit test shows that for given Admin role only first two components get created whereas for the two others nulls are returned (due to the fact that user doesn't have proper rights).

That is how in our project we used Spring's AOP to create a simple framework that would check if the user can have the given component created or not. Thanks to this after having programmed the aspects one doesn't have to remember about writing any security related code since it will be done for him.

If you have any suggestions related to this post please feel free to comment it :)

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).