001/* 002 * Copyright 2002-2018 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.util.Assert; 041import org.springframework.util.ClassUtils; 042import org.springframework.util.CollectionUtils; 043 044/** 045 * Base class for AOP proxy configuration managers. 046 * These are not themselves AOP proxies, but subclasses of this class are 047 * normally factories from which AOP proxy instances are obtained directly. 048 * 049 * <p>This class frees subclasses of the housekeeping of Advices 050 * and Advisors, but doesn't actually implement proxy creation 051 * methods, which are provided by subclasses. 052 * 053 * <p>This class is serializable; subclasses need not be. 054 * This class is used to hold snapshots of proxies. 055 * 056 * @author Rod Johnson 057 * @author Juergen Hoeller 058 * @see org.springframework.aop.framework.AopProxy 059 */ 060public class AdvisedSupport extends ProxyConfig implements Advised { 061 062 /** use serialVersionUID from Spring 2.0 for interoperability */ 063 private static final long serialVersionUID = 2651364800145442165L; 064 065 066 /** 067 * Canonical TargetSource when there's no target, and behavior is 068 * supplied by the advisors. 069 */ 070 public static final TargetSource EMPTY_TARGET_SOURCE = EmptyTargetSource.INSTANCE; 071 072 073 /** Package-protected to allow direct access for efficiency */ 074 TargetSource targetSource = EMPTY_TARGET_SOURCE; 075 076 /** Whether the Advisors are already filtered for the specific target class */ 077 private boolean preFiltered = false; 078 079 /** The AdvisorChainFactory to use */ 080 AdvisorChainFactory advisorChainFactory = new DefaultAdvisorChainFactory(); 081 082 /** Cache with Method as key and advisor chain List as value */ 083 private transient Map<MethodCacheKey, List<Object>> methodCache; 084 085 /** 086 * Interfaces to be implemented by the proxy. Held in List to keep the order 087 * of registration, to create JDK proxy with specified order of interfaces. 088 */ 089 private List<Class<?>> interfaces = new ArrayList<Class<?>>(); 090 091 /** 092 * List of Advisors. If an Advice is added, it will be wrapped 093 * in an Advisor before being added to this List. 094 */ 095 private List<Advisor> advisors = new ArrayList<Advisor>(); 096 097 /** 098 * Array updated on changes to the advisors list, which is easier 099 * to manipulate internally. 100 */ 101 private Advisor[] advisorArray = new Advisor[0]; 102 103 104 /** 105 * No-arg constructor for use as a JavaBean. 106 */ 107 public AdvisedSupport() { 108 initMethodCache(); 109 } 110 111 /** 112 * Create a AdvisedSupport instance with the given parameters. 113 * @param interfaces the proxied interfaces 114 */ 115 public AdvisedSupport(Class<?>... interfaces) { 116 this(); 117 setInterfaces(interfaces); 118 } 119 120 /** 121 * Initialize the method cache. 122 */ 123 private void initMethodCache() { 124 this.methodCache = new ConcurrentHashMap<MethodCacheKey, List<Object>>(32); 125 } 126 127 128 /** 129 * Set the given object as target. 130 * Will create a SingletonTargetSource for the object. 131 * @see #setTargetSource 132 * @see org.springframework.aop.target.SingletonTargetSource 133 */ 134 public void setTarget(Object target) { 135 setTargetSource(new SingletonTargetSource(target)); 136 } 137 138 @Override 139 public void setTargetSource(TargetSource targetSource) { 140 this.targetSource = (targetSource != null ? targetSource : EMPTY_TARGET_SOURCE); 141 } 142 143 @Override 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 */ 161 public void setTargetClass(Class<?> targetClass) { 162 this.targetSource = EmptyTargetSource.forClass(targetClass); 163 } 164 165 @Override 166 public Class<?> getTargetClass() { 167 return this.targetSource.getTargetClass(); 168 } 169 170 @Override 171 public void setPreFiltered(boolean preFiltered) { 172 this.preFiltered = preFiltered; 173 } 174 175 @Override 176 public boolean isPreFiltered() { 177 return this.preFiltered; 178 } 179 180 /** 181 * Set the advisor chain factory to use. 182 * <p>Default is a {@link DefaultAdvisorChainFactory}. 183 */ 184 public void setAdvisorChainFactory(AdvisorChainFactory advisorChainFactory) { 185 Assert.notNull(advisorChainFactory, "AdvisorChainFactory must not be null"); 186 this.advisorChainFactory = advisorChainFactory; 187 } 188 189 /** 190 * Return the advisor chain factory to use (never {@code null}). 191 */ 192 public AdvisorChainFactory getAdvisorChainFactory() { 193 return this.advisorChainFactory; 194 } 195 196 197 /** 198 * Set the interfaces to be proxied. 199 */ 200 public void setInterfaces(Class<?>... interfaces) { 201 Assert.notNull(interfaces, "Interfaces must not be null"); 202 this.interfaces.clear(); 203 for (Class<?> ifc : interfaces) { 204 addInterface(ifc); 205 } 206 } 207 208 /** 209 * Add a new proxied interface. 210 * @param intf the additional interface to proxy 211 */ 212 public void addInterface(Class<?> intf) { 213 Assert.notNull(intf, "Interface must not be null"); 214 if (!intf.isInterface()) { 215 throw new IllegalArgumentException("[" + intf.getName() + "] is not an interface"); 216 } 217 if (!this.interfaces.contains(intf)) { 218 this.interfaces.add(intf); 219 adviceChanged(); 220 } 221 } 222 223 /** 224 * Remove a proxied interface. 225 * <p>Does nothing if the given interface isn't proxied. 226 * @param intf the interface to remove from the proxy 227 * @return {@code true} if the interface was removed; {@code false} 228 * if the interface was not found and hence could not be removed 229 */ 230 public boolean removeInterface(Class<?> intf) { 231 return this.interfaces.remove(intf); 232 } 233 234 @Override 235 public Class<?>[] getProxiedInterfaces() { 236 return ClassUtils.toClassArray(this.interfaces); 237 } 238 239 @Override 240 public boolean isInterfaceProxied(Class<?> intf) { 241 for (Class<?> proxyIntf : this.interfaces) { 242 if (intf.isAssignableFrom(proxyIntf)) { 243 return true; 244 } 245 } 246 return false; 247 } 248 249 250 @Override 251 public final Advisor[] getAdvisors() { 252 return this.advisorArray; 253 } 254 255 @Override 256 public void addAdvisor(Advisor advisor) { 257 int pos = this.advisors.size(); 258 addAdvisor(pos, advisor); 259 } 260 261 @Override 262 public void addAdvisor(int pos, Advisor advisor) throws AopConfigException { 263 if (advisor instanceof IntroductionAdvisor) { 264 validateIntroductionAdvisor((IntroductionAdvisor) advisor); 265 } 266 addAdvisorInternal(pos, advisor); 267 } 268 269 @Override 270 public boolean removeAdvisor(Advisor advisor) { 271 int index = indexOf(advisor); 272 if (index == -1) { 273 return false; 274 } 275 else { 276 removeAdvisor(index); 277 return true; 278 } 279 } 280 281 @Override 282 public void removeAdvisor(int index) throws AopConfigException { 283 if (isFrozen()) { 284 throw new AopConfigException("Cannot remove Advisor: Configuration is frozen."); 285 } 286 if (index < 0 || index > this.advisors.size() - 1) { 287 throw new AopConfigException("Advisor index " + index + " is out of bounds: " + 288 "This configuration only has " + this.advisors.size() + " advisors."); 289 } 290 291 Advisor advisor = this.advisors.get(index); 292 if (advisor instanceof IntroductionAdvisor) { 293 IntroductionAdvisor ia = (IntroductionAdvisor) advisor; 294 // We need to remove introduction interfaces. 295 for (int j = 0; j < ia.getInterfaces().length; j++) { 296 removeInterface(ia.getInterfaces()[j]); 297 } 298 } 299 300 this.advisors.remove(index); 301 updateAdvisorArray(); 302 adviceChanged(); 303 } 304 305 @Override 306 public int indexOf(Advisor advisor) { 307 Assert.notNull(advisor, "Advisor must not be null"); 308 return this.advisors.indexOf(advisor); 309 } 310 311 @Override 312 public boolean replaceAdvisor(Advisor a, Advisor b) throws AopConfigException { 313 Assert.notNull(a, "Advisor a must not be null"); 314 Assert.notNull(b, "Advisor b must not be null"); 315 int index = indexOf(a); 316 if (index == -1) { 317 return false; 318 } 319 removeAdvisor(index); 320 addAdvisor(index, b); 321 return true; 322 } 323 324 /** 325 * Add all of the given advisors to this proxy configuration. 326 * @param advisors the advisors to register 327 */ 328 public void addAdvisors(Advisor... advisors) { 329 addAdvisors(Arrays.asList(advisors)); 330 } 331 332 /** 333 * Add all of the given advisors to this proxy configuration. 334 * @param advisors the advisors to register 335 */ 336 public void addAdvisors(Collection<Advisor> advisors) { 337 if (isFrozen()) { 338 throw new AopConfigException("Cannot add advisor: Configuration is frozen."); 339 } 340 if (!CollectionUtils.isEmpty(advisors)) { 341 for (Advisor advisor : advisors) { 342 if (advisor instanceof IntroductionAdvisor) { 343 validateIntroductionAdvisor((IntroductionAdvisor) advisor); 344 } 345 Assert.notNull(advisor, "Advisor must not be null"); 346 this.advisors.add(advisor); 347 } 348 updateAdvisorArray(); 349 adviceChanged(); 350 } 351 } 352 353 private void validateIntroductionAdvisor(IntroductionAdvisor advisor) { 354 advisor.validateInterfaces(); 355 // If the advisor passed validation, we can make the change. 356 Class<?>[] ifcs = advisor.getInterfaces(); 357 for (Class<?> ifc : ifcs) { 358 addInterface(ifc); 359 } 360 } 361 362 private void addAdvisorInternal(int pos, Advisor advisor) throws AopConfigException { 363 Assert.notNull(advisor, "Advisor must not be null"); 364 if (isFrozen()) { 365 throw new AopConfigException("Cannot add advisor: Configuration is frozen."); 366 } 367 if (pos > this.advisors.size()) { 368 throw new IllegalArgumentException( 369 "Illegal position " + pos + " in advisor list with size " + this.advisors.size()); 370 } 371 this.advisors.add(pos, advisor); 372 updateAdvisorArray(); 373 adviceChanged(); 374 } 375 376 /** 377 * Bring the array up to date with the list. 378 */ 379 protected final void updateAdvisorArray() { 380 this.advisorArray = this.advisors.toArray(new Advisor[this.advisors.size()]); 381 } 382 383 /** 384 * Allows uncontrolled access to the {@link List} of {@link Advisor Advisors}. 385 * <p>Use with care, and remember to {@link #updateAdvisorArray() refresh the advisor array} 386 * and {@link #adviceChanged() fire advice changed events} when making any modifications. 387 */ 388 protected final List<Advisor> getAdvisorsInternal() { 389 return this.advisors; 390 } 391 392 393 @Override 394 public void addAdvice(Advice advice) throws AopConfigException { 395 int pos = this.advisors.size(); 396 addAdvice(pos, advice); 397 } 398 399 /** 400 * Cannot add introductions this way unless the advice implements IntroductionInfo. 401 */ 402 @Override 403 public void addAdvice(int pos, Advice advice) throws AopConfigException { 404 Assert.notNull(advice, "Advice must not be null"); 405 if (advice instanceof IntroductionInfo) { 406 // We don't need an IntroductionAdvisor for this kind of introduction: 407 // It's fully self-describing. 408 addAdvisor(pos, new DefaultIntroductionAdvisor(advice, (IntroductionInfo) advice)); 409 } 410 else if (advice instanceof DynamicIntroductionAdvice) { 411 // We need an IntroductionAdvisor for this kind of introduction. 412 throw new AopConfigException("DynamicIntroductionAdvice may only be added as part of IntroductionAdvisor"); 413 } 414 else { 415 addAdvisor(pos, new DefaultPointcutAdvisor(advice)); 416 } 417 } 418 419 @Override 420 public boolean removeAdvice(Advice advice) throws AopConfigException { 421 int index = indexOf(advice); 422 if (index == -1) { 423 return false; 424 } 425 else { 426 removeAdvisor(index); 427 return true; 428 } 429 } 430 431 @Override 432 public int indexOf(Advice advice) { 433 Assert.notNull(advice, "Advice must not be null"); 434 for (int i = 0; i < this.advisors.size(); i++) { 435 Advisor advisor = this.advisors.get(i); 436 if (advisor.getAdvice() == advice) { 437 return i; 438 } 439 } 440 return -1; 441 } 442 443 /** 444 * Is the given advice included in any advisor within this proxy configuration? 445 * @param advice the advice to check inclusion of 446 * @return whether this advice instance is included 447 */ 448 public boolean adviceIncluded(Advice advice) { 449 if (advice != null) { 450 for (Advisor advisor : this.advisors) { 451 if (advisor.getAdvice() == advice) { 452 return true; 453 } 454 } 455 } 456 return false; 457 } 458 459 /** 460 * Count advices of the given class. 461 * @param adviceClass the advice class to check 462 * @return the count of the interceptors of this class or subclasses 463 */ 464 public int countAdvicesOfType(Class<?> adviceClass) { 465 int count = 0; 466 if (adviceClass != null) { 467 for (Advisor advisor : this.advisors) { 468 if (adviceClass.isInstance(advisor.getAdvice())) { 469 count++; 470 } 471 } 472 } 473 return count; 474 } 475 476 477 /** 478 * Determine a list of {@link org.aopalliance.intercept.MethodInterceptor} objects 479 * for the given method, based on this configuration. 480 * @param method the proxied method 481 * @param targetClass the target class 482 * @return a List of MethodInterceptors (may also include InterceptorAndDynamicMethodMatchers) 483 */ 484 public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, Class<?> targetClass) { 485 MethodCacheKey cacheKey = new MethodCacheKey(method); 486 List<Object> cached = this.methodCache.get(cacheKey); 487 if (cached == null) { 488 cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice( 489 this, method, targetClass); 490 this.methodCache.put(cacheKey, cached); 491 } 492 return cached; 493 } 494 495 /** 496 * Invoked when advice has changed. 497 */ 498 protected void adviceChanged() { 499 this.methodCache.clear(); 500 } 501 502 /** 503 * Call this method on a new instance created by the no-arg constructor 504 * to create an independent copy of the configuration from the given object. 505 * @param other the AdvisedSupport object to copy configuration from 506 */ 507 protected void copyConfigurationFrom(AdvisedSupport other) { 508 copyConfigurationFrom(other, other.targetSource, new ArrayList<Advisor>(other.advisors)); 509 } 510 511 /** 512 * Copy the AOP configuration from the given AdvisedSupport object, 513 * but allow substitution of a fresh TargetSource and a given interceptor chain. 514 * @param other the AdvisedSupport object to take proxy configuration from 515 * @param targetSource the new TargetSource 516 * @param advisors the Advisors for the chain 517 */ 518 protected void copyConfigurationFrom(AdvisedSupport other, TargetSource targetSource, List<Advisor> advisors) { 519 copyFrom(other); 520 this.targetSource = targetSource; 521 this.advisorChainFactory = other.advisorChainFactory; 522 this.interfaces = new ArrayList<Class<?>>(other.interfaces); 523 for (Advisor advisor : advisors) { 524 if (advisor instanceof IntroductionAdvisor) { 525 validateIntroductionAdvisor((IntroductionAdvisor) advisor); 526 } 527 Assert.notNull(advisor, "Advisor must not be null"); 528 this.advisors.add(advisor); 529 } 530 updateAdvisorArray(); 531 adviceChanged(); 532 } 533 534 /** 535 * Build a configuration-only copy of this AdvisedSupport, 536 * replacing the TargetSource. 537 */ 538 AdvisedSupport getConfigurationOnlyCopy() { 539 AdvisedSupport copy = new AdvisedSupport(); 540 copy.copyFrom(this); 541 copy.targetSource = EmptyTargetSource.forClass(getTargetClass(), getTargetSource().isStatic()); 542 copy.advisorChainFactory = this.advisorChainFactory; 543 copy.interfaces = this.interfaces; 544 copy.advisors = this.advisors; 545 copy.updateAdvisorArray(); 546 return copy; 547 } 548 549 550 //--------------------------------------------------------------------- 551 // Serialization support 552 //--------------------------------------------------------------------- 553 554 private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { 555 // Rely on default serialization; just initialize state after deserialization. 556 ois.defaultReadObject(); 557 558 // Initialize transient fields. 559 initMethodCache(); 560 } 561 562 563 @Override 564 public String toProxyConfigString() { 565 return toString(); 566 } 567 568 /** 569 * For debugging/diagnostic use. 570 */ 571 @Override 572 public String toString() { 573 StringBuilder sb = new StringBuilder(getClass().getName()); 574 sb.append(": ").append(this.interfaces.size()).append(" interfaces "); 575 sb.append(ClassUtils.classNamesToString(this.interfaces)).append("; "); 576 sb.append(this.advisors.size()).append(" advisors "); 577 sb.append(this.advisors).append("; "); 578 sb.append("targetSource [").append(this.targetSource).append("]; "); 579 sb.append(super.toString()); 580 return sb.toString(); 581 } 582 583 584 /** 585 * Simple wrapper class around a Method. Used as the key when 586 * caching methods, for efficient equals and hashCode comparisons. 587 */ 588 private static final class MethodCacheKey implements Comparable<MethodCacheKey> { 589 590 private final Method method; 591 592 private final int hashCode; 593 594 public MethodCacheKey(Method method) { 595 this.method = method; 596 this.hashCode = method.hashCode(); 597 } 598 599 @Override 600 public boolean equals(Object other) { 601 return (this == other || (other instanceof MethodCacheKey && 602 this.method == ((MethodCacheKey) other).method)); 603 } 604 605 @Override 606 public int hashCode() { 607 return this.hashCode; 608 } 609 610 @Override 611 public String toString() { 612 return this.method.toString(); 613 } 614 615 @Override 616 public int compareTo(MethodCacheKey other) { 617 int result = this.method.getName().compareTo(other.method.getName()); 618 if (result == 0) { 619 result = this.method.toString().compareTo(other.method.toString()); 620 } 621 return result; 622 } 623 } 624 625}