001/*
002 * Copyright 2002-2018 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.framework;
018
019import java.util.Map;
020import java.util.concurrent.ConcurrentHashMap;
021
022import org.springframework.aop.Advisor;
023import org.springframework.aop.support.AopUtils;
024import org.springframework.beans.factory.config.BeanPostProcessor;
025
026/**
027 * Base class for {@link BeanPostProcessor} implementations that apply a
028 * Spring AOP {@link Advisor} to specific beans.
029 *
030 * @author Juergen Hoeller
031 * @since 3.2
032 */
033@SuppressWarnings("serial")
034public abstract class AbstractAdvisingBeanPostProcessor extends ProxyProcessorSupport implements BeanPostProcessor {
035
036        protected Advisor advisor;
037
038        protected boolean beforeExistingAdvisors = false;
039
040        private final Map<Class<?>, Boolean> eligibleBeans = new ConcurrentHashMap<Class<?>, Boolean>(256);
041
042
043        /**
044         * Set whether this post-processor's advisor is supposed to apply before
045         * existing advisors when encountering a pre-advised object.
046         * <p>Default is "false", applying the advisor after existing advisors, i.e.
047         * as close as possible to the target method. Switch this to "true" in order
048         * for this post-processor's advisor to wrap existing advisors as well.
049         * <p>Note: Check the concrete post-processor's javadoc whether it possibly
050         * changes this flag by default, depending on the nature of its advisor.
051         */
052        public void setBeforeExistingAdvisors(boolean beforeExistingAdvisors) {
053                this.beforeExistingAdvisors = beforeExistingAdvisors;
054        }
055
056
057        @Override
058        public Object postProcessBeforeInitialization(Object bean, String beanName) {
059                return bean;
060        }
061
062        @Override
063        public Object postProcessAfterInitialization(Object bean, String beanName) {
064                if (bean instanceof AopInfrastructureBean) {
065                        // Ignore AOP infrastructure such as scoped proxies.
066                        return bean;
067                }
068
069                if (bean instanceof Advised) {
070                        Advised advised = (Advised) bean;
071                        if (!advised.isFrozen() && isEligible(AopUtils.getTargetClass(bean))) {
072                                // Add our local Advisor to the existing proxy's Advisor chain...
073                                if (this.beforeExistingAdvisors) {
074                                        advised.addAdvisor(0, this.advisor);
075                                }
076                                else {
077                                        advised.addAdvisor(this.advisor);
078                                }
079                                return bean;
080                        }
081                }
082
083                if (isEligible(bean, beanName)) {
084                        ProxyFactory proxyFactory = prepareProxyFactory(bean, beanName);
085                        if (!proxyFactory.isProxyTargetClass()) {
086                                evaluateProxyInterfaces(bean.getClass(), proxyFactory);
087                        }
088                        proxyFactory.addAdvisor(this.advisor);
089                        customizeProxyFactory(proxyFactory);
090                        return proxyFactory.getProxy(getProxyClassLoader());
091                }
092
093                // No proxy needed.
094                return bean;
095        }
096
097        /**
098         * Check whether the given bean is eligible for advising with this
099         * post-processor's {@link Advisor}.
100         * <p>Delegates to {@link #isEligible(Class)} for target class checking.
101         * Can be overridden e.g. to specifically exclude certain beans by name.
102         * <p>Note: Only called for regular bean instances but not for existing
103         * proxy instances which implement {@link Advised} and allow for adding
104         * the local {@link Advisor} to the existing proxy's {@link Advisor} chain.
105         * For the latter, {@link #isEligible(Class)} is being called directly,
106         * with the actual target class behind the existing proxy (as determined
107         * by {@link AopUtils#getTargetClass(Object)}).
108         * @param bean the bean instance
109         * @param beanName the name of the bean
110         * @see #isEligible(Class)
111         */
112        protected boolean isEligible(Object bean, String beanName) {
113                return isEligible(bean.getClass());
114        }
115
116        /**
117         * Check whether the given class is eligible for advising with this
118         * post-processor's {@link Advisor}.
119         * <p>Implements caching of {@code canApply} results per bean target class.
120         * @param targetClass the class to check against
121         * @see AopUtils#canApply(Advisor, Class)
122         */
123        protected boolean isEligible(Class<?> targetClass) {
124                Boolean eligible = this.eligibleBeans.get(targetClass);
125                if (eligible != null) {
126                        return eligible;
127                }
128                eligible = AopUtils.canApply(this.advisor, targetClass);
129                this.eligibleBeans.put(targetClass, eligible);
130                return eligible;
131        }
132
133        /**
134         * Prepare a {@link ProxyFactory} for the given bean.
135         * <p>Subclasses may customize the handling of the target instance and in
136         * particular the exposure of the target class. The default introspection
137         * of interfaces for non-target-class proxies and the configured advisor
138         * will be applied afterwards; {@link #customizeProxyFactory} allows for
139         * late customizations of those parts right before proxy creation.
140         * @param bean the bean instance to create a proxy for
141         * @param beanName the corresponding bean name
142         * @return the ProxyFactory, initialized with this processor's
143         * {@link ProxyConfig} settings and the specified bean
144         * @since 4.2.3
145         * @see #customizeProxyFactory
146         */
147        protected ProxyFactory prepareProxyFactory(Object bean, String beanName) {
148                ProxyFactory proxyFactory = new ProxyFactory();
149                proxyFactory.copyFrom(this);
150                proxyFactory.setTarget(bean);
151                return proxyFactory;
152        }
153
154        /**
155         * Subclasses may choose to implement this: for example,
156         * to change the interfaces exposed.
157         * <p>The default implementation is empty.
158         * @param proxyFactory the ProxyFactory that is already configured with
159         * target, advisor and interfaces and will be used to create the proxy
160         * immediately after this method returns
161         * @since 4.2.3
162         * @see #prepareProxyFactory
163         */
164        protected void customizeProxyFactory(ProxyFactory proxyFactory) {
165        }
166
167}