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