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

@@ -0,0 +1,145 @@
/*
* 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.core.convert.converter;
import java.util.Comparator;
import java.util.Map;
import java.util.Map.Entry;
import org.springframework.core.convert.ConversionService;
import org.springframework.util.Assert;
import org.springframework.util.comparator.ComparableComparator;
/**
* A {@link Comparator} that converts values before they are compared. The specified
* {@link Converter} will be used to convert each value before it passed to the underlying
* {@code Comparator}.
*
* @author Phillip Webb
* @param <S> the source type
* @param <T> the target type
* @since 3.2
*/
public class ConvertingComparator<S, T> implements Comparator<S> {
private Comparator<T> comparator;
private Converter<S, T> converter;
/**
* Create a new {@link ConvertingComparator} instance.
*
* @param comparator the underlying comparator used to compare the converted values
* @param converter the converter
*/
@SuppressWarnings("unchecked")
public ConvertingComparator(Converter<S, T> converter) {
this(ComparableComparator.INSTANCE, converter);
}
/**
* Create a new {@link ConvertingComparator} instance.
*
* @param comparator the underlying comparator used to compare the converted values
* @param converter the converter
*/
public ConvertingComparator(Comparator<T> comparator, Converter<S, T> converter) {
Assert.notNull(comparator, "Comparator must not be null");
Assert.notNull(converter, "Converter must not be null");
this.comparator = comparator;
this.converter = converter;
}
/**
* Create a new {@link ComparableComparator} instance.
*
* @param comparator the underlying comparator
* @param conversionService the conversion service
* @param targetType the target type
*/
public ConvertingComparator(Comparator<T> comparator,
ConversionService conversionService, Class<? extends T> targetType) {
this(comparator, new ConversionServiceConverter<S, T>(
conversionService, targetType));
}
public int compare(S o1, S o2) {
T c1 = this.converter.convert(o1);
T c2 = this.converter.convert(o2);
return this.comparator.compare(c1, c2);
}
/**
* Create a new {@link ConvertingComparator} that compares {@link Map.Entry map
* entries} based on their {@link Map.Entry#getKey() keys}.
*
* @param comparator the underlying comparator used to compare keys
* @return a new {@link ConvertingComparator} instance
*/
public static <K, V> ConvertingComparator<Map.Entry<K, V>, K> mapEntryKeys(
Comparator<K> comparator) {
return new ConvertingComparator<Map.Entry<K,V>, K>(comparator, new Converter<Map.Entry<K, V>, K>() {
public K convert(Entry<K, V> source) {
return source.getKey();
}
});
}
/**
* Create a new {@link ConvertingComparator} that compares {@link Map.Entry map
* entries} based on their {@link Map.Entry#getValue() values}.
*
* @param comparator the underlying comparator used to compare values
* @return a new {@link ConvertingComparator} instance
*/
public static <K, V> ConvertingComparator<Map.Entry<K, V>, V> mapEntryValues(
Comparator<V> comparator) {
return new ConvertingComparator<Map.Entry<K,V>, V>(comparator, new Converter<Map.Entry<K, V>, V>() {
public V convert(Entry<K, V> source) {
return source.getValue();
}
});
}
/**
* Adapts a {@link ConversionService} and <tt>targetType</tt> to a {@link Converter}.
*/
private static class ConversionServiceConverter<S, T> implements Converter<S, T> {
private final ConversionService conversionService;
private final Class<? extends T> targetType;
public ConversionServiceConverter(ConversionService conversionService,
Class<? extends T> targetType) {
Assert.notNull(conversionService, "ConversionService must not be null");
Assert.notNull(targetType, "TargetType must not be null");
this.conversionService = conversionService;
this.targetType = targetType;
}
public T convert(S source) {
return this.conversionService.convert(source, this.targetType);
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2008 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.
@@ -29,6 +29,9 @@ import java.util.Comparator;
*/
public class ComparableComparator<T extends Comparable<T>> implements Comparator<T> {
@SuppressWarnings("rawtypes")
public static final ComparableComparator INSTANCE = new ComparableComparator();
public int compare(T o1, T o2) {
return o1.compareTo(o2);
}

View File

@@ -0,0 +1,72 @@
/*
* 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.util.comparator;
import java.util.Comparator;
import org.springframework.util.Assert;
/**
* Compares objects based on an arbitrary class order. Allows objects to be sorted based
* on the types of class that they inherit, for example: this comparator can be used to
* sort a list {@code Number}s such that {@code Long}s occur before {@code Integer}s.
*
* <p>Only the specified {@code instanceOrder} classes are considered during comparison.
* If two objects are both instances of the ordered type this comparator will return a
* {@code 0}. Consider combining with a {@link CompoundComparator} if additional sorting
* is required.
*
* @author Phillip Webb
* @param <T> The type of objects being compared
* @see CompoundComparator
* @since 3.2
*/
public class InstanceComparator<T> implements Comparator<T> {
private Class<?>[] instanceOrder;
/**
* Create a new {@link InstanceComparator} instance.
*
* @param instanceOrder the ordered list of classes that should be used when comparing
* objects. Classes earlier in the list will be be given a higher priority.
*/
public InstanceComparator(Class<?>... instanceOrder) {
Assert.notNull(instanceOrder, "InstanceOrder must not be null");
this.instanceOrder = instanceOrder;
}
public int compare(T o1, T o2) {
int i1 = getOrder(o1);
int i2 = getOrder(o2);
return (i1 < i2 ? -1 : (i1 == i2 ? 0 : 1));
}
private int getOrder(T object) {
if(object != null) {
for (int i = 0; i < instanceOrder.length; i++) {
if (instanceOrder[i].isInstance(object)) {
return i;
}
}
}
return instanceOrder.length;
}
}