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