Reduce ProxyCallbackFilter to key-only role after class generation
Avoids memory leaks from ProxyCallbackFilter-contained Advisors. Includes consistent ProxyCallbackFilter#equals/hashCode methods. Closes gh-26266 Closes gh-30615
This commit is contained in:
		
							parent
							
								
									e210f08dce
								
							
						
					
					
						commit
						045df81f14
					
				| 
						 | 
					@ -1,5 +1,5 @@
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * Copyright 2002-2022 the original author or authors.
 | 
					 * Copyright 2002-2023 the original author or authors.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
					 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 * you may not use this file except in compliance with the License.
 | 
					 * you may not use this file except in compliance with the License.
 | 
				
			||||||
| 
						 | 
					@ -22,6 +22,7 @@ import java.lang.reflect.Method;
 | 
				
			||||||
import java.util.ArrayList;
 | 
					import java.util.ArrayList;
 | 
				
			||||||
import java.util.Arrays;
 | 
					import java.util.Arrays;
 | 
				
			||||||
import java.util.Collection;
 | 
					import java.util.Collection;
 | 
				
			||||||
 | 
					import java.util.Collections;
 | 
				
			||||||
import java.util.List;
 | 
					import java.util.List;
 | 
				
			||||||
import java.util.Map;
 | 
					import java.util.Map;
 | 
				
			||||||
import java.util.concurrent.ConcurrentHashMap;
 | 
					import java.util.concurrent.ConcurrentHashMap;
 | 
				
			||||||
| 
						 | 
					@ -32,6 +33,8 @@ import org.springframework.aop.Advisor;
 | 
				
			||||||
import org.springframework.aop.DynamicIntroductionAdvice;
 | 
					import org.springframework.aop.DynamicIntroductionAdvice;
 | 
				
			||||||
import org.springframework.aop.IntroductionAdvisor;
 | 
					import org.springframework.aop.IntroductionAdvisor;
 | 
				
			||||||
import org.springframework.aop.IntroductionInfo;
 | 
					import org.springframework.aop.IntroductionInfo;
 | 
				
			||||||
 | 
					import org.springframework.aop.Pointcut;
 | 
				
			||||||
 | 
					import org.springframework.aop.PointcutAdvisor;
 | 
				
			||||||
import org.springframework.aop.TargetSource;
 | 
					import org.springframework.aop.TargetSource;
 | 
				
			||||||
import org.springframework.aop.support.DefaultIntroductionAdvisor;
 | 
					import org.springframework.aop.support.DefaultIntroductionAdvisor;
 | 
				
			||||||
import org.springframework.aop.support.DefaultPointcutAdvisor;
 | 
					import org.springframework.aop.support.DefaultPointcutAdvisor;
 | 
				
			||||||
| 
						 | 
					@ -41,6 +44,7 @@ import org.springframework.lang.Nullable;
 | 
				
			||||||
import org.springframework.util.Assert;
 | 
					import org.springframework.util.Assert;
 | 
				
			||||||
import org.springframework.util.ClassUtils;
 | 
					import org.springframework.util.ClassUtils;
 | 
				
			||||||
import org.springframework.util.CollectionUtils;
 | 
					import org.springframework.util.CollectionUtils;
 | 
				
			||||||
 | 
					import org.springframework.util.ObjectUtils;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Base class for AOP proxy configuration managers.
 | 
					 * Base class for AOP proxy configuration managers.
 | 
				
			||||||
| 
						 | 
					@ -72,15 +76,13 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/** Package-protected to allow direct access for efficiency. */
 | 
						/** Package-protected to allow direct access for efficiency. */
 | 
				
			||||||
	@SuppressWarnings("serial")
 | 
					 | 
				
			||||||
	TargetSource targetSource = EMPTY_TARGET_SOURCE;
 | 
						TargetSource targetSource = EMPTY_TARGET_SOURCE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/** Whether the Advisors are already filtered for the specific target class. */
 | 
						/** Whether the Advisors are already filtered for the specific target class. */
 | 
				
			||||||
	private boolean preFiltered = false;
 | 
						private boolean preFiltered = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/** The AdvisorChainFactory to use. */
 | 
						/** The AdvisorChainFactory to use. */
 | 
				
			||||||
	@SuppressWarnings("serial")
 | 
						private AdvisorChainFactory advisorChainFactory;
 | 
				
			||||||
	AdvisorChainFactory advisorChainFactory = new DefaultAdvisorChainFactory();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/** Cache with Method as key and advisor chain List as value. */
 | 
						/** Cache with Method as key and advisor chain List as value. */
 | 
				
			||||||
	private transient Map<MethodCacheKey, List<Object>> methodCache;
 | 
						private transient Map<MethodCacheKey, List<Object>> methodCache;
 | 
				
			||||||
| 
						 | 
					@ -89,21 +91,22 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
 | 
				
			||||||
	 * Interfaces to be implemented by the proxy. Held in List to keep the order
 | 
						 * Interfaces to be implemented by the proxy. Held in List to keep the order
 | 
				
			||||||
	 * of registration, to create JDK proxy with specified order of interfaces.
 | 
						 * of registration, to create JDK proxy with specified order of interfaces.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	@SuppressWarnings("serial")
 | 
					 | 
				
			||||||
	private List<Class<?>> interfaces = new ArrayList<>();
 | 
						private List<Class<?>> interfaces = new ArrayList<>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * List of Advisors. If an Advice is added, it will be wrapped
 | 
						 * List of Advisors. If an Advice is added, it will be wrapped
 | 
				
			||||||
	 * in an Advisor before being added to this List.
 | 
						 * in an Advisor before being added to this List.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	@SuppressWarnings("serial")
 | 
					 | 
				
			||||||
	private List<Advisor> advisors = new ArrayList<>();
 | 
						private List<Advisor> advisors = new ArrayList<>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private List<Advisor> advisorKey = this.advisors;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * No-arg constructor for use as a JavaBean.
 | 
						 * No-arg constructor for use as a JavaBean.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	public AdvisedSupport() {
 | 
						public AdvisedSupport() {
 | 
				
			||||||
 | 
							this.advisorChainFactory = DefaultAdvisorChainFactory.INSTANCE;
 | 
				
			||||||
		this.methodCache = new ConcurrentHashMap<>(32);
 | 
							this.methodCache = new ConcurrentHashMap<>(32);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -116,6 +119,15 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
 | 
				
			||||||
		setInterfaces(interfaces);
 | 
							setInterfaces(interfaces);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Internal constructor for {@link #getConfigurationOnlyCopy()}.
 | 
				
			||||||
 | 
						 * @since 6.0.10
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						private AdvisedSupport(AdvisorChainFactory advisorChainFactory, Map<MethodCacheKey, List<Object>> methodCache) {
 | 
				
			||||||
 | 
							this.advisorChainFactory = advisorChainFactory;
 | 
				
			||||||
 | 
							this.methodCache = methodCache;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * Set the given object as target.
 | 
						 * Set the given object as target.
 | 
				
			||||||
| 
						 | 
					@ -520,15 +532,27 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
 | 
				
			||||||
	 * replacing the TargetSource.
 | 
						 * replacing the TargetSource.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	AdvisedSupport getConfigurationOnlyCopy() {
 | 
						AdvisedSupport getConfigurationOnlyCopy() {
 | 
				
			||||||
		AdvisedSupport copy = new AdvisedSupport();
 | 
							AdvisedSupport copy = new AdvisedSupport(this.advisorChainFactory, this.methodCache);
 | 
				
			||||||
		copy.copyFrom(this);
 | 
							copy.copyFrom(this);
 | 
				
			||||||
		copy.targetSource = EmptyTargetSource.forClass(getTargetClass(), getTargetSource().isStatic());
 | 
							copy.targetSource = EmptyTargetSource.forClass(getTargetClass(), getTargetSource().isStatic());
 | 
				
			||||||
		copy.advisorChainFactory = this.advisorChainFactory;
 | 
					 | 
				
			||||||
		copy.interfaces = new ArrayList<>(this.interfaces);
 | 
							copy.interfaces = new ArrayList<>(this.interfaces);
 | 
				
			||||||
		copy.advisors = new ArrayList<>(this.advisors);
 | 
							copy.advisors = new ArrayList<>(this.advisors);
 | 
				
			||||||
 | 
							copy.advisorKey = new ArrayList<>(this.advisors.size());
 | 
				
			||||||
 | 
							for (Advisor advisor : this.advisors) {
 | 
				
			||||||
 | 
								copy.advisorKey.add(new AdvisorKeyEntry(advisor));
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		return copy;
 | 
							return copy;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void reduceToAdvisorKey() {
 | 
				
			||||||
 | 
							this.advisors = this.advisorKey;
 | 
				
			||||||
 | 
							this.methodCache = Collections.emptyMap();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Object getAdvisorKey() {
 | 
				
			||||||
 | 
							return this.advisorKey;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	//---------------------------------------------------------------------
 | 
						//---------------------------------------------------------------------
 | 
				
			||||||
	// Serialization support
 | 
						// Serialization support
 | 
				
			||||||
| 
						 | 
					@ -604,4 +628,51 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Stub for an Advisor instance that is just needed for key purposes,
 | 
				
			||||||
 | 
						 * allowing for efficient equals and hashCode comparisons against the
 | 
				
			||||||
 | 
						 * advice class and the pointcut.
 | 
				
			||||||
 | 
						 * @since 6.0.10
 | 
				
			||||||
 | 
						 * @see #getConfigurationOnlyCopy()
 | 
				
			||||||
 | 
						 * @see #getAdvisorKey()
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						private static class AdvisorKeyEntry implements Advisor {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							private final Class<?> adviceType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							@Nullable
 | 
				
			||||||
 | 
							private String classFilterKey;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							@Nullable
 | 
				
			||||||
 | 
							private String methodMatcherKey;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public AdvisorKeyEntry(Advisor advisor) {
 | 
				
			||||||
 | 
								this.adviceType = advisor.getAdvice().getClass();
 | 
				
			||||||
 | 
								if (advisor instanceof PointcutAdvisor pointcutAdvisor) {
 | 
				
			||||||
 | 
									Pointcut pointcut = pointcutAdvisor.getPointcut();
 | 
				
			||||||
 | 
									this.classFilterKey = ObjectUtils.identityToString(pointcut.getClassFilter());
 | 
				
			||||||
 | 
									this.methodMatcherKey = ObjectUtils.identityToString(pointcut.getMethodMatcher());
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							@Override
 | 
				
			||||||
 | 
							public Advice getAdvice() {
 | 
				
			||||||
 | 
								throw new UnsupportedOperationException();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							@Override
 | 
				
			||||||
 | 
							public boolean equals(Object other) {
 | 
				
			||||||
 | 
								return (this == other || (other instanceof AdvisorKeyEntry otherEntry &&
 | 
				
			||||||
 | 
										this.adviceType == otherEntry.adviceType &&
 | 
				
			||||||
 | 
										ObjectUtils.nullSafeEquals(this.classFilterKey, otherEntry.classFilterKey) &&
 | 
				
			||||||
 | 
										ObjectUtils.nullSafeEquals(this.methodMatcherKey, otherEntry.methodMatcherKey)));
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							@Override
 | 
				
			||||||
 | 
							public int hashCode() {
 | 
				
			||||||
 | 
								return this.adviceType.hashCode();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -26,14 +26,11 @@ import java.util.Map;
 | 
				
			||||||
import java.util.Set;
 | 
					import java.util.Set;
 | 
				
			||||||
import java.util.WeakHashMap;
 | 
					import java.util.WeakHashMap;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import org.aopalliance.aop.Advice;
 | 
					 | 
				
			||||||
import org.aopalliance.intercept.MethodInvocation;
 | 
					import org.aopalliance.intercept.MethodInvocation;
 | 
				
			||||||
import org.apache.commons.logging.Log;
 | 
					import org.apache.commons.logging.Log;
 | 
				
			||||||
import org.apache.commons.logging.LogFactory;
 | 
					import org.apache.commons.logging.LogFactory;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import org.springframework.aop.Advisor;
 | 
					 | 
				
			||||||
import org.springframework.aop.AopInvocationException;
 | 
					import org.springframework.aop.AopInvocationException;
 | 
				
			||||||
import org.springframework.aop.PointcutAdvisor;
 | 
					 | 
				
			||||||
import org.springframework.aop.RawTargetAccess;
 | 
					import org.springframework.aop.RawTargetAccess;
 | 
				
			||||||
import org.springframework.aop.TargetSource;
 | 
					import org.springframework.aop.TargetSource;
 | 
				
			||||||
import org.springframework.aop.support.AopUtils;
 | 
					import org.springframework.aop.support.AopUtils;
 | 
				
			||||||
| 
						 | 
					@ -205,12 +202,21 @@ class CglibAopProxy implements AopProxy, Serializable {
 | 
				
			||||||
				types[x] = callbacks[x].getClass();
 | 
									types[x] = callbacks[x].getClass();
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			// fixedInterceptorMap only populated at this point, after getCallbacks call above
 | 
								// fixedInterceptorMap only populated at this point, after getCallbacks call above
 | 
				
			||||||
			enhancer.setCallbackFilter(new ProxyCallbackFilter(
 | 
								ProxyCallbackFilter filter = new ProxyCallbackFilter(
 | 
				
			||||||
					this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
 | 
										this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset);
 | 
				
			||||||
 | 
								enhancer.setCallbackFilter(filter);
 | 
				
			||||||
			enhancer.setCallbackTypes(types);
 | 
								enhancer.setCallbackTypes(types);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// Generate the proxy class and create a proxy instance.
 | 
								// Generate the proxy class and create a proxy instance.
 | 
				
			||||||
			return (classOnly ? createProxyClass(enhancer) : createProxyClassAndInstance(enhancer, callbacks));
 | 
								// ProxyCallbackFilter has method introspection capability with Advisor access.
 | 
				
			||||||
 | 
								try {
 | 
				
			||||||
 | 
									return (classOnly ? createProxyClass(enhancer) : createProxyClassAndInstance(enhancer, callbacks));
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								finally {
 | 
				
			||||||
 | 
									// Reduce ProxyCallbackFilter to key-only state for its class cache role
 | 
				
			||||||
 | 
									// in the CGLIB$CALLBACK_FILTER field, not leaking any Advisor state...
 | 
				
			||||||
 | 
									filter.advised.reduceToAdvisorKey();
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		catch (CodeGenerationException | IllegalArgumentException ex) {
 | 
							catch (CodeGenerationException | IllegalArgumentException ex) {
 | 
				
			||||||
			throw new AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() +
 | 
								throw new AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() +
 | 
				
			||||||
| 
						 | 
					@ -294,9 +300,9 @@ class CglibAopProxy implements AopProxy, Serializable {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private Callback[] getCallbacks(Class<?> rootClass) throws Exception {
 | 
						private Callback[] getCallbacks(Class<?> rootClass) throws Exception {
 | 
				
			||||||
		// Parameters used for optimization choices...
 | 
							// Parameters used for optimization choices...
 | 
				
			||||||
		boolean exposeProxy = this.advised.isExposeProxy();
 | 
					 | 
				
			||||||
		boolean isFrozen = this.advised.isFrozen();
 | 
					 | 
				
			||||||
		boolean isStatic = this.advised.getTargetSource().isStatic();
 | 
							boolean isStatic = this.advised.getTargetSource().isStatic();
 | 
				
			||||||
 | 
							boolean isFrozen = this.advised.isFrozen();
 | 
				
			||||||
 | 
							boolean exposeProxy = this.advised.isExposeProxy();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Choose an "aop" interceptor (used for AOP calls).
 | 
							// Choose an "aop" interceptor (used for AOP calls).
 | 
				
			||||||
		Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);
 | 
							Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);
 | 
				
			||||||
| 
						 | 
					@ -776,7 +782,7 @@ class CglibAopProxy implements AopProxy, Serializable {
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	private static class ProxyCallbackFilter implements CallbackFilter {
 | 
						private static class ProxyCallbackFilter implements CallbackFilter {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		private final AdvisedSupport advised;
 | 
							final AdvisedSupport advised;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		private final Map<Method, Integer> fixedInterceptorMap;
 | 
							private final Map<Method, Integer> fixedInterceptorMap;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -857,9 +863,9 @@ class CglibAopProxy implements AopProxy, Serializable {
 | 
				
			||||||
			// Proxy is not yet available, but that shouldn't matter.
 | 
								// Proxy is not yet available, but that shouldn't matter.
 | 
				
			||||||
			List<?> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
 | 
								List<?> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
 | 
				
			||||||
			boolean haveAdvice = !chain.isEmpty();
 | 
								boolean haveAdvice = !chain.isEmpty();
 | 
				
			||||||
			boolean exposeProxy = this.advised.isExposeProxy();
 | 
					 | 
				
			||||||
			boolean isStatic = this.advised.getTargetSource().isStatic();
 | 
								boolean isStatic = this.advised.getTargetSource().isStatic();
 | 
				
			||||||
			boolean isFrozen = this.advised.isFrozen();
 | 
								boolean isFrozen = this.advised.isFrozen();
 | 
				
			||||||
 | 
								boolean exposeProxy = this.advised.isExposeProxy();
 | 
				
			||||||
			if (haveAdvice || !isFrozen) {
 | 
								if (haveAdvice || !isFrozen) {
 | 
				
			||||||
				// If exposing the proxy, then AOP_PROXY must be used.
 | 
									// If exposing the proxy, then AOP_PROXY must be used.
 | 
				
			||||||
				if (exposeProxy) {
 | 
									if (exposeProxy) {
 | 
				
			||||||
| 
						 | 
					@ -921,63 +927,18 @@ class CglibAopProxy implements AopProxy, Serializable {
 | 
				
			||||||
				return false;
 | 
									return false;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			AdvisedSupport otherAdvised = otherCallbackFilter.advised;
 | 
								AdvisedSupport otherAdvised = otherCallbackFilter.advised;
 | 
				
			||||||
			if (this.advised.isFrozen() != otherAdvised.isFrozen()) {
 | 
								return (this.advised.getAdvisorKey().equals(otherAdvised.getAdvisorKey()) &&
 | 
				
			||||||
				return false;
 | 
										AopProxyUtils.equalsProxiedInterfaces(this.advised, otherAdvised) &&
 | 
				
			||||||
			}
 | 
										ObjectUtils.nullSafeEquals(this.advised.getTargetClass(), otherAdvised.getTargetClass()) &&
 | 
				
			||||||
			if (this.advised.isExposeProxy() != otherAdvised.isExposeProxy()) {
 | 
										this.advised.getTargetSource().isStatic() == otherAdvised.getTargetSource().isStatic() &&
 | 
				
			||||||
				return false;
 | 
										this.advised.isFrozen() == otherAdvised.isFrozen() &&
 | 
				
			||||||
			}
 | 
										this.advised.isExposeProxy() == otherAdvised.isExposeProxy() &&
 | 
				
			||||||
			if (this.advised.getTargetSource().isStatic() != otherAdvised.getTargetSource().isStatic()) {
 | 
										this.advised.isOpaque() == otherAdvised.isOpaque());
 | 
				
			||||||
				return false;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			if (!AopProxyUtils.equalsProxiedInterfaces(this.advised, otherAdvised)) {
 | 
					 | 
				
			||||||
				return false;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			// Advice instance identity is unimportant to the proxy class:
 | 
					 | 
				
			||||||
			// All that matters is type and ordering.
 | 
					 | 
				
			||||||
			if (this.advised.getAdvisorCount() != otherAdvised.getAdvisorCount()) {
 | 
					 | 
				
			||||||
				return false;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			Advisor[] thisAdvisors = this.advised.getAdvisors();
 | 
					 | 
				
			||||||
			Advisor[] thatAdvisors = otherAdvised.getAdvisors();
 | 
					 | 
				
			||||||
			for (int i = 0; i < thisAdvisors.length; i++) {
 | 
					 | 
				
			||||||
				Advisor thisAdvisor = thisAdvisors[i];
 | 
					 | 
				
			||||||
				Advisor thatAdvisor = thatAdvisors[i];
 | 
					 | 
				
			||||||
				if (!equalsAdviceClasses(thisAdvisor, thatAdvisor)) {
 | 
					 | 
				
			||||||
					return false;
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				if (!equalsPointcuts(thisAdvisor, thatAdvisor)) {
 | 
					 | 
				
			||||||
					return false;
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			return true;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		private static boolean equalsAdviceClasses(Advisor a, Advisor b) {
 | 
					 | 
				
			||||||
			return (a.getAdvice().getClass() == b.getAdvice().getClass());
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		private static boolean equalsPointcuts(Advisor a, Advisor b) {
 | 
					 | 
				
			||||||
			// If only one of the advisor (but not both) is PointcutAdvisor, then it is a mismatch.
 | 
					 | 
				
			||||||
			// Takes care of the situations where an IntroductionAdvisor is used (see SPR-3959).
 | 
					 | 
				
			||||||
			return (!(a instanceof PointcutAdvisor pointcutAdvisor1) ||
 | 
					 | 
				
			||||||
					(b instanceof PointcutAdvisor pointcutAdvisor2 &&
 | 
					 | 
				
			||||||
							ObjectUtils.nullSafeEquals(pointcutAdvisor1.getPointcut(), pointcutAdvisor2.getPointcut())));
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		@Override
 | 
							@Override
 | 
				
			||||||
		public int hashCode() {
 | 
							public int hashCode() {
 | 
				
			||||||
			int hashCode = 0;
 | 
								return this.advised.getAdvisorKey().hashCode();
 | 
				
			||||||
			Advisor[] advisors = this.advised.getAdvisors();
 | 
					 | 
				
			||||||
			for (Advisor advisor : advisors) {
 | 
					 | 
				
			||||||
				Advice advice = advisor.getAdvice();
 | 
					 | 
				
			||||||
				hashCode = 13 * hashCode + advice.getClass().hashCode();
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			hashCode = 13 * hashCode + (this.advised.isFrozen() ? 1 : 0);
 | 
					 | 
				
			||||||
			hashCode = 13 * hashCode + (this.advised.isExposeProxy() ? 1 : 0);
 | 
					 | 
				
			||||||
			hashCode = 13 * hashCode + (this.advised.isOptimize() ? 1 : 0);
 | 
					 | 
				
			||||||
			hashCode = 13 * hashCode + (this.advised.isOpaque() ? 1 : 0);
 | 
					 | 
				
			||||||
			return hashCode;
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -28,6 +28,7 @@ import org.springframework.aop.MethodMatcher;
 | 
				
			||||||
import org.springframework.aop.Pointcut;
 | 
					import org.springframework.aop.Pointcut;
 | 
				
			||||||
import org.springframework.aop.support.AopUtils;
 | 
					import org.springframework.aop.support.AopUtils;
 | 
				
			||||||
import org.springframework.aop.support.DefaultPointcutAdvisor;
 | 
					import org.springframework.aop.support.DefaultPointcutAdvisor;
 | 
				
			||||||
 | 
					import org.springframework.aop.support.annotation.AnnotationMatchingPointcut;
 | 
				
			||||||
import org.springframework.aop.testfixture.advice.CountingBeforeAdvice;
 | 
					import org.springframework.aop.testfixture.advice.CountingBeforeAdvice;
 | 
				
			||||||
import org.springframework.aop.testfixture.interceptor.NopInterceptor;
 | 
					import org.springframework.aop.testfixture.interceptor.NopInterceptor;
 | 
				
			||||||
import org.springframework.beans.testfixture.beans.ITestBean;
 | 
					import org.springframework.beans.testfixture.beans.ITestBean;
 | 
				
			||||||
| 
						 | 
					@ -35,6 +36,7 @@ import org.springframework.beans.testfixture.beans.TestBean;
 | 
				
			||||||
import org.springframework.context.ApplicationContext;
 | 
					import org.springframework.context.ApplicationContext;
 | 
				
			||||||
import org.springframework.context.ApplicationContextException;
 | 
					import org.springframework.context.ApplicationContextException;
 | 
				
			||||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
 | 
					import org.springframework.context.support.ClassPathXmlApplicationContext;
 | 
				
			||||||
 | 
					import org.springframework.lang.NonNull;
 | 
				
			||||||
import org.springframework.lang.Nullable;
 | 
					import org.springframework.lang.Nullable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import static org.assertj.core.api.Assertions.assertThat;
 | 
					import static org.assertj.core.api.Assertions.assertThat;
 | 
				
			||||||
| 
						 | 
					@ -304,6 +306,8 @@ public class CglibProxyTests extends AbstractAopProxyTests implements Serializab
 | 
				
			||||||
		CglibAopProxy cglib = new CglibAopProxy(as);
 | 
							CglibAopProxy cglib = new CglibAopProxy(as);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		ITestBean proxy1 = (ITestBean) cglib.getProxy();
 | 
							ITestBean proxy1 = (ITestBean) cglib.getProxy();
 | 
				
			||||||
 | 
							ITestBean proxy1a = (ITestBean) cglib.getProxy();
 | 
				
			||||||
 | 
							assertThat(proxy1a.getClass()).isSameAs(proxy1.getClass());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		mockTargetSource.setTarget(proxy1);
 | 
							mockTargetSource.setTarget(proxy1);
 | 
				
			||||||
		as = new AdvisedSupport(new Class<?>[]{});
 | 
							as = new AdvisedSupport(new Class<?>[]{});
 | 
				
			||||||
| 
						 | 
					@ -313,6 +317,39 @@ public class CglibProxyTests extends AbstractAopProxyTests implements Serializab
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		ITestBean proxy2 = (ITestBean) cglib.getProxy();
 | 
							ITestBean proxy2 = (ITestBean) cglib.getProxy();
 | 
				
			||||||
		assertThat(proxy2).isInstanceOf(Serializable.class);
 | 
							assertThat(proxy2).isInstanceOf(Serializable.class);
 | 
				
			||||||
 | 
							assertThat(proxy2.getClass()).isNotSameAs(proxy1.getClass());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ITestBean proxy2a = (ITestBean) cglib.getProxy();
 | 
				
			||||||
 | 
							assertThat(proxy2a).isInstanceOf(Serializable.class);
 | 
				
			||||||
 | 
							assertThat(proxy2a.getClass()).isSameAs(proxy2.getClass());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							mockTargetSource.setTarget(proxy1);
 | 
				
			||||||
 | 
							as = new AdvisedSupport(new Class<?>[]{});
 | 
				
			||||||
 | 
							as.setTargetSource(mockTargetSource);
 | 
				
			||||||
 | 
							as.addAdvisor(new DefaultPointcutAdvisor(new AnnotationMatchingPointcut(Nullable.class), new NopInterceptor()));
 | 
				
			||||||
 | 
							cglib = new CglibAopProxy(as);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ITestBean proxy3 = (ITestBean) cglib.getProxy();
 | 
				
			||||||
 | 
							assertThat(proxy3).isInstanceOf(Serializable.class);
 | 
				
			||||||
 | 
							assertThat(proxy3.getClass()).isNotSameAs(proxy2.getClass());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ITestBean proxy3a = (ITestBean) cglib.getProxy();
 | 
				
			||||||
 | 
							assertThat(proxy3a).isInstanceOf(Serializable.class);
 | 
				
			||||||
 | 
							assertThat(proxy3a.getClass()).isSameAs(proxy3.getClass());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							mockTargetSource.setTarget(proxy1);
 | 
				
			||||||
 | 
							as = new AdvisedSupport(new Class<?>[]{});
 | 
				
			||||||
 | 
							as.setTargetSource(mockTargetSource);
 | 
				
			||||||
 | 
							as.addAdvisor(new DefaultPointcutAdvisor(new AnnotationMatchingPointcut(NonNull.class), new NopInterceptor()));
 | 
				
			||||||
 | 
							cglib = new CglibAopProxy(as);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ITestBean proxy4 = (ITestBean) cglib.getProxy();
 | 
				
			||||||
 | 
							assertThat(proxy4).isInstanceOf(Serializable.class);
 | 
				
			||||||
 | 
							assertThat(proxy4.getClass()).isNotSameAs(proxy3.getClass());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ITestBean proxy4a = (ITestBean) cglib.getProxy();
 | 
				
			||||||
 | 
							assertThat(proxy4a).isInstanceOf(Serializable.class);
 | 
				
			||||||
 | 
							assertThat(proxy4a.getClass()).isSameAs(proxy4.getClass());
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue