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.interceptor;
018
019import org.aopalliance.intercept.MethodInterceptor;
020import org.aopalliance.intercept.MethodInvocation;
021
022import org.springframework.aop.Advisor;
023import org.springframework.aop.ProxyMethodInvocation;
024import org.springframework.aop.support.DefaultIntroductionAdvisor;
025import org.springframework.aop.support.DefaultPointcutAdvisor;
026import org.springframework.aop.support.DelegatingIntroductionInterceptor;
027import org.springframework.beans.factory.NamedBean;
028
029/**
030 * Convenient methods for creating advisors that may be used when autoproxying beans
031 * created with the Spring IoC container, binding the bean name to the current
032 * invocation. May support a {@code bean()} pointcut designator with AspectJ.
033 *
034 * <p>Typically used in Spring auto-proxying, where the bean name is known
035 * at proxy creation time.
036 *
037 * @author Rod Johnson
038 * @author Juergen Hoeller
039 * @since 2.0
040 * @see org.springframework.beans.factory.NamedBean
041 */
042public abstract class ExposeBeanNameAdvisors {
043
044        /**
045         * Binding for the bean name of the bean which is currently being invoked
046         * in the ReflectiveMethodInvocation userAttributes Map.
047         */
048        private static final String BEAN_NAME_ATTRIBUTE = ExposeBeanNameAdvisors.class.getName() + ".BEAN_NAME";
049
050
051        /**
052         * Find the bean name for the current invocation. Assumes that an ExposeBeanNameAdvisor
053         * has been included in the interceptor chain, and that the invocation is exposed
054         * with ExposeInvocationInterceptor.
055         * @return the bean name (never {@code null})
056         * @throws IllegalStateException if the bean name has not been exposed
057         */
058        public static String getBeanName() throws IllegalStateException {
059                return getBeanName(ExposeInvocationInterceptor.currentInvocation());
060        }
061
062        /**
063         * Find the bean name for the given invocation. Assumes that an ExposeBeanNameAdvisor
064         * has been included in the interceptor chain.
065         * @param mi MethodInvocation that should contain the bean name as an attribute
066         * @return the bean name (never {@code null})
067         * @throws IllegalStateException if the bean name has not been exposed
068         */
069        public static String getBeanName(MethodInvocation mi) throws IllegalStateException {
070                if (!(mi instanceof ProxyMethodInvocation)) {
071                        throw new IllegalArgumentException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
072                }
073                ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi;
074                String beanName = (String) pmi.getUserAttribute(BEAN_NAME_ATTRIBUTE);
075                if (beanName == null) {
076                        throw new IllegalStateException("Cannot get bean name; not set on MethodInvocation: " + mi);
077                }
078                return beanName;
079        }
080
081        /**
082         * Create a new advisor that will expose the given bean name,
083         * with no introduction
084         * @param beanName bean name to expose
085         */
086        public static Advisor createAdvisorWithoutIntroduction(String beanName) {
087                return new DefaultPointcutAdvisor(new ExposeBeanNameInterceptor(beanName));
088        }
089
090        /**
091         * Create a new advisor that will expose the given bean name, introducing
092         * the NamedBean interface to make the bean name accessible without forcing
093         * the target object to be aware of this Spring IoC concept.
094         * @param beanName the bean name to expose
095         */
096        public static Advisor createAdvisorIntroducingNamedBean(String beanName) {
097                return new DefaultIntroductionAdvisor(new ExposeBeanNameIntroduction(beanName));
098        }
099
100
101        /**
102         * Interceptor that exposes the specified bean name as invocation attribute.
103         */
104        private static class ExposeBeanNameInterceptor implements MethodInterceptor {
105
106                private final String beanName;
107
108                public ExposeBeanNameInterceptor(String beanName) {
109                        this.beanName = beanName;
110                }
111
112                @Override
113                public Object invoke(MethodInvocation mi) throws Throwable {
114                        if (!(mi instanceof ProxyMethodInvocation)) {
115                                throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
116                        }
117                        ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi;
118                        pmi.setUserAttribute(BEAN_NAME_ATTRIBUTE, this.beanName);
119                        return mi.proceed();
120                }
121        }
122
123
124        /**
125         * Introduction that exposes the specified bean name as invocation attribute.
126         */
127        @SuppressWarnings("serial")
128        private static class ExposeBeanNameIntroduction extends DelegatingIntroductionInterceptor implements NamedBean {
129
130                private final String beanName;
131
132                public ExposeBeanNameIntroduction(String beanName) {
133                        this.beanName = beanName;
134                }
135
136                @Override
137                public Object invoke(MethodInvocation mi) throws Throwable {
138                        if (!(mi instanceof ProxyMethodInvocation)) {
139                                throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
140                        }
141                        ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi;
142                        pmi.setUserAttribute(BEAN_NAME_ATTRIBUTE, this.beanName);
143                        return super.invoke(mi);
144                }
145
146                @Override
147                public String getBeanName() {
148                        return this.beanName;
149                }
150        }
151
152}