001/*
002 * Copyright 2002-2016 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.aspectj;
018
019import java.io.Serializable;
020import java.lang.reflect.Method;
021import java.lang.reflect.Type;
022
023import org.springframework.aop.AfterAdvice;
024import org.springframework.aop.AfterReturningAdvice;
025import org.springframework.lang.Nullable;
026import org.springframework.util.ClassUtils;
027import org.springframework.util.TypeUtils;
028
029/**
030 * Spring AOP advice wrapping an AspectJ after-returning advice method.
031 *
032 * @author Rod Johnson
033 * @author Juergen Hoeller
034 * @author Ramnivas Laddad
035 * @since 2.0
036 */
037@SuppressWarnings("serial")
038public class AspectJAfterReturningAdvice extends AbstractAspectJAdvice
039                implements AfterReturningAdvice, AfterAdvice, Serializable {
040
041        public AspectJAfterReturningAdvice(
042                        Method aspectJBeforeAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif) {
043
044                super(aspectJBeforeAdviceMethod, pointcut, aif);
045        }
046
047
048        @Override
049        public boolean isBeforeAdvice() {
050                return false;
051        }
052
053        @Override
054        public boolean isAfterAdvice() {
055                return true;
056        }
057
058        @Override
059        public void setReturningName(String name) {
060                setReturningNameNoCheck(name);
061        }
062
063        @Override
064        public void afterReturning(@Nullable Object returnValue, Method method, Object[] args, @Nullable Object target) throws Throwable {
065                if (shouldInvokeOnReturnValueOf(method, returnValue)) {
066                        invokeAdviceMethod(getJoinPointMatch(), returnValue, null);
067                }
068        }
069
070
071        /**
072         * Following AspectJ semantics, if a returning clause was specified, then the
073         * advice is only invoked if the returned value is an instance of the given
074         * returning type and generic type parameters, if any, match the assignment
075         * rules. If the returning type is Object, the advice is *always* invoked.
076         * @param returnValue the return value of the target method
077         * @return whether to invoke the advice method for the given return value
078         */
079        private boolean shouldInvokeOnReturnValueOf(Method method, @Nullable Object returnValue) {
080                Class<?> type = getDiscoveredReturningType();
081                Type genericType = getDiscoveredReturningGenericType();
082                // If we aren't dealing with a raw type, check if generic parameters are assignable.
083                return (matchesReturnValue(type, method, returnValue) &&
084                                (genericType == null || genericType == type ||
085                                                TypeUtils.isAssignable(genericType, method.getGenericReturnType())));
086        }
087
088        /**
089         * Following AspectJ semantics, if a return value is null (or return type is void),
090         * then the return type of target method should be used to determine whether advice
091         * is invoked or not. Also, even if the return type is void, if the type of argument
092         * declared in the advice method is Object, then the advice must still get invoked.
093         * @param type the type of argument declared in advice method
094         * @param method the advice method
095         * @param returnValue the return value of the target method
096         * @return whether to invoke the advice method for the given return value and type
097         */
098        private boolean matchesReturnValue(Class<?> type, Method method, @Nullable Object returnValue) {
099                if (returnValue != null) {
100                        return ClassUtils.isAssignableValue(type, returnValue);
101                }
102                else if (Object.class == type && void.class == method.getReturnType()) {
103                        return true;
104                }
105                else {
106                        return ClassUtils.isAssignable(type, method.getReturnType());
107                }
108        }
109
110}