1 /*
2  * Copyright 2002-2010 the original author or authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package org.springframework.aop.framework;
18 
19 import java.io.IOException;
20 import java.io.ObjectInputStream;
21 import java.lang.reflect.Method;
22 import java.util.ArrayList;
23 import java.util.Arrays;
24 import java.util.Collection;
25 import java.util.LinkedList;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.concurrent.ConcurrentHashMap;
29 
30 import org.aopalliance.aop.Advice;
31 
32 import org.springframework.aop.Advisor;
33 import org.springframework.aop.DynamicIntroductionAdvice;
34 import org.springframework.aop.IntroductionAdvisor;
35 import org.springframework.aop.IntroductionInfo;
36 import org.springframework.aop.TargetSource;
37 import org.springframework.aop.support.DefaultIntroductionAdvisor;
38 import org.springframework.aop.support.DefaultPointcutAdvisor;
39 import org.springframework.aop.target.EmptyTargetSource;
40 import org.springframework.aop.target.SingletonTargetSource;
41 import org.springframework.util.Assert;
42 import org.springframework.util.ClassUtils;
43 import org.springframework.util.CollectionUtils;
44 
45 /**
46  * Base class for AOP proxy configuration managers.
47  * These are not themselves AOP proxies, but subclasses of this class are
48  * normally factories from which AOP proxy instances are obtained directly.
49  *
50  * <p>This class frees subclasses of the housekeeping of Advices
51  * and Advisors, but doesn't actually implement proxy creation
52  * methods, which are provided by subclasses.
53  *
54  * <p>This class is serializable; subclasses need not be.
55  * This class is used to hold snapshots of proxies.
56  *
57  * @author Rod Johnson
58  * @author Juergen Hoeller
59  * @see org.springframework.aop.framework.AopProxy
60  */
61 @SuppressWarnings("unchecked")
62 public class AdvisedSupport extends ProxyConfig implements Advised {
63 
64 	/** use serialVersionUID from Spring 2.0 for interoperability */
65 	private static final long serialVersionUID = 2651364800145442165L;
66 
67 
68 	/**
69 	 * Canonical TargetSource when there's no target, and behavior is
70 	 * supplied by the advisors.
71 	 */
72 	public static final TargetSource EMPTY_TARGET_SOURCE = EmptyTargetSource.INSTANCE;
73 
74 
75 	/** Package-protected to allow direct access for efficiency */
76 	TargetSource targetSource = EMPTY_TARGET_SOURCE;
77 
78 	/** Whether the Advisors are already filtered for the specific target class */
79 	private boolean preFiltered = false;
80 
81 	/** The AdvisorChainFactory to use */
82 	AdvisorChainFactory advisorChainFactory = new DefaultAdvisorChainFactory();
83 
84 	/** Cache with Method as key and advisor chain List as value */
85 	private transient Map<MethodCacheKey, List<Object>> methodCache;
86 
87 	/**
88 	 * Interfaces to be implemented by the proxy. Held in List to keep the order
89 	 * of registration, to create JDK proxy with specified order of interfaces.
90 	 */
91 	private List<Class> interfaces = new ArrayList<Class>();
92 
93 	/**
94 	 * List of Advisors. If an Advice is added, it will be wrapped
95 	 * in an Advisor before being added to this List.
96 	 */
97 	private List<Advisor> advisors = new LinkedList<Advisor>();
98 
99 	/**
100 	 * Array updated on changes to the advisors list, which is easier
101 	 * to manipulate internally.
102 	 */
103 	private Advisor[] advisorArray = new Advisor[0];
104 
105 
106 	/**
107 	 * No-arg constructor for use as a JavaBean.
108 	 */
AdvisedSupport()109 	public AdvisedSupport() {
110 		initMethodCache();
111 	}
112 
113 	/**
114 	 * Create a AdvisedSupport instance with the given parameters.
115 	 * @param interfaces the proxied interfaces
116 	 */
AdvisedSupport(Class[] interfaces)117 	public AdvisedSupport(Class[] interfaces) {
118 		this();
119 		setInterfaces(interfaces);
120 	}
121 
122 	/**
123 	 * Initialize the method cache.
124 	 */
initMethodCache()125 	private void initMethodCache() {
126 		this.methodCache = new ConcurrentHashMap<MethodCacheKey, List<Object>>(32);
127 	}
128 
129 
130 	/**
131 	 * Set the given object as target.
132 	 * Will create a SingletonTargetSource for the object.
133 	 * @see #setTargetSource
134 	 * @see org.springframework.aop.target.SingletonTargetSource
135 	 */
setTarget(Object target)136 	public void setTarget(Object target) {
137 		setTargetSource(new SingletonTargetSource(target));
138 	}
139 
setTargetSource(TargetSource targetSource)140 	public void setTargetSource(TargetSource targetSource) {
141 		this.targetSource = (targetSource != null ? targetSource : EMPTY_TARGET_SOURCE);
142 	}
143 
getTargetSource()144 	public TargetSource getTargetSource() {
145 		return this.targetSource;
146 	}
147 
148 	/**
149 	 * Set a target class to be proxied, indicating that the proxy
150 	 * should be castable to the given class.
151 	 * <p>Internally, an {@link org.springframework.aop.target.EmptyTargetSource}
152 	 * for the given target class will be used. The kind of proxy needed
153 	 * will be determined on actual creation of the proxy.
154 	 * <p>This is a replacement for setting a "targetSource" or "target",
155 	 * for the case where we want a proxy based on a target class
156 	 * (which can be an interface or a concrete class) without having
157 	 * a fully capable TargetSource available.
158 	 * @see #setTargetSource
159 	 * @see #setTarget
160 	 */
setTargetClass(Class targetClass)161 	public void setTargetClass(Class targetClass) {
162 		this.targetSource = EmptyTargetSource.forClass(targetClass);
163 	}
164 
getTargetClass()165 	public Class<?> getTargetClass() {
166 		return this.targetSource.getTargetClass();
167 	}
168 
setPreFiltered(boolean preFiltered)169 	public void setPreFiltered(boolean preFiltered) {
170 		this.preFiltered = preFiltered;
171 	}
172 
isPreFiltered()173 	public boolean isPreFiltered() {
174 		return this.preFiltered;
175 	}
176 
177 	/**
178 	 * Set the advisor chain factory to use.
179 	 * <p>Default is a {@link DefaultAdvisorChainFactory}.
180 	 */
setAdvisorChainFactory(AdvisorChainFactory advisorChainFactory)181 	public void setAdvisorChainFactory(AdvisorChainFactory advisorChainFactory) {
182 		Assert.notNull(advisorChainFactory, "AdvisorChainFactory must not be null");
183 		this.advisorChainFactory = advisorChainFactory;
184 	}
185 
186 	/**
187 	 * Return the advisor chain factory to use (never <code>null</code>).
188 	 */
getAdvisorChainFactory()189 	public AdvisorChainFactory getAdvisorChainFactory() {
190 		return this.advisorChainFactory;
191 	}
192 
193 
194 	/**
195 	 * Set the interfaces to be proxied.
196 	 */
setInterfaces(Class[] interfaces)197 	public void setInterfaces(Class[] interfaces) {
198 		Assert.notNull(interfaces, "Interfaces must not be null");
199 		this.interfaces.clear();
200 		for (Class ifc : interfaces) {
201 			addInterface(ifc);
202 		}
203 	}
204 
205 	/**
206 	 * Add a new proxied interface.
207 	 * @param intf the additional interface to proxy
208 	 */
addInterface(Class intf)209 	public void addInterface(Class intf) {
210 		Assert.notNull(intf, "Interface must not be null");
211 		if (!intf.isInterface()) {
212 			throw new IllegalArgumentException("[" + intf.getName() + "] is not an interface");
213 		}
214 		if (!this.interfaces.contains(intf)) {
215 			this.interfaces.add(intf);
216 			adviceChanged();
217 		}
218 	}
219 
220 	/**
221 	 * Remove a proxied interface.
222 	 * <p>Does nothing if the given interface isn't proxied.
223 	 * @param intf the interface to remove from the proxy
224 	 * @return <code>true</code> if the interface was removed; <code>false</code>
225 	 * if the interface was not found and hence could not be removed
226 	 */
removeInterface(Class intf)227 	public boolean removeInterface(Class intf) {
228 		return this.interfaces.remove(intf);
229 	}
230 
getProxiedInterfaces()231 	public Class[] getProxiedInterfaces() {
232 		return this.interfaces.toArray(new Class[this.interfaces.size()]);
233 	}
234 
isInterfaceProxied(Class intf)235 	public boolean isInterfaceProxied(Class intf) {
236 		for (Class proxyIntf : this.interfaces) {
237 			if (intf.isAssignableFrom(proxyIntf)) {
238 				return true;
239 			}
240 		}
241 		return false;
242 	}
243 
244 
getAdvisors()245 	public final Advisor[] getAdvisors() {
246 		return this.advisorArray;
247 	}
248 
addAdvisor(Advisor advisor)249 	public void addAdvisor(Advisor advisor) {
250 		int pos = this.advisors.size();
251 		addAdvisor(pos, advisor);
252 	}
253 
addAdvisor(int pos, Advisor advisor)254 	public void addAdvisor(int pos, Advisor advisor) throws AopConfigException {
255 		if (advisor instanceof IntroductionAdvisor) {
256 			validateIntroductionAdvisor((IntroductionAdvisor) advisor);
257 		}
258 		addAdvisorInternal(pos, advisor);
259 	}
260 
removeAdvisor(Advisor advisor)261 	public boolean removeAdvisor(Advisor advisor) {
262 		int index = indexOf(advisor);
263 		if (index == -1) {
264 			return false;
265 		}
266 		else {
267 			removeAdvisor(index);
268 			return true;
269 		}
270 	}
271 
removeAdvisor(int index)272 	public void removeAdvisor(int index) throws AopConfigException {
273 		if (isFrozen()) {
274 			throw new AopConfigException("Cannot remove Advisor: Configuration is frozen.");
275 		}
276 		if (index < 0 || index > this.advisors.size() - 1) {
277 			throw new AopConfigException("Advisor index " + index + " is out of bounds: " +
278 					"This configuration only has " + this.advisors.size() + " advisors.");
279 		}
280 
281 		Advisor advisor = this.advisors.get(index);
282 		if (advisor instanceof IntroductionAdvisor) {
283 			IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
284 			// We need to remove introduction interfaces.
285 			for (int j = 0; j < ia.getInterfaces().length; j++) {
286 				removeInterface(ia.getInterfaces()[j]);
287 			}
288 		}
289 
290 		this.advisors.remove(index);
291 		updateAdvisorArray();
292 		adviceChanged();
293 	}
294 
indexOf(Advisor advisor)295 	public int indexOf(Advisor advisor) {
296 		Assert.notNull(advisor, "Advisor must not be null");
297 		return this.advisors.indexOf(advisor);
298 	}
299 
replaceAdvisor(Advisor a, Advisor b)300 	public boolean replaceAdvisor(Advisor a, Advisor b) throws AopConfigException {
301 		Assert.notNull(a, "Advisor a must not be null");
302 		Assert.notNull(b, "Advisor b must not be null");
303 		int index = indexOf(a);
304 		if (index == -1) {
305 			return false;
306 		}
307 		removeAdvisor(index);
308 		addAdvisor(index, b);
309 		return true;
310 	}
311 
312 	/**
313 	 * Add all of the given advisors to this proxy configuration.
314 	 * @param advisors the advisors to register
315 	 * @deprecated as of Spring 3.0, in favor of {@link #addAdvisors}
316 	 */
317 	@Deprecated
addAllAdvisors(Advisor[] advisors)318 	public void addAllAdvisors(Advisor[] advisors) {
319 		addAdvisors(Arrays.asList(advisors));
320 	}
321 
322 	/**
323 	 * Add all of the given advisors to this proxy configuration.
324 	 * @param advisors the advisors to register
325 	 */
addAdvisors(Advisor... advisors)326 	public void addAdvisors(Advisor... advisors) {
327 		addAdvisors(Arrays.asList(advisors));
328 	}
329 
330 	/**
331 	 * Add all of the given advisors to this proxy configuration.
332 	 * @param advisors the advisors to register
333 	 */
addAdvisors(Collection<Advisor> advisors)334 	public void addAdvisors(Collection<Advisor> advisors) {
335 		if (isFrozen()) {
336 			throw new AopConfigException("Cannot add advisor: Configuration is frozen.");
337 		}
338 		if (!CollectionUtils.isEmpty(advisors)) {
339 			for (Advisor advisor : advisors) {
340 				if (advisor instanceof IntroductionAdvisor) {
341 					validateIntroductionAdvisor((IntroductionAdvisor) advisor);
342 				}
343 				Assert.notNull(advisor, "Advisor must not be null");
344 				this.advisors.add(advisor);
345 			}
346 			updateAdvisorArray();
347 			adviceChanged();
348 		}
349 	}
350 
validateIntroductionAdvisor(IntroductionAdvisor advisor)351 	private void validateIntroductionAdvisor(IntroductionAdvisor advisor) {
352 		advisor.validateInterfaces();
353 		// If the advisor passed validation, we can make the change.
354 		Class[] ifcs = advisor.getInterfaces();
355 		for (Class ifc : ifcs) {
356 			addInterface(ifc);
357 		}
358 	}
359 
addAdvisorInternal(int pos, Advisor advisor)360 	private void addAdvisorInternal(int pos, Advisor advisor) throws AopConfigException {
361 		Assert.notNull(advisor, "Advisor must not be null");
362 		if (isFrozen()) {
363 			throw new AopConfigException("Cannot add advisor: Configuration is frozen.");
364 		}
365 		if (pos > this.advisors.size()) {
366 			throw new IllegalArgumentException(
367 					"Illegal position " + pos + " in advisor list with size " + this.advisors.size());
368 		}
369 		this.advisors.add(pos, advisor);
370 		updateAdvisorArray();
371 		adviceChanged();
372 	}
373 
374 	/**
375 	 * Bring the array up to date with the list.
376 	 */
updateAdvisorArray()377 	protected final void updateAdvisorArray() {
378 		this.advisorArray = this.advisors.toArray(new Advisor[this.advisors.size()]);
379 	}
380 
381 	/**
382 	 * Allows uncontrolled access to the {@link List} of {@link Advisor Advisors}.
383 	 * <p>Use with care, and remember to {@link #updateAdvisorArray() refresh the advisor array}
384 	 * and {@link #adviceChanged() fire advice changed events} when making any modifications.
385 	 */
getAdvisorsInternal()386 	protected final List<Advisor> getAdvisorsInternal() {
387 		return this.advisors;
388 	}
389 
390 
addAdvice(Advice advice)391 	public void addAdvice(Advice advice) throws AopConfigException {
392 		int pos = this.advisors.size();
393 		addAdvice(pos, advice);
394 	}
395 
396 	/**
397 	 * Cannot add introductions this way unless the advice implements IntroductionInfo.
398 	 */
addAdvice(int pos, Advice advice)399 	public void addAdvice(int pos, Advice advice) throws AopConfigException {
400 		Assert.notNull(advice, "Advice must not be null");
401 		if (advice instanceof IntroductionInfo) {
402 			// We don't need an IntroductionAdvisor for this kind of introduction:
403 			// It's fully self-describing.
404 			addAdvisor(pos, new DefaultIntroductionAdvisor(advice, (IntroductionInfo) advice));
405 		}
406 		else if (advice instanceof DynamicIntroductionAdvice) {
407 			// We need an IntroductionAdvisor for this kind of introduction.
408 			throw new AopConfigException("DynamicIntroductionAdvice may only be added as part of IntroductionAdvisor");
409 		}
410 		else {
411 			addAdvisor(pos, new DefaultPointcutAdvisor(advice));
412 		}
413 	}
414 
removeAdvice(Advice advice)415 	public boolean removeAdvice(Advice advice) throws AopConfigException {
416 		int index = indexOf(advice);
417 		if (index == -1) {
418 			return false;
419 		}
420 		else {
421 			removeAdvisor(index);
422 			return true;
423 		}
424 	}
425 
indexOf(Advice advice)426 	public int indexOf(Advice advice) {
427 		Assert.notNull(advice, "Advice must not be null");
428 		for (int i = 0; i < this.advisors.size(); i++) {
429 			Advisor advisor = this.advisors.get(i);
430 			if (advisor.getAdvice() == advice) {
431 				return i;
432 			}
433 		}
434 		return -1;
435 	}
436 
437 	/**
438 	 * Is the given advice included in any advisor within this proxy configuration?
439 	 * @param advice the advice to check inclusion of
440 	 * @return whether this advice instance is included
441 	 */
adviceIncluded(Advice advice)442 	public boolean adviceIncluded(Advice advice) {
443 		if (advice != null) {
444 			for (Advisor advisor : this.advisors) {
445 				if (advisor.getAdvice() == advice) {
446 					return true;
447 				}
448 			}
449 		}
450 		return false;
451 	}
452 
453 	/**
454 	 * Count advices of the given class.
455 	 * @param adviceClass the advice class to check
456 	 * @return the count of the interceptors of this class or subclasses
457 	 */
countAdvicesOfType(Class adviceClass)458 	public int countAdvicesOfType(Class adviceClass) {
459 		int count = 0;
460 		if (adviceClass != null) {
461 			for (Advisor advisor : this.advisors) {
462 				if (adviceClass.isInstance(advisor.getAdvice())) {
463 					count++;
464 				}
465 			}
466 		}
467 		return count;
468 	}
469 
470 
471 	/**
472 	 * Determine a list of {@link org.aopalliance.intercept.MethodInterceptor} objects
473 	 * for the given method, based on this configuration.
474 	 * @param method the proxied method
475 	 * @param targetClass the target class
476 	 * @return List of MethodInterceptors (may also include InterceptorAndDynamicMethodMatchers)
477 	 */
getInterceptorsAndDynamicInterceptionAdvice(Method method, Class targetClass)478 	public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, Class targetClass) {
479 		MethodCacheKey cacheKey = new MethodCacheKey(method);
480 		List<Object> cached = this.methodCache.get(cacheKey);
481 		if (cached == null) {
482 			cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
483 					this, method, targetClass);
484 			this.methodCache.put(cacheKey, cached);
485 		}
486 		return cached;
487 	}
488 
489 	/**
490 	 * Invoked when advice has changed.
491 	 */
adviceChanged()492 	protected void adviceChanged() {
493 		this.methodCache.clear();
494 	}
495 
496 	/**
497 	 * Call this method on a new instance created by the no-arg constructor
498 	 * to create an independent copy of the configuration from the given object.
499 	 * @param other the AdvisedSupport object to copy configuration from
500 	 */
copyConfigurationFrom(AdvisedSupport other)501 	protected void copyConfigurationFrom(AdvisedSupport other) {
502 		copyConfigurationFrom(other, other.targetSource, new ArrayList<Advisor>(other.advisors));
503 	}
504 
505 	/**
506 	 * Copy the AOP configuration from the given AdvisedSupport object,
507 	 * but allow substitution of a fresh TargetSource and a given interceptor chain.
508 	 * @param other the AdvisedSupport object to take proxy configuration from
509 	 * @param targetSource the new TargetSource
510 	 * @param advisors the Advisors for the chain
511 	 */
copyConfigurationFrom(AdvisedSupport other, TargetSource targetSource, List<Advisor> advisors)512 	protected void copyConfigurationFrom(AdvisedSupport other, TargetSource targetSource, List<Advisor> advisors) {
513 		copyFrom(other);
514 		this.targetSource = targetSource;
515 		this.advisorChainFactory = other.advisorChainFactory;
516 		this.interfaces = new ArrayList<Class>(other.interfaces);
517 		for (Advisor advisor : advisors) {
518 			if (advisor instanceof IntroductionAdvisor) {
519 				validateIntroductionAdvisor((IntroductionAdvisor) advisor);
520 			}
521 			Assert.notNull(advisor, "Advisor must not be null");
522 			this.advisors.add(advisor);
523 		}
524 		updateAdvisorArray();
525 		adviceChanged();
526 	}
527 
528 	/**
529 	 * Build a configuration-only copy of this AdvisedSupport,
530 	 * replacing the TargetSource
531 	 */
getConfigurationOnlyCopy()532 	AdvisedSupport getConfigurationOnlyCopy() {
533 		AdvisedSupport copy = new AdvisedSupport();
534 		copy.copyFrom(this);
535 		copy.targetSource = EmptyTargetSource.forClass(getTargetClass(), getTargetSource().isStatic());
536 		copy.advisorChainFactory = this.advisorChainFactory;
537 		copy.interfaces = this.interfaces;
538 		copy.advisors = this.advisors;
539 		copy.updateAdvisorArray();
540 		return copy;
541 	}
542 
543 
544 	//---------------------------------------------------------------------
545 	// Serialization support
546 	//---------------------------------------------------------------------
547 
readObject(ObjectInputStream ois)548 	private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
549 		// Rely on default serialization; just initialize state after deserialization.
550 		ois.defaultReadObject();
551 
552 		// Initialize transient fields.
553 		initMethodCache();
554 	}
555 
556 
toProxyConfigString()557 	public String toProxyConfigString() {
558 		return toString();
559 	}
560 
561 	/**
562 	 * For debugging/diagnostic use.
563 	 */
564 	@Override
toString()565 	public String toString() {
566 		StringBuilder sb = new StringBuilder(getClass().getName());
567 		sb.append(": ").append(this.interfaces.size()).append(" interfaces ");
568 		sb.append(ClassUtils.classNamesToString(this.interfaces)).append("; ");
569 		sb.append(this.advisors.size()).append(" advisors ");
570 		sb.append(this.advisors).append("; ");
571 		sb.append("targetSource [").append(this.targetSource).append("]; ");
572 		sb.append(super.toString());
573 		return sb.toString();
574 	}
575 
576 
577 	/**
578 	 * Simple wrapper class around a Method. Used as the key when
579 	 * caching methods, for efficient equals and hashCode comparisons.
580 	 */
581 	private static class MethodCacheKey {
582 
583 		private final Method method;
584 
585 		private final int hashCode;
586 
MethodCacheKey(Method method)587 		public MethodCacheKey(Method method) {
588 			this.method = method;
589 			this.hashCode = method.hashCode();
590 		}
591 
592 		@Override
equals(Object other)593 		public boolean equals(Object other) {
594 			if (other == this) {
595 				return true;
596 			}
597 			MethodCacheKey otherKey = (MethodCacheKey) other;
598 			return (this.method == otherKey.method);
599 		}
600 
601 		@Override
hashCode()602 		public int hashCode() {
603 			return this.hashCode;
604 		}
605 	}
606 
607 }
608