001/* 002 * Copyright 2002-2017 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.support; 018 019import java.util.Map; 020import java.util.WeakHashMap; 021 022import org.aopalliance.intercept.MethodInvocation; 023 024import org.springframework.aop.DynamicIntroductionAdvice; 025import org.springframework.aop.IntroductionInterceptor; 026import org.springframework.aop.ProxyMethodInvocation; 027import org.springframework.lang.Nullable; 028import org.springframework.util.ReflectionUtils; 029 030/** 031 * Convenient implementation of the 032 * {@link org.springframework.aop.IntroductionInterceptor} interface. 033 * 034 * <p>This differs from {@link DelegatingIntroductionInterceptor} in that a single 035 * instance of this class can be used to advise multiple target objects, and each target 036 * object will have its <i>own</i> delegate (whereas DelegatingIntroductionInterceptor 037 * shares the same delegate, and hence the same state across all targets). 038 * 039 * <p>The {@code suppressInterface} method can be used to suppress interfaces 040 * implemented by the delegate class but which should not be introduced to the 041 * owning AOP proxy. 042 * 043 * <p>An instance of this class is serializable if the delegates are. 044 * 045 * <p><i>Note: There are some implementation similarities between this class and 046 * {@link DelegatingIntroductionInterceptor} that suggest a possible refactoring 047 * to extract a common ancestor class in the future.</i> 048 * 049 * @author Adrian Colyer 050 * @author Juergen Hoeller 051 * @since 2.0 052 * @see #suppressInterface 053 * @see DelegatingIntroductionInterceptor 054 */ 055@SuppressWarnings("serial") 056public class DelegatePerTargetObjectIntroductionInterceptor extends IntroductionInfoSupport 057 implements IntroductionInterceptor { 058 059 /** 060 * Hold weak references to keys as we don't want to interfere with garbage collection.. 061 */ 062 private final Map<Object, Object> delegateMap = new WeakHashMap<>(); 063 064 private Class<?> defaultImplType; 065 066 private Class<?> interfaceType; 067 068 069 public DelegatePerTargetObjectIntroductionInterceptor(Class<?> defaultImplType, Class<?> interfaceType) { 070 this.defaultImplType = defaultImplType; 071 this.interfaceType = interfaceType; 072 // Create a new delegate now (but don't store it in the map). 073 // We do this for two reasons: 074 // 1) to fail early if there is a problem instantiating delegates 075 // 2) to populate the interface map once and once only 076 Object delegate = createNewDelegate(); 077 implementInterfacesOnObject(delegate); 078 suppressInterface(IntroductionInterceptor.class); 079 suppressInterface(DynamicIntroductionAdvice.class); 080 } 081 082 083 /** 084 * Subclasses may need to override this if they want to perform custom 085 * behaviour in around advice. However, subclasses should invoke this 086 * method, which handles introduced interfaces and forwarding to the target. 087 */ 088 @Override 089 @Nullable 090 public Object invoke(MethodInvocation mi) throws Throwable { 091 if (isMethodOnIntroducedInterface(mi)) { 092 Object delegate = getIntroductionDelegateFor(mi.getThis()); 093 094 // Using the following method rather than direct reflection, 095 // we get correct handling of InvocationTargetException 096 // if the introduced method throws an exception. 097 Object retVal = AopUtils.invokeJoinpointUsingReflection(delegate, mi.getMethod(), mi.getArguments()); 098 099 // Massage return value if possible: if the delegate returned itself, 100 // we really want to return the proxy. 101 if (retVal == delegate && mi instanceof ProxyMethodInvocation) { 102 retVal = ((ProxyMethodInvocation) mi).getProxy(); 103 } 104 return retVal; 105 } 106 107 return doProceed(mi); 108 } 109 110 /** 111 * Proceed with the supplied {@link org.aopalliance.intercept.MethodInterceptor}. 112 * Subclasses can override this method to intercept method invocations on the 113 * target object which is useful when an introduction needs to monitor the object 114 * that it is introduced into. This method is <strong>never</strong> called for 115 * {@link MethodInvocation MethodInvocations} on the introduced interfaces. 116 */ 117 protected Object doProceed(MethodInvocation mi) throws Throwable { 118 // If we get here, just pass the invocation on. 119 return mi.proceed(); 120 } 121 122 private Object getIntroductionDelegateFor(Object targetObject) { 123 synchronized (this.delegateMap) { 124 if (this.delegateMap.containsKey(targetObject)) { 125 return this.delegateMap.get(targetObject); 126 } 127 else { 128 Object delegate = createNewDelegate(); 129 this.delegateMap.put(targetObject, delegate); 130 return delegate; 131 } 132 } 133 } 134 135 private Object createNewDelegate() { 136 try { 137 return ReflectionUtils.accessibleConstructor(this.defaultImplType).newInstance(); 138 } 139 catch (Throwable ex) { 140 throw new IllegalArgumentException("Cannot create default implementation for '" + 141 this.interfaceType.getName() + "' mixin (" + this.defaultImplType.getName() + "): " + ex); 142 } 143 } 144 145}