Skip to content

Commit 41b834f

Browse files
committed
Consistent ClassLoader propagation and ConcurrentHashMap setup for AspectJ pointcuts
Issue: SPR-15040 (cherry picked from commit d64d9ab)
1 parent a88436c commit 41b834f

File tree

8 files changed

+151
-143
lines changed

8 files changed

+151
-143
lines changed

spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJExpressionPointcut.java

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -186,13 +186,24 @@ private void checkReadyToMatch() {
186186
throw new IllegalStateException("Must set property 'expression' before attempting to match");
187187
}
188188
if (this.pointcutExpression == null) {
189-
this.pointcutClassLoader = (this.beanFactory instanceof ConfigurableBeanFactory ?
190-
((ConfigurableBeanFactory) this.beanFactory).getBeanClassLoader() :
191-
ClassUtils.getDefaultClassLoader());
189+
this.pointcutClassLoader = determinePointcutClassLoader();
192190
this.pointcutExpression = buildPointcutExpression(this.pointcutClassLoader);
193191
}
194192
}
195193

194+
/**
195+
* Determine the ClassLoader to use for pointcut evaluation.
196+
*/
197+
private ClassLoader determinePointcutClassLoader() {
198+
if (this.beanFactory instanceof ConfigurableBeanFactory) {
199+
return ((ConfigurableBeanFactory) this.beanFactory).getBeanClassLoader();
200+
}
201+
if (this.pointcutDeclarationScope != null) {
202+
return this.pointcutDeclarationScope.getClassLoader();
203+
}
204+
return ClassUtils.getDefaultClassLoader();
205+
}
206+
196207
/**
197208
* Build the underlying AspectJ pointcut expression.
198209
*/

spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJExpressionPointcutAdvisor.java

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2014 the original author or authors.
2+
* Copyright 2002-2016 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -18,6 +18,8 @@
1818

1919
import org.springframework.aop.Pointcut;
2020
import org.springframework.aop.support.AbstractGenericPointcutAdvisor;
21+
import org.springframework.beans.factory.BeanFactory;
22+
import org.springframework.beans.factory.BeanFactoryAware;
2123

2224
/**
2325
* Spring AOP Advisor that can be used for any AspectJ pointcut expression.
@@ -26,16 +28,11 @@
2628
* @since 2.0
2729
*/
2830
@SuppressWarnings("serial")
29-
public class AspectJExpressionPointcutAdvisor extends AbstractGenericPointcutAdvisor {
31+
public class AspectJExpressionPointcutAdvisor extends AbstractGenericPointcutAdvisor implements BeanFactoryAware {
3032

3133
private final AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
3234

3335

34-
@Override
35-
public Pointcut getPointcut() {
36-
return this.pointcut;
37-
}
38-
3936
public void setExpression(String expression) {
4037
this.pointcut.setExpression(expression);
4138
}
@@ -52,12 +49,22 @@ public String getLocation() {
5249
return this.pointcut.getLocation();
5350
}
5451

55-
public void setParameterTypes(Class<?>[] types) {
52+
public void setParameterNames(String... names) {
53+
this.pointcut.setParameterNames(names);
54+
}
55+
56+
public void setParameterTypes(Class<?>... types) {
5657
this.pointcut.setParameterTypes(types);
5758
}
5859

59-
public void setParameterNames(String... names) {
60-
this.pointcut.setParameterNames(names);
60+
@Override
61+
public void setBeanFactory(BeanFactory beanFactory) {
62+
this.pointcut.setBeanFactory(beanFactory);
63+
}
64+
65+
@Override
66+
public Pointcut getPointcut() {
67+
return this.pointcut;
6168
}
6269

6370
}

spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/AbstractAspectJAdvisorFactory.java

Lines changed: 1 addition & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2015 the original author or authors.
2+
* Copyright 2002-2016 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -38,7 +38,6 @@
3838
import org.aspectj.lang.reflect.AjTypeSystem;
3939
import org.aspectj.lang.reflect.PerClauseKind;
4040

41-
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
4241
import org.springframework.aop.framework.AopConfigException;
4342
import org.springframework.core.ParameterNameDiscoverer;
4443
import org.springframework.core.annotation.AnnotationUtils;
@@ -121,49 +120,6 @@ public void validate(Class<?> aspectClass) throws AopConfigException {
121120
}
122121
}
123122

124-
/**
125-
* The pointcut and advice annotations both have an "argNames" member which contains a
126-
* comma-separated list of the argument names. We use this (if non-empty) to build the
127-
* formal parameters for the pointcut.
128-
*/
129-
protected AspectJExpressionPointcut createPointcutExpression(
130-
Method annotatedMethod, Class<?> declarationScope, String[] pointcutParameterNames) {
131-
132-
Class<?> [] pointcutParameterTypes = new Class<?>[0];
133-
if (pointcutParameterNames != null) {
134-
pointcutParameterTypes = extractPointcutParameterTypes(pointcutParameterNames,annotatedMethod);
135-
}
136-
137-
AspectJExpressionPointcut ajexp =
138-
new AspectJExpressionPointcut(declarationScope,pointcutParameterNames,pointcutParameterTypes);
139-
ajexp.setLocation(annotatedMethod.toString());
140-
return ajexp;
141-
}
142-
143-
/**
144-
* Create the pointcut parameters needed by aspectj based on the given argument names
145-
* and the argument types that are available from the adviceMethod. Needs to take into
146-
* account (ignore) any JoinPoint based arguments as these are not pointcut context but
147-
* rather part of the advice execution context (thisJoinPoint, thisJoinPointStaticPart)
148-
*/
149-
private Class<?>[] extractPointcutParameterTypes(String[] argNames, Method adviceMethod) {
150-
Class<?>[] ret = new Class<?>[argNames.length];
151-
Class<?>[] paramTypes = adviceMethod.getParameterTypes();
152-
if (argNames.length > paramTypes.length) {
153-
throw new IllegalStateException("Expecting at least " + argNames.length +
154-
" arguments in the advice declaration, but only found " + paramTypes.length);
155-
}
156-
157-
// Make the simplifying assumption for now that all of the JoinPoint based arguments
158-
// come first in the advice declaration.
159-
int typeOffset = paramTypes.length - argNames.length;
160-
for (int i = 0; i < ret.length; i++) {
161-
ret[i] = paramTypes[i + typeOffset];
162-
}
163-
return ret;
164-
}
165-
166-
167123
/**
168124
* Find and return the first AspectJ annotation on the given method
169125
* (there <i>should</i> only be one anyway...)

spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/AnnotationAwareAspectJAutoProxyCreator.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2013 the original author or authors.
2+
* Copyright 2002-2016 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -50,7 +50,7 @@ public class AnnotationAwareAspectJAutoProxyCreator extends AspectJAwareAdvisorA
5050

5151
private List<Pattern> includePatterns;
5252

53-
private AspectJAdvisorFactory aspectJAdvisorFactory = new ReflectiveAspectJAdvisorFactory();
53+
private AspectJAdvisorFactory aspectJAdvisorFactory;
5454

5555
private BeanFactoryAspectJAdvisorsBuilder aspectJAdvisorsBuilder;
5656

@@ -74,6 +74,9 @@ public void setAspectJAdvisorFactory(AspectJAdvisorFactory aspectJAdvisorFactory
7474
@Override
7575
protected void initBeanFactory(ConfigurableListableBeanFactory beanFactory) {
7676
super.initBeanFactory(beanFactory);
77+
if (this.aspectJAdvisorFactory == null) {
78+
this.aspectJAdvisorFactory = new ReflectiveAspectJAdvisorFactory(beanFactory);
79+
}
7780
this.aspectJAdvisorsBuilder =
7881
new BeanFactoryAspectJAdvisorsBuilderAdapter(beanFactory, this.aspectJAdvisorFactory);
7982
}
@@ -130,6 +133,7 @@ private class BeanFactoryAspectJAdvisorsBuilderAdapter extends BeanFactoryAspect
130133

131134
public BeanFactoryAspectJAdvisorsBuilderAdapter(
132135
ListableBeanFactory beanFactory, AspectJAdvisorFactory advisorFactory) {
136+
133137
super(beanFactory, advisorFactory);
134138
}
135139

spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/AspectJProxyFactory.java

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2015 the original author or authors.
2+
* Copyright 2002-2016 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,9 +16,9 @@
1616

1717
package org.springframework.aop.aspectj.annotation;
1818

19-
import java.util.HashMap;
2019
import java.util.List;
2120
import java.util.Map;
21+
import java.util.concurrent.ConcurrentHashMap;
2222

2323
import org.aspectj.lang.reflect.PerClauseKind;
2424

@@ -50,7 +50,7 @@
5050
public class AspectJProxyFactory extends ProxyCreatorSupport {
5151

5252
/** Cache for singleton aspect instances */
53-
private static final Map<Class<?>, Object> aspectCache = new HashMap<Class<?>, Object>();
53+
private static final Map<Class<?>, Object> aspectCache = new ConcurrentHashMap<Class<?>, Object>();
5454

5555
private final AspectJAdvisorFactory aspectFactory = new ReflectiveAspectJAdvisorFactory();
5656

@@ -144,7 +144,7 @@ private AspectMetadata createAspectMetadata(Class<?> aspectClass, String aspectN
144144
private MetadataAwareAspectInstanceFactory createAspectInstanceFactory(
145145
AspectMetadata am, Class<?> aspectClass, String aspectName) {
146146

147-
MetadataAwareAspectInstanceFactory instanceFactory = null;
147+
MetadataAwareAspectInstanceFactory instanceFactory;
148148
if (am.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
149149
// Create a shared aspect instance.
150150
Object instance = getSingletonAspectInstance(aspectClass);
@@ -162,23 +162,29 @@ private MetadataAwareAspectInstanceFactory createAspectInstanceFactory(
162162
* is created if one cannot be found in the instance cache.
163163
*/
164164
private Object getSingletonAspectInstance(Class<?> aspectClass) {
165-
synchronized (aspectCache) {
166-
Object instance = aspectCache.get(aspectClass);
167-
if (instance != null) {
168-
return instance;
169-
}
170-
try {
171-
instance = aspectClass.newInstance();
172-
aspectCache.put(aspectClass, instance);
173-
return instance;
174-
}
175-
catch (InstantiationException ex) {
176-
throw new AopConfigException("Unable to instantiate aspect class [" + aspectClass.getName() + "]", ex);
177-
}
178-
catch (IllegalAccessException ex) {
179-
throw new AopConfigException("Cannot access aspect class [" + aspectClass.getName() + "]", ex);
165+
// Quick check without a lock...
166+
Object instance = aspectCache.get(aspectClass);
167+
if (instance == null) {
168+
synchronized (aspectCache) {
169+
// To be safe, check within full lock now...
170+
instance = aspectCache.get(aspectClass);
171+
if (instance != null) {
172+
return instance;
173+
}
174+
try {
175+
instance = aspectClass.newInstance();
176+
aspectCache.put(aspectClass, instance);
177+
return instance;
178+
}
179+
catch (InstantiationException ex) {
180+
throw new AopConfigException("Unable to instantiate aspect class [" + aspectClass.getName() + "]", ex);
181+
}
182+
catch (IllegalAccessException ex) {
183+
throw new AopConfigException("Cannot access aspect class [" + aspectClass.getName() + "]", ex);
184+
}
180185
}
181186
}
187+
return instance;
182188
}
183189

184190

spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/AspectMetadata.java

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2015 the original author or authors.
2+
* Copyright 2002-2016 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -35,9 +35,8 @@
3535
* Metadata for an AspectJ aspect class, with an additional Spring AOP pointcut
3636
* for the per clause.
3737
*
38-
* <p>Uses AspectJ 5 AJType reflection API, so is only supported on Java 5.
39-
* Enables us to work with different AspectJ instantiation models such as
40-
* "singleton", "pertarget" and "perthis".
38+
* <p>Uses AspectJ 5 AJType reflection API, enabling us to work with different
39+
* AspectJ instantiation models such as "singleton", "pertarget" and "perthis".
4140
*
4241
* @author Rod Johnson
4342
* @author Juergen Hoeller
@@ -102,20 +101,22 @@ public AspectMetadata(Class<?> aspectClass, String aspectName) {
102101
this.ajType = ajType;
103102

104103
switch (this.ajType.getPerClause().getKind()) {
105-
case SINGLETON :
104+
case SINGLETON:
106105
this.perClausePointcut = Pointcut.TRUE;
107106
return;
108-
case PERTARGET : case PERTHIS :
107+
case PERTARGET:
108+
case PERTHIS:
109109
AspectJExpressionPointcut ajexp = new AspectJExpressionPointcut();
110-
ajexp.setLocation("@Aspect annotation on " + aspectClass.getName());
110+
ajexp.setLocation(aspectClass.getName());
111111
ajexp.setExpression(findPerClause(aspectClass));
112+
ajexp.setPointcutDeclarationScope(aspectClass);
112113
this.perClausePointcut = ajexp;
113114
return;
114-
case PERTYPEWITHIN :
115+
case PERTYPEWITHIN:
115116
// Works with a type pattern
116117
this.perClausePointcut = new ComposablePointcut(new TypePatternClassFilter(findPerClause(aspectClass)));
117118
return;
118-
default :
119+
default:
119120
throw new AopConfigException(
120121
"PerClause " + ajType.getPerClause().getKind() + " not supported by Spring AOP for " + aspectClass);
121122
}
@@ -125,8 +126,6 @@ public AspectMetadata(Class<?> aspectClass, String aspectName) {
125126
* Extract contents from String of form {@code pertarget(contents)}.
126127
*/
127128
private String findPerClause(Class<?> aspectClass) {
128-
// TODO when AspectJ provides this, we can remove this hack. Hence we don't
129-
// bother to make it elegant. Or efficient. Or robust :-)
130129
String str = aspectClass.getAnnotation(Aspect.class).value();
131130
str = str.substring(str.indexOf("(") + 1);
132131
str = str.substring(0, str.length() - 1);

0 commit comments

Comments
 (0)