Sort candidate @AspectJ methods deterministically

Update the ReflectiveAspectJAdvisorFactory class to sort candidate
AOP methods based on their annotation first and method name second.

Prior to this the order of aspects created from annotated methods
could differ depending on the underling JVM, as first noticed under
JDK7 in SPR-9729.

 - ConvertingComparator and InstanceComparator have been introduced in
   support of this change, per SPR-9730.

 - A shared static INSTANCE field has been added to ComparableComparator
   to avoid unnecessary instantiation costs within ConvertingComparator
   as well as to prevent generics warnings during certain caller
   scenarios.

Issue: SPR-9729, SPR-9730
This commit is contained in:
Phillip Webb
2012-08-27 13:24:58 -07:00
committed by Chris Beams
parent 719a9e120d
commit 77c9321967
6 changed files with 457 additions and 35 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2007 the original author or authors.
* Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,14 +16,20 @@
package org.springframework.aop.aspectj.annotation;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import org.aopalliance.aop.Advice;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.DeclareParents;
import org.aspectj.lang.annotation.Pointcut;
@@ -40,8 +46,12 @@ import org.springframework.aop.aspectj.DeclareParentsAdvisor;
import org.springframework.aop.framework.AopConfigException;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.ConvertingComparator;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.util.comparator.CompoundComparator;
import org.springframework.util.comparator.InstanceComparator;
/**
* Factory that can create Spring AOP Advisors given AspectJ classes from
@@ -52,10 +62,34 @@ import org.springframework.util.StringUtils;
* @author Adrian Colyer
* @author Juergen Hoeller
* @author Ramnivas Laddad
* @author Phillip Webb
* @since 2.0
*/
public class ReflectiveAspectJAdvisorFactory extends AbstractAspectJAdvisorFactory {
private static final Comparator<Method> METHOD_COMPARATOR;
static {
CompoundComparator<Method> comparator = new CompoundComparator<Method>();
comparator.addComparator(new ConvertingComparator<Method, Annotation>(
new InstanceComparator<Annotation>(
Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class),
new Converter<Method, Annotation>() {
public Annotation convert(Method method) {
AspectJAnnotation<?> annotation = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(method);
return annotation == null ? null : annotation.getAnnotation();
}
}));
comparator.addComparator(new ConvertingComparator<Method, String>(
new Converter<Method, String>() {
public String convert(Method method) {
return method.getName();
}
}));
METHOD_COMPARATOR = comparator;
}
public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory maaif) {
final Class<?> aspectClass = maaif.getAspectMetadata().getAspectClass();
final String aspectName = maaif.getAspectMetadata().getAspectName();
@@ -67,17 +101,12 @@ public class ReflectiveAspectJAdvisorFactory extends AbstractAspectJAdvisorFacto
new LazySingletonAspectInstanceFactoryDecorator(maaif);
final List<Advisor> advisors = new LinkedList<Advisor>();
ReflectionUtils.doWithMethods(aspectClass, new ReflectionUtils.MethodCallback() {
public void doWith(Method method) throws IllegalArgumentException {
// Exclude pointcuts
if (AnnotationUtils.getAnnotation(method, Pointcut.class) == null) {
Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);
if (advisor != null) {
advisors.add(advisor);
}
}
for (Method method : getAdvisorMethods(aspectClass)) {
Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);
if (advisor != null) {
advisors.add(advisor);
}
});
}
// If it's a per target aspect, emit the dummy instantiating aspect.
if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
@@ -96,6 +125,20 @@ public class ReflectiveAspectJAdvisorFactory extends AbstractAspectJAdvisorFacto
return advisors;
}
private List<Method> getAdvisorMethods(Class<?> aspectClass) {
final List<Method> methods = new LinkedList<Method>();
ReflectionUtils.doWithMethods(aspectClass, new ReflectionUtils.MethodCallback() {
public void doWith(Method method) throws IllegalArgumentException {
// Exclude pointcuts
if (AnnotationUtils.getAnnotation(method, Pointcut.class) == null) {
methods.add(method);
}
}
});
Collections.sort(methods, METHOD_COMPARATOR);
return methods;
}
/**
* Build a {@link org.springframework.aop.aspectj.DeclareParentsAdvisor}
* for the given introduction field.
@@ -104,7 +147,7 @@ public class ReflectiveAspectJAdvisorFactory extends AbstractAspectJAdvisorFacto
* @return <code>null</code> if not an Advisor
*/
private Advisor getDeclareParentsAdvisor(Field introductionField) {
DeclareParents declareParents = (DeclareParents) introductionField.getAnnotation(DeclareParents.class);
DeclareParents declareParents = introductionField.getAnnotation(DeclareParents.class);
if (declareParents == null) {
// Not an introduction field
return null;
@@ -224,6 +267,7 @@ public class ReflectiveAspectJAdvisorFactory extends AbstractAspectJAdvisorFacto
* Triggered by per-clause pointcut on non-singleton aspect.
* The advice has no effect.
*/
@SuppressWarnings("serial")
protected static class SyntheticInstantiationAdvisor extends DefaultPointcutAdvisor {
public SyntheticInstantiationAdvisor(final MetadataAwareAspectInstanceFactory aif) {