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