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 org.aopalliance.intercept.MethodInvocation;
020
021import org.springframework.aop.DynamicIntroductionAdvice;
022import org.springframework.aop.IntroductionInterceptor;
023import org.springframework.aop.ProxyMethodInvocation;
024import org.springframework.util.Assert;
025
026/**
027 * Convenient implementation of the
028 * {@link org.springframework.aop.IntroductionInterceptor} interface.
029 *
030 * <p>Subclasses merely need to extend this class and implement the interfaces
031 * to be introduced themselves. In this case the delegate is the subclass
032 * instance itself. Alternatively a separate delegate may implement the
033 * interface, and be set via the delegate bean property.
034 *
035 * <p>Delegates or subclasses may implement any number of interfaces.
036 * All interfaces except IntroductionInterceptor are picked up from
037 * the subclass or delegate by default.
038 *
039 * <p>The {@code suppressInterface} method can be used to suppress interfaces
040 * implemented by the delegate but which should not be introduced to the owning
041 * AOP proxy.
042 *
043 * <p>An instance of this class is serializable if the delegate is.
044 *
045 * @author Rod Johnson
046 * @author Juergen Hoeller
047 * @since 16.11.2003
048 * @see #suppressInterface
049 * @see DelegatePerTargetObjectIntroductionInterceptor
050 */
051@SuppressWarnings("serial")
052public class DelegatingIntroductionInterceptor extends IntroductionInfoSupport
053                implements IntroductionInterceptor {
054
055        /**
056         * Object that actually implements the interfaces.
057         * May be "this" if a subclass implements the introduced interfaces.
058         */
059        private Object delegate;
060
061
062        /**
063         * Construct a new DelegatingIntroductionInterceptor, providing
064         * a delegate that implements the interfaces to be introduced.
065         * @param delegate the delegate that implements the introduced interfaces
066         */
067        public DelegatingIntroductionInterceptor(Object delegate) {
068                init(delegate);
069        }
070
071        /**
072         * Construct a new DelegatingIntroductionInterceptor.
073         * The delegate will be the subclass, which must implement
074         * additional interfaces.
075         */
076        protected DelegatingIntroductionInterceptor() {
077                init(this);
078        }
079
080
081        /**
082         * Both constructors use this init method, as it is impossible to pass
083         * a "this" reference from one constructor to another.
084         * @param delegate the delegate object
085         */
086        private void init(Object delegate) {
087                Assert.notNull(delegate, "Delegate must not be null");
088                this.delegate = delegate;
089                implementInterfacesOnObject(delegate);
090
091                // We don't want to expose the control interface
092                suppressInterface(IntroductionInterceptor.class);
093                suppressInterface(DynamicIntroductionAdvice.class);
094        }
095
096
097        /**
098         * Subclasses may need to override this if they want to perform custom
099         * behaviour in around advice. However, subclasses should invoke this
100         * method, which handles introduced interfaces and forwarding to the target.
101         */
102        @Override
103        public Object invoke(MethodInvocation mi) throws Throwable {
104                if (isMethodOnIntroducedInterface(mi)) {
105                        // Using the following method rather than direct reflection, we
106                        // get correct handling of InvocationTargetException
107                        // if the introduced method throws an exception.
108                        Object retVal = AopUtils.invokeJoinpointUsingReflection(this.delegate, mi.getMethod(), mi.getArguments());
109
110                        // Massage return value if possible: if the delegate returned itself,
111                        // we really want to return the proxy.
112                        if (retVal == this.delegate && mi instanceof ProxyMethodInvocation) {
113                                Object proxy = ((ProxyMethodInvocation) mi).getProxy();
114                                if (mi.getMethod().getReturnType().isInstance(proxy)) {
115                                        retVal = proxy;
116                                }
117                        }
118                        return retVal;
119                }
120
121                return doProceed(mi);
122        }
123
124        /**
125         * Proceed with the supplied {@link org.aopalliance.intercept.MethodInterceptor}.
126         * Subclasses can override this method to intercept method invocations on the
127         * target object which is useful when an introduction needs to monitor the object
128         * that it is introduced into. This method is <strong>never</strong> called for
129         * {@link MethodInvocation MethodInvocations} on the introduced interfaces.
130         */
131        protected Object doProceed(MethodInvocation mi) throws Throwable {
132                // If we get here, just pass the invocation on.
133                return mi.proceed();
134        }
135
136}