Change AnnotatedHandlerRepositoryEventListener into a BeanPostProcessor which works better all around and processes every bean, looking for event handlers.

This commit is contained in:
Jon Brisbin
2012-10-29 10:24:30 -05:00
committed by Jon Brisbin
parent 334e079348
commit 9dd9afb93f
8 changed files with 210 additions and 218 deletions

View File

@@ -0,0 +1,173 @@
package org.springframework.data.rest.repository.context;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationListener;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.data.rest.repository.annotation.HandleAfterDelete;
import org.springframework.data.rest.repository.annotation.HandleAfterLinkSave;
import org.springframework.data.rest.repository.annotation.HandleAfterSave;
import org.springframework.data.rest.repository.annotation.HandleBeforeDelete;
import org.springframework.data.rest.repository.annotation.HandleBeforeLinkSave;
import org.springframework.data.rest.repository.annotation.HandleBeforeSave;
import org.springframework.data.rest.repository.annotation.RepositoryEventHandler;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
/**
* @author Jon Brisbin
*/
public class AnnotatedHandlerBeanPostProcessor implements ApplicationListener<RepositoryEvent>,
BeanPostProcessor {
private static final Logger LOG = LoggerFactory.getLogger(AnnotatedHandlerBeanPostProcessor.class);
private Multimap<Class<? extends RepositoryEvent>, EventHandlerMethod> handlerMethods = ArrayListMultimap.create();
@Override public void onApplicationEvent(RepositoryEvent event) {
Class<? extends RepositoryEvent> eventType = event.getClass();
if(!handlerMethods.containsKey(eventType)) {
return;
}
for(EventHandlerMethod handlerMethod : handlerMethods.get(eventType)) {
try {
Object src = event.getSource();
if(!ClassUtils.isAssignable(handlerMethod.targetType, src.getClass())) {
continue;
}
List<Object> params = new ArrayList<Object>();
params.add(src);
if(event instanceof BeforeLinkSaveEvent) {
params.add(((BeforeLinkSaveEvent)event).getLinked());
} else if(event instanceof AfterLinkSaveEvent) {
params.add(((AfterLinkSaveEvent)event).getLinked());
}
if(LOG.isDebugEnabled()) {
LOG.debug("Invoking " + event.getClass().getSimpleName() + " handler for " + event.getSource());
}
handlerMethod.method.invoke(handlerMethod.handler, params.toArray());
} catch(Exception e) {
throw new IllegalStateException(e);
}
}
}
@Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Override public Object postProcessAfterInitialization(final Object bean, String beanName) throws BeansException {
final Class<?> beanType = bean.getClass();
RepositoryEventHandler typeAnno = AnnotationUtils.findAnnotation(beanType, RepositoryEventHandler.class);
if(null == typeAnno) {
return bean;
}
Class<?>[] targetTypes = typeAnno.value();
if(targetTypes.length == 0) {
targetTypes = new Class<?>[]{null};
}
for(final Class<?> targetType : targetTypes) {
ReflectionUtils.doWithMethods(
beanType,
new ReflectionUtils.MethodCallback() {
@Override public void doWith(Method method)
throws IllegalArgumentException, IllegalAccessException {
inspect(targetType, bean, method, HandleBeforeSave.class, BeforeSaveEvent.class);
inspect(targetType, bean, method, HandleAfterSave.class, AfterSaveEvent.class);
inspect(targetType, bean, method, HandleBeforeLinkSave.class, BeforeLinkSaveEvent.class);
inspect(targetType, bean, method, HandleAfterLinkSave.class, AfterLinkSaveEvent.class);
inspect(targetType, bean, method, HandleBeforeDelete.class, BeforeDeleteEvent.class);
inspect(targetType, bean, method, HandleAfterDelete.class, AfterDeleteEvent.class);
}
},
new ReflectionUtils.MethodFilter() {
@Override public boolean matches(Method method) {
return (!method.isSynthetic()
&& !method.isBridge()
&& method.getDeclaringClass() != Object.class
&& !method.getName().contains("$"));
}
}
);
}
return bean;
}
private <T extends Annotation> void inspect(Class<?> targetType,
Object handler,
Method method,
Class<T> annoType,
Class<? extends RepositoryEvent> eventType) {
T anno = method.getAnnotation(annoType);
if(null != anno) {
try {
Class<?>[] targetTypes;
if(null == targetType) {
targetTypes = (Class<?>[])anno.getClass().getMethod("value", new Class[0]).invoke(anno);
} else {
targetTypes = new Class<?>[]{targetType};
}
for(Class<?> type : targetTypes) {
EventHandlerMethod m = new EventHandlerMethod(type, handler, method);
if(LOG.isInfoEnabled()) {
LOG.info("Annotated handler method found: " + m);
}
handlerMethods.put(eventType, m);
}
} catch(NoSuchMethodException e) {
if(LOG.isDebugEnabled()) {
LOG.debug(e.getMessage(), e);
}
} catch(InvocationTargetException e) {
if(LOG.isDebugEnabled()) {
LOG.debug(e.getMessage(), e);
}
} catch(IllegalAccessException e) {
if(LOG.isDebugEnabled()) {
LOG.debug(e.getMessage(), e);
}
}
}
}
private class EventHandlerMethod {
final Class<?> targetType;
final Method method;
final Object handler;
private EventHandlerMethod(Class<?> targetType, Object handler, Method method) {
this.targetType = targetType;
this.method = method;
this.handler = handler;
}
@Override public String toString() {
return "EventHandlerMethod{" +
"targetType=" + targetType +
", method=" + method +
", handler=" + handler +
'}';
}
}
}

View File

@@ -1,210 +0,0 @@
package org.springframework.data.rest.repository.context;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.data.rest.repository.annotation.HandleAfterDelete;
import org.springframework.data.rest.repository.annotation.HandleAfterLinkSave;
import org.springframework.data.rest.repository.annotation.HandleAfterSave;
import org.springframework.data.rest.repository.annotation.HandleBeforeDelete;
import org.springframework.data.rest.repository.annotation.HandleBeforeLinkSave;
import org.springframework.data.rest.repository.annotation.HandleBeforeSave;
import org.springframework.data.rest.repository.annotation.RepositoryEventHandler;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
/**
* {@link ApplicationListener} that will dispatch {@link RepositoryEvent}s to handlers annotated with {@link
* RepositoryEventHandler}.
*
* @author Jon Brisbin <jbrisbin@vmware.com>
*/
public class AnnotatedHandlerRepositoryEventListener implements ApplicationListener<RepositoryEvent>,
ApplicationContextAware,
InitializingBean {
private String basePackage;
private ApplicationContext applicationContext;
private Multimap<Class<? extends RepositoryEvent>, EventHandlerMethod> handlerMethods = ArrayListMultimap.create();
public AnnotatedHandlerRepositoryEventListener() {
}
public AnnotatedHandlerRepositoryEventListener(String basePackage) {
this.basePackage = basePackage;
}
@Override public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.applicationContext = applicationContext;
}
/**
* Get the base package in which to search for event handlers.
*
* @return Base package to search.
*/
public String getBasePackage() {
return basePackage;
}
/**
* Set the base package in which to search for event handlers.
*
* @param basePackage
* Base package to search for handlers.
*
* @return @this
*/
public AnnotatedHandlerRepositoryEventListener setBasePackage(String basePackage) {
this.basePackage = basePackage;
return this;
}
/**
* Get the base package in which to search for event handlers.
*
* @return Base package to search.
*/
public String basePackage() {
return basePackage;
}
/**
* Set the base package in which to search for event handlers.
*
* @param basePackage
* Base package to search for handlers.
*
* @return @this
*/
public AnnotatedHandlerRepositoryEventListener basePackage(String basePackage) {
this.basePackage = basePackage;
return this;
}
@Override public void afterPropertiesSet() throws Exception {
ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
scanner.addIncludeFilter(new AnnotationTypeFilter(RepositoryEventHandler.class, true, true));
for(BeanDefinition beanDef : scanner.findCandidateComponents(basePackage)) {
String typeName = beanDef.getBeanClassName();
Class<?> handlerType = ClassUtils.forName(typeName, ClassUtils.getDefaultClassLoader());
RepositoryEventHandler typeAnno = handlerType.getAnnotation(RepositoryEventHandler.class);
Class<?>[] targetTypes = typeAnno.value();
if(targetTypes.length == 0) {
targetTypes = new Class<?>[]{null};
}
for(final Class<?> targetType : targetTypes) {
for(final Object handler : applicationContext.getBeansOfType(handlerType).values()) {
ReflectionUtils.doWithMethods(
handler.getClass(),
new ReflectionUtils.MethodCallback() {
@Override public void doWith(Method method)
throws IllegalArgumentException, IllegalAccessException {
inspect(targetType, handler, method, HandleBeforeSave.class, BeforeSaveEvent.class);
inspect(targetType, handler, method, HandleAfterSave.class, AfterSaveEvent.class);
inspect(targetType, handler, method, HandleBeforeLinkSave.class, BeforeLinkSaveEvent.class);
inspect(targetType, handler, method, HandleAfterLinkSave.class, AfterLinkSaveEvent.class);
inspect(targetType, handler, method, HandleBeforeDelete.class, BeforeDeleteEvent.class);
inspect(targetType, handler, method, HandleAfterDelete.class, AfterDeleteEvent.class);
}
},
new ReflectionUtils.MethodFilter() {
@Override public boolean matches(Method method) {
return (!method.isSynthetic()
&& !method.isBridge()
&& method.getDeclaringClass() != Object.class
&& !method.getName().contains("$"));
}
}
);
}
}
}
}
@Override public void onApplicationEvent(RepositoryEvent event) {
Class<? extends RepositoryEvent> eventType = event.getClass();
if(!handlerMethods.containsKey(eventType)) {
return;
}
for(EventHandlerMethod handlerMethod : handlerMethods.get(eventType)) {
try {
Object src = event.getSource();
if(!ClassUtils.isAssignable(handlerMethod.targetType, src.getClass())) {
continue;
}
List<Object> params = new ArrayList<Object>();
params.add(src);
if(event instanceof BeforeLinkSaveEvent) {
params.add(((BeforeLinkSaveEvent)event).getLinked());
} else if(event instanceof AfterLinkSaveEvent) {
params.add(((AfterLinkSaveEvent)event).getLinked());
}
handlerMethod.method.invoke(handlerMethod.handler, params.toArray());
} catch(Exception e) {
throw new IllegalStateException(e);
}
}
}
private <T extends Annotation> void inspect(Class<?> targetType,
Object handler,
Method method,
Class<T> annoType,
Class<? extends RepositoryEvent> eventType) {
T anno = method.getAnnotation(annoType);
if(null != anno) {
try {
Class<?>[] targetTypes;
if(null == targetType) {
targetTypes = (Class<?>[])anno.getClass().getMethod("value", new Class[0]).invoke(anno);
} else {
targetTypes = new Class<?>[]{targetType};
}
for(Class<?> type : targetTypes) {
handlerMethods.put(eventType,
new EventHandlerMethod(type,
handler,
method));
}
} catch(NoSuchMethodException ignored) {
} catch(InvocationTargetException ignored) {
} catch(IllegalAccessException ignored) {
}
}
}
private class EventHandlerMethod {
final Class<?> targetType;
final Method method;
final Object handler;
private EventHandlerMethod(Class<?> targetType,
Object handler,
Method method) {
this.targetType = targetType;
this.method = method;
this.handler = handler;
}
}
}

View File

@@ -15,7 +15,6 @@ import org.springframework.data.rest.repository.annotation.RepositoryEventHandle
import org.springframework.data.rest.repository.context.AfterDeleteEvent
import org.springframework.data.rest.repository.context.AfterLinkSaveEvent
import org.springframework.data.rest.repository.context.AfterSaveEvent
import org.springframework.data.rest.repository.context.AnnotatedHandlerRepositoryEventListener
import org.springframework.data.rest.repository.context.BeforeDeleteEvent
import org.springframework.data.rest.repository.context.BeforeLinkSaveEvent
import org.springframework.data.rest.repository.context.BeforeSaveEvent
@@ -65,10 +64,6 @@ class ExtensionsSpec extends Specification {
@Configuration
class EventsApplicationConfig {
@Bean AnnotatedHandlerRepositoryEventListener repositoryEventListener() {
new AnnotatedHandlerRepositoryEventListener("org.springframework.data.rest.repository.spec");
}
@Bean PersonEventHandler personEventHandler() {
new PersonEventHandler()
}