001/* 002 * Copyright 2002-2019 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.aop.framework; 018 019import java.io.IOException; 020import java.io.ObjectInputStream; 021import java.lang.reflect.Method; 022import java.util.ArrayList; 023import java.util.Arrays; 024import java.util.Collection; 025import java.util.List; 026import java.util.Map; 027import java.util.concurrent.ConcurrentHashMap; 028 029import org.aopalliance.aop.Advice; 030 031import org.springframework.aop.Advisor; 032import org.springframework.aop.DynamicIntroductionAdvice; 033import org.springframework.aop.IntroductionAdvisor; 034import org.springframework.aop.IntroductionInfo; 035import org.springframework.aop.TargetSource; 036import org.springframework.aop.support.DefaultIntroductionAdvisor; 037import org.springframework.aop.support.DefaultPointcutAdvisor; 038import org.springframework.aop.target.EmptyTargetSource; 039import org.springframework.aop.target.SingletonTargetSource; 040import org.springframework.lang.Nullable; 041import org.springframework.util.Assert; 042import org.springframework.util.ClassUtils; 043import org.springframework.util.CollectionUtils; 044 045/** 046 * Base class for AOP proxy configuration managers. 047 * These are not themselves AOP proxies, but subclasses of this class are 048 * normally factories from which AOP proxy instances are obtained directly. 049 * 050 * <p>This class frees subclasses of the housekeeping of Advices 051 * and Advisors, but doesn't actually implement proxy creation 052 * methods, which are provided by subclasses. 053 * 054 * <p>This class is serializable; subclasses need not be. 055 * This class is used to hold snapshots of proxies. 056 * 057 * @author Rod Johnson 058 * @author Juergen Hoeller 059 * @see org.springframework.aop.framework.AopProxy 060 */ 061public class AdvisedSupport extends ProxyConfig implements Advised { 062 063 /** use serialVersionUID from Spring 2.0 for interoperability. */ 064 private static final long serialVersionUID = 2651364800145442165L; 065 066 067 /** 068 * Canonical TargetSource when there's no target, and behavior is 069 * supplied by the advisors. 070 */ 071 public static final TargetSource EMPTY_TARGET_SOURCE = EmptyTargetSource.INSTANCE; 072 073 074 /** Package-protected to allow direct access for efficiency. */ 075 TargetSource targetSource = EMPTY_TARGET_SOURCE; 076 077 /** Whether the Advisors are already filtered for the specific target class. */ 078 private boolean preFiltered = false; 079 080 /** The AdvisorChainFactory to use. */ 081 AdvisorChainFactory advisorChainFactory = new DefaultAdvisorChainFactory(); 082 083 /** Cache with Method as key and advisor chain List as value. */ 084 private transient Map<MethodCacheKey, List<Object>> methodCache; 085 086 /** 087 * Interfaces to be implemented by the proxy. Held in List to keep the order 088 * of registration, to create JDK proxy with specified order of interfaces. 089 */ 090 private List<Class<?>> interfaces = new ArrayList<>(); 091 092 /** 093 * List of Advisors. If an Advice is added, it will be wrapped 094 * in an Advisor before being added to this List. 095 */ 096 private List<Advisor> advisors = new ArrayList<>(); 097 098 /** 099 * Array updated on changes to the advisors list, which is easier 100 * to manipulate internally. 101 */ 102 private Advisor[] advisorArray = new Advisor[0]; 103 104 105 /** 106 * No-arg constructor for use as a JavaBean. 107 */ 108 public AdvisedSupport() { 109 this.methodCache = new ConcurrentHashMap<>(32); 110 } 111 112 /** 113 * Create a AdvisedSupport instance with the given parameters. 114 * @param interfaces the proxied interfaces 115 */ 116 public AdvisedSupport(Class<?>... interfaces) { 117 this(); 118 setInterfaces(interfaces); 119 } 120 121 122 /** 123 * Set the given object as target. 124 * Will create a SingletonTargetSource for the object. 125 * @see #setTargetSource 126 * @see org.springframework.aop.target.SingletonTargetSource 127 */ 128 public void setTarget(Object target) { 129 setTargetSource(new SingletonTargetSource(target)); 130 } 131 132 @Override 133 public void setTargetSource(@Nullable TargetSource targetSource) { 134 this.targetSource = (targetSource != null ? targetSource : EMPTY_TARGET_SOURCE); 135 } 136 137 @Override 138 public TargetSource getTargetSource() { 139 return this.targetSource; 140 } 141 142 /** 143 * Set a target class to be proxied, indicating that the proxy 144 * should be castable to the given class. 145 * <p>Internally, an {@link org.springframework.aop.target.EmptyTargetSource} 146 * for the given target class will be used. The kind of proxy needed 147 * will be determined on actual creation of the proxy. 148 * <p>This is a replacement for setting a "targetSource" or "target", 149 * for the case where we want a proxy based on a target class 150 * (which can be an interface or a concrete class) without having 151 * a fully capable TargetSource available. 152 * @see #setTargetSource 153 * @see #setTarget 154 */ 155 public void setTargetClass(@Nullable Class<?> targetClass) { 156 this.targetSource = EmptyTargetSource.forClass(targetClass); 157 } 158 159 @Override 160 @Nullable 161 public Class<?> getTargetClass() { 162 return this.targetSource.getTargetClass(); 163 } 164 165 @Override 166 public void setPreFiltered(boolean preFiltered) { 167 this.preFiltered = preFiltered; 168 } 169 170 @Override 171 public boolean isPreFiltered() { 172 return this.preFiltered; 173 } 174 175 /** 176 * Set the advisor chain factory to use. 177 * <p>Default is a {@link DefaultAdvisorChainFactory}. 178 */ 179 public void setAdvisorChainFactory(AdvisorChainFactory advisorChainFactory) { 180 Assert.notNull(advisorChainFactory, "AdvisorChainFactory must not be null"); 181 this.advisorChainFactory = advisorChainFactory; 182 } 183 184 /** 185 * Return the advisor chain factory to use (never {@code null}). 186 */ 187 public AdvisorChainFactory getAdvisorChainFactory() { 188 return this.advisorChainFactory; 189 } 190 191 192 /** 193 * Set the interfaces to be proxied. 194 */ 195 public void setInterfaces(Class<?>... interfaces) { 196 Assert.notNull(interfaces, "Interfaces must not be null"); 197 this.interfaces.clear(); 198 for (Class<?> ifc : interfaces) { 199 addInterface(ifc); 200 } 201 } 202 203 /** 204 * Add a new proxied interface. 205 * @param intf the additional interface to proxy 206 */ 207 public void addInterface(Class<?> intf) { 208 Assert.notNull(intf, "Interface must not be null"); 209 if (!intf.isInterface()) { 210 throw new IllegalArgumentException("[" + intf.getName() + "] is not an interface"); 211 } 212 if (!this.interfaces.contains(intf)) { 213 this.interfaces.add(intf); 214 adviceChanged(); 215 } 216 } 217 218 /** 219 * Remove a proxied interface. 220 * <p>Does nothing if the given interface isn't proxied. 221 * @param intf the interface to remove from the proxy 222 * @return {@code true} if the interface was removed; {@code false} 223 * if the interface was not found and hence could not be removed 224 */ 225 public boolean removeInterface(Class<?> intf) { 226 return this.interfaces.remove(intf); 227 } 228 229 @Override 230 public Class<?>[] getProxiedInterfaces() { 231 return ClassUtils.toClassArray(this.interfaces); 232 } 233 234 @Override 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 245 @Override 246 public final Advisor[] getAdvisors() { 247 return this.advisorArray; 248 } 249 250 @Override 251 public void addAdvisor(Advisor advisor) { 252 int pos = this.advisors.size(); 253 addAdvisor(pos, advisor); 254 } 255 256 @Override 257 public void addAdvisor(int pos, Advisor advisor) throws AopConfigException { 258 if (advisor instanceof IntroductionAdvisor) { 259 validateIntroductionAdvisor((IntroductionAdvisor) advisor); 260 } 261 addAdvisorInternal(pos, advisor); 262 } 263 264 @Override 265 public boolean removeAdvisor(Advisor advisor) { 266 int index = indexOf(advisor); 267 if (index == -1) { 268 return false; 269 } 270 else { 271 removeAdvisor(index); 272 return true; 273 } 274 } 275 276 @Override 277 public void removeAdvisor(int index) throws AopConfigException { 278 if (isFrozen()) { 279 throw new AopConfigException("Cannot remove Advisor: Configuration is frozen."); 280 } 281 if (index < 0 || index > this.advisors.size() - 1) { 282 throw new AopConfigException("Advisor index " + index + " is out of bounds: " + 283 "This configuration only has " + this.advisors.size() + " advisors."); 284 } 285 286 Advisor advisor = this.advisors.remove(index); 287 if (advisor instanceof IntroductionAdvisor) { 288 IntroductionAdvisor ia = (IntroductionAdvisor) advisor; 289 // We need to remove introduction interfaces. 290 for (Class<?> ifc : ia.getInterfaces()) { 291 removeInterface(ifc); 292 } 293 } 294 295 updateAdvisorArray(); 296 adviceChanged(); 297 } 298 299 @Override 300 public int indexOf(Advisor advisor) { 301 Assert.notNull(advisor, "Advisor must not be null"); 302 return this.advisors.indexOf(advisor); 303 } 304 305 @Override 306 public boolean replaceAdvisor(Advisor a, Advisor b) throws AopConfigException { 307 Assert.notNull(a, "Advisor a must not be null"); 308 Assert.notNull(b, "Advisor b must not be null"); 309 int index = indexOf(a); 310 if (index == -1) { 311 return false; 312 } 313 removeAdvisor(index); 314 addAdvisor(index, b); 315 return true; 316 } 317 318 /** 319 * Add all of the given advisors to this proxy configuration. 320 * @param advisors the advisors to register 321 */ 322 public void addAdvisors(Advisor... advisors) { 323 addAdvisors(Arrays.asList(advisors)); 324 } 325 326 /** 327 * Add all of the given advisors to this proxy configuration. 328 * @param advisors the advisors to register 329 */ 330 public void addAdvisors(Collection<Advisor> advisors) { 331 if (isFrozen()) { 332 throw new AopConfigException("Cannot add advisor: Configuration is frozen."); 333 } 334 if (!CollectionUtils.isEmpty(advisors)) { 335 for (Advisor advisor : advisors) { 336 if (advisor instanceof IntroductionAdvisor) { 337 validateIntroductionAdvisor((IntroductionAdvisor) advisor); 338 } 339 Assert.notNull(advisor, "Advisor must not be null"); 340 this.advisors.add(advisor); 341 } 342 updateAdvisorArray(); 343 adviceChanged(); 344 } 345 } 346 347 private void validateIntroductionAdvisor(IntroductionAdvisor advisor) { 348 advisor.validateInterfaces(); 349 // If the advisor passed validation, we can make the change. 350 Class<?>[] ifcs = advisor.getInterfaces(); 351 for (Class<?> ifc : ifcs) { 352 addInterface(ifc); 353 } 354 } 355 356 private void addAdvisorInternal(int pos, Advisor advisor) throws AopConfigException { 357 Assert.notNull(advisor, "Advisor must not be null"); 358 if (isFrozen()) { 359 throw new AopConfigException("Cannot add advisor: Configuration is frozen."); 360 } 361 if (pos > this.advisors.size()) { 362 throw new IllegalArgumentException( 363 "Illegal position " + pos + " in advisor list with size " + this.advisors.size()); 364 } 365 this.advisors.add(pos, advisor); 366 updateAdvisorArray(); 367 adviceChanged(); 368 } 369 370 /** 371 * Bring the array up to date with the list. 372 */ 373 protected final void updateAdvisorArray() { 374 this.advisorArray = this.advisors.toArray(new Advisor[0]); 375 } 376 377 /** 378 * Allows uncontrolled access to the {@link List} of {@link Advisor Advisors}. 379 * <p>Use with care, and remember to {@link #updateAdvisorArray() refresh the advisor array} 380 * and {@link #adviceChanged() fire advice changed events} when making any modifications. 381 */ 382 protected final List<Advisor> getAdvisorsInternal() { 383 return this.advisors; 384 } 385 386 387 @Override 388 public void addAdvice(Advice advice) throws AopConfigException { 389 int pos = this.advisors.size(); 390 addAdvice(pos, advice); 391 } 392 393 /** 394 * Cannot add introductions this way unless the advice implements IntroductionInfo. 395 */ 396 @Override 397 public void addAdvice(int pos, Advice advice) throws AopConfigException { 398 Assert.notNull(advice, "Advice must not be null"); 399 if (advice instanceof IntroductionInfo) { 400 // We don't need an IntroductionAdvisor for this kind of introduction: 401 // It's fully self-describing. 402 addAdvisor(pos, new DefaultIntroductionAdvisor(advice, (IntroductionInfo) advice)); 403 } 404 else if (advice instanceof DynamicIntroductionAdvice) { 405 // We need an IntroductionAdvisor for this kind of introduction. 406 throw new AopConfigException("DynamicIntroductionAdvice may only be added as part of IntroductionAdvisor"); 407 } 408 else { 409 addAdvisor(pos, new DefaultPointcutAdvisor(advice)); 410 } 411 } 412 413 @Override 414 public boolean removeAdvice(Advice advice) throws AopConfigException { 415 int index = indexOf(advice); 416 if (index == -1) { 417 return false; 418 } 419 else { 420 removeAdvisor(index); 421 return true; 422 } 423 } 424 425 @Override 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 */ 442 public boolean adviceIncluded(@Nullable 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 */ 458 public int countAdvicesOfType(@Nullable 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 a List of MethodInterceptors (may also include InterceptorAndDynamicMethodMatchers) 477 */ 478 public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, @Nullable 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 */ 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 */ 501 protected void copyConfigurationFrom(AdvisedSupport other) { 502 copyConfigurationFrom(other, other.targetSource, new ArrayList<>(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 */ 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<>(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 */ 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 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 this.methodCache = new ConcurrentHashMap<>(32); 554 } 555 556 557 @Override 558 public String toProxyConfigString() { 559 return toString(); 560 } 561 562 /** 563 * For debugging/diagnostic use. 564 */ 565 @Override 566 public String toString() { 567 StringBuilder sb = new StringBuilder(getClass().getName()); 568 sb.append(": ").append(this.interfaces.size()).append(" interfaces "); 569 sb.append(ClassUtils.classNamesToString(this.interfaces)).append("; "); 570 sb.append(this.advisors.size()).append(" advisors "); 571 sb.append(this.advisors).append("; "); 572 sb.append("targetSource [").append(this.targetSource).append("]; "); 573 sb.append(super.toString()); 574 return sb.toString(); 575 } 576 577 578 /** 579 * Simple wrapper class around a Method. Used as the key when 580 * caching methods, for efficient equals and hashCode comparisons. 581 */ 582 private static final class MethodCacheKey implements Comparable<MethodCacheKey> { 583 584 private final Method method; 585 586 private final int hashCode; 587 588 public MethodCacheKey(Method method) { 589 this.method = method; 590 this.hashCode = method.hashCode(); 591 } 592 593 @Override 594 public boolean equals(@Nullable Object other) { 595 return (this == other || (other instanceof MethodCacheKey && 596 this.method == ((MethodCacheKey) other).method)); 597 } 598 599 @Override 600 public int hashCode() { 601 return this.hashCode; 602 } 603 604 @Override 605 public String toString() { 606 return this.method.toString(); 607 } 608 609 @Override 610 public int compareTo(MethodCacheKey other) { 611 int result = this.method.getName().compareTo(other.method.getName()); 612 if (result == 0) { 613 result = this.method.toString().compareTo(other.method.toString()); 614 } 615 return result; 616 } 617 } 618 619}