/* * Copyright 2002-2008 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.aop.framework.autoproxy; import java.beans.PropertyDescriptor; import java.lang.reflect.Constructor; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.aopalliance.aop.Advice; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.aop.Advisor; import org.springframework.aop.TargetSource; import org.springframework.aop.framework.AopInfrastructureBean; import org.springframework.aop.framework.ProxyConfig; import org.springframework.aop.framework.ProxyFactory; import org.springframework.aop.framework.adapter.AdvisorAdapterRegistry; import org.springframework.aop.framework.adapter.GlobalAdvisorAdapterRegistry; import org.springframework.aop.target.SingletonTargetSource; import org.springframework.beans.BeansException; import org.springframework.beans.PropertyValues; import org.springframework.beans.factory.BeanClassLoaderAware; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor; import org.springframework.core.CollectionFactory; import org.springframework.core.Ordered; import org.springframework.util.ClassUtils; /** * {@link org.springframework.beans.factory.config.BeanPostProcessor} implementation * that wraps each eligible bean with an AOP proxy, delegating to specified interceptors * before invoking the bean itself. * * <p>This class distinguishes between "common" interceptors: shared for all proxies it * creates, and "specific" interceptors: unique per bean instance. There need not * be any common interceptors. If there are, they are set using the interceptorNames * property. As with ProxyFactoryBean, interceptors names in the current factory * are used rather than bean references to allow correct handling of prototype * advisors and interceptors: for example, to support stateful mixins. * Any advice type is supported for "interceptorNames" entries. * * <p>Such auto-proxying is particularly useful if there's a large number of beans that * need to be wrapped with similar proxies, i.e. delegating to the same interceptors. * Instead of x repetitive proxy definitions for x target beans, you can register * one single such post processor with the bean factory to achieve the same effect. * * <p>Subclasses can apply any strategy to decide if a bean is to be proxied, * e.g. by type, by name, by definition details, etc. They can also return * additional interceptors that should just be applied to the specific bean * instance. The default concrete implementation is BeanNameAutoProxyCreator, * identifying the beans to be proxied via a list of bean names. * * <p>Any number of {@link TargetSourceCreator} implementations can be used to create * a custom target source - for example, to pool prototype objects. Auto-proxying will * occur even if there is no advice, as long as a TargetSourceCreator specifies a custom * {@link org.springframework.aop.TargetSource}. If there are no TargetSourceCreators set, * or if none matches, a {@link org.springframework.aop.target.SingletonTargetSource} * will be used by default to wrap the target bean instance. * * @author Juergen Hoeller * @author Rod Johnson * @author Rob Harrop * @since 13.10.2003 * @see #setInterceptorNames * @see #getAdvicesAndAdvisorsForBean * @see BeanNameAutoProxyCreator * @see DefaultAdvisorAutoProxyCreator */ public abstract class AbstractAutoProxyCreator extends ProxyConfig implements SmartInstantiationAwareBeanPostProcessor, BeanClassLoaderAware, BeanFactoryAware, Ordered, AopInfrastructureBean { /** * Convenience constant for subclasses: Return value for "do not proxy". * @see #getAdvicesAndAdvisorsForBean */ protected static final Object[] DO_NOT_PROXY = null; /** * Convenience constant for subclasses: Return value for * "proxy without additional interceptors, just the common ones". * @see #getAdvicesAndAdvisorsForBean */ protected static final Object[] PROXY_WITHOUT_ADDITIONAL_INTERCEPTORS = new Object[0]; /** Logger available to subclasses */ protected final Log logger = LogFactory.getLog(getClass()); /** Default value is same as non-ordered */ private int order = Integer.MAX_VALUE; /** Default is global AdvisorAdapterRegistry */ private AdvisorAdapterRegistry advisorAdapterRegistry = GlobalAdvisorAdapterRegistry.getInstance(); /** * Indicates whether or not the proxy should be frozen. Overridden from super * to prevent the configuration from becoming frozen too early. */ private boolean freezeProxy = false; /** Default is no common interceptors */ private String[] interceptorNames = new String[0]; private boolean applyCommonInterceptorsFirst = true; private TargetSourceCreator[] customTargetSourceCreators; private ClassLoader proxyClassLoader = ClassUtils.getDefaultClassLoader(); private boolean classLoaderConfigured = false; private BeanFactory beanFactory; /** * Set of bean name Strings, referring to all beans that this auto-proxy creator * created a custom TargetSource for. Used to detect own pre-built proxies (from * "postProcessBeforeInstantiation") in the "postProcessAfterInitialization" method. */ private final Set targetSourcedBeans = Collections.synchronizedSet(new HashSet()); private final Set earlyProxyReferences = Collections.synchronizedSet(new HashSet()); private final Set advisedBeans = Collections.synchronizedSet(new HashSet()); private final Set nonAdvisedBeans = Collections.synchronizedSet(new HashSet()); private final Map proxyTypes = CollectionFactory.createConcurrentMapIfPossible(16); /** * Set the ordering which will apply to this class's implementation * of Ordered, used when applying multiple BeanPostProcessors. * <p>Default value is <code>Integer.MAX_VALUE</code>, meaning that it's non-ordered. * @param order ordering value */ public final void setOrder(int order) { this.order = order; } public final int getOrder() { return this.order; } /** * Set whether or not the proxy should be frozen, preventing advice * from being added to it once it is created. * <p>Overridden from the super class to prevent the proxy configuration * from being frozen before the proxy is created. */ public void setFrozen(boolean frozen) { this.freezeProxy = frozen; } public boolean isFrozen() { return this.freezeProxy; } /** * Specify the AdvisorAdapterRegistry to use. * Default is the global AdvisorAdapterRegistry. * @see org.springframework.aop.framework.adapter.GlobalAdvisorAdapterRegistry */ public void setAdvisorAdapterRegistry(AdvisorAdapterRegistry advisorAdapterRegistry) { this.advisorAdapterRegistry = advisorAdapterRegistry; } /** * Set custom TargetSourceCreators to be applied in this order. * If the list is empty, or they all return null, a SingletonTargetSource * will be created for each bean. * <p>Note that TargetSourceCreators will kick in even for target beans * where no advices or advisors have been found. If a TargetSourceCreator * returns a TargetSource for a specific bean, that bean will be proxied * in any case. * <p>TargetSourceCreators can only be invoked if this post processor is used * in a BeanFactory, and its BeanFactoryAware callback is used. * @param targetSourceCreators list of TargetSourceCreator. * Ordering is significant: The TargetSource returned from the first matching * TargetSourceCreator (that is, the first that returns non-null) will be used. */ public void setCustomTargetSourceCreators(TargetSourceCreator[] targetSourceCreators) { this.customTargetSourceCreators = targetSourceCreators; } /** * Set the common interceptors. These must be bean names in the current factory. * They can be of any advice or advisor type Spring supports. * <p>If this property isn't set, there will be zero common interceptors. * This is perfectly valid, if "specific" interceptors such as matching * Advisors are all we want. */ public void setInterceptorNames(String[] interceptorNames) { this.interceptorNames = interceptorNames; } /** * Set whether the common interceptors should be applied before bean-specific ones. * Default is "true"; else, bean-specific interceptors will get applied first. */ public void setApplyCommonInterceptorsFirst(boolean applyCommonInterceptorsFirst) { this.applyCommonInterceptorsFirst = applyCommonInterceptorsFirst; } /** * Set the ClassLoader to generate the proxy class in. * <p>Default is the bean ClassLoader, i.e. the ClassLoader used by the * containing BeanFactory for loading all bean classes. This can be * overridden here for specific proxies. */ public void setProxyClassLoader(ClassLoader classLoader) { this.proxyClassLoader = classLoader; this.classLoaderConfigured = (classLoader != null); } public void setBeanClassLoader(ClassLoader classLoader) { if (!this.classLoaderConfigured) { this.proxyClassLoader = classLoader; } } public void setBeanFactory(BeanFactory beanFactory) { this.beanFactory = beanFactory; } /** * Return the owning BeanFactory. * May be <code>null</code>, as this object doesn't need to belong to a bean factory. */ protected BeanFactory getBeanFactory() { return this.beanFactory; } public Class predictBeanType(Class beanClass, String beanName) { Object cacheKey = getCacheKey(beanClass, beanName); return (Class) this.proxyTypes.get(cacheKey); } public Constructor[] determineCandidateConstructors(Class beanClass, String beanName) throws BeansException { return null; } public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException { Object cacheKey = getCacheKey(bean.getClass(), beanName); this.earlyProxyReferences.add(cacheKey); return wrapIfNecessary(bean, beanName, cacheKey); } public Object postProcessBeforeInstantiation(Class beanClass, String beanName) throws BeansException { Object cacheKey = getCacheKey(beanClass, beanName); if (!this.targetSourcedBeans.contains(cacheKey)) { if (this.advisedBeans.contains(cacheKey) || this.nonAdvisedBeans.contains(cacheKey)) { return null; } if (isInfrastructureClass(beanClass, beanName) || shouldSkip(beanClass, beanName)) { this.nonAdvisedBeans.add(cacheKey); return null; } } // Create proxy here if we have a custom TargetSource. // Suppresses unnecessary default instantiation of the target bean: // The TargetSource will handle target instances in a custom fashion. TargetSource targetSource = getCustomTargetSource(beanClass, beanName); if (targetSource != null) { this.targetSourcedBeans.add(beanName); Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource); Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource); this.proxyTypes.put(cacheKey, proxy.getClass()); return proxy; } return null; } public boolean postProcessAfterInstantiation(Object bean, String beanName) { return true; } public PropertyValues postProcessPropertyValues( PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) { return pvs; } public Object postProcessBeforeInitialization(Object bean, String beanName) { return bean; } /** * Create a proxy with the configured interceptors if the bean is * identified as one to proxy by the subclass. * @see #getAdvicesAndAdvisorsForBean */ public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean != null) { Object cacheKey = getCacheKey(bean.getClass(), beanName); if (!this.earlyProxyReferences.contains(cacheKey)) { return wrapIfNecessary(bean, beanName, cacheKey); } } return bean; } /** * Build a cache key for the given bean class and bean name. * @param beanClass the bean class * @param beanName the bean name * @return the cache key for the given class and name */ protected Object getCacheKey(Class beanClass, String beanName) { return beanClass.getName() + "_" + beanName; } /** * Wrap the given bean if necessary, i.e. if it is eligible for being proxied. * @param bean the raw bean instance * @param beanName the name of the bean * @param cacheKey the cache key for metadata access * @return a proxy wrapping the bean, or the raw bean instance as-is */ protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { if (this.targetSourcedBeans.contains(beanName)) { return bean; } if (this.nonAdvisedBeans.contains(cacheKey)) { return bean; } if (isInfrastructureClass(bean.getClass(), beanName) || shouldSkip(bean.getClass(), beanName)) { this.nonAdvisedBeans.add(cacheKey); return bean; } // Create proxy if we have advice. Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null); if (specificInterceptors != DO_NOT_PROXY) { this.advisedBeans.add(cacheKey); Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean)); this.proxyTypes.put(cacheKey, proxy.getClass()); return proxy; } this.nonAdvisedBeans.add(cacheKey); return bean; } /** * Return whether the given bean class and bean name represents an * infrastructure class that should never be proxied. * @deprecated in favor of <code>isInfrastructureClass(beanClass)</code> * @see #isInfrastructureClass(Class) */ protected boolean isInfrastructureClass(Class beanClass, String beanName) { return isInfrastructureClass(beanClass); } /** * Return whether the given bean class represents an infrastructure class * that should never be proxied. * <p>Default implementation considers Advisors, Advices and * AbstractAutoProxyCreators as infrastructure classes. * @param beanClass the class of the bean * @return whether the bean represents an infrastructure class * @see org.springframework.aop.Advisor * @see org.aopalliance.intercept.MethodInterceptor * @see #shouldSkip */ protected boolean isInfrastructureClass(Class beanClass) { boolean retVal = Advisor.class.isAssignableFrom(beanClass) || Advice.class.isAssignableFrom(beanClass) || AopInfrastructureBean.class.isAssignableFrom(beanClass); if (retVal && logger.isTraceEnabled()) { logger.trace("Did not attempt to auto-proxy infrastructure class [" + beanClass.getName() + "]"); } return retVal; } /** * Subclasses should override this method to return <code>true</code> if the * given bean should not be considered for auto-proxying by this post-processor. * <p>Sometimes we need to be able to avoid this happening if it will lead to * a circular reference. This implementation returns <code>false</code>. * @param beanClass the class of the bean * @param beanName the name of the bean * @return whether to skip the given bean */ protected boolean shouldSkip(Class beanClass, String beanName) { return false; } /** * Create a target source for bean instances. Uses any TargetSourceCreators if set. * Returns <code>null</code> if no custom TargetSource should be used. * <p>This implementation uses the "customTargetSourceCreators" property. * Subclasses can override this method to use a different mechanism. * @param beanClass the class of the bean to create a TargetSource for * @param beanName the name of the bean * @return a TargetSource for this bean * @see #setCustomTargetSourceCreators */ protected TargetSource getCustomTargetSource(Class beanClass, String beanName) { // We can't create fancy target sources for directly registered singletons. if (this.customTargetSourceCreators != null && this.beanFactory != null && this.beanFactory.containsBean(beanName)) { for (int i = 0; i < this.customTargetSourceCreators.length; i++) { TargetSourceCreator tsc = this.customTargetSourceCreators[i]; TargetSource ts = tsc.getTargetSource(beanClass, beanName); if (ts != null) { // Found a matching TargetSource. if (logger.isDebugEnabled()) { logger.debug("TargetSourceCreator [" + tsc + " found custom TargetSource for bean with name '" + beanName + "'"); } return ts; } } } // No custom TargetSource found. return null; } /** * Create an AOP proxy for the given bean. * @param beanClass the class of the bean * @param beanName the name of the bean * @param specificInterceptors the set of interceptors that is * specific to this bean (may be empty, but not null) * @param targetSource the TargetSource for the proxy, * already pre-configured to access the bean * @return the AOP proxy for the bean * @see #buildAdvisors */ protected Object createProxy( Class beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) { ProxyFactory proxyFactory = new ProxyFactory(); // Copy our properties (proxyTargetClass etc) inherited from ProxyConfig. proxyFactory.copyFrom(this); if (!shouldProxyTargetClass(beanClass, beanName)) { // Must allow for introductions; can't just set interfaces to // the target's interfaces only. Class[] targetInterfaces = ClassUtils.getAllInterfacesForClass(beanClass, this.proxyClassLoader); for (int i = 0; i < targetInterfaces.length; i++) { proxyFactory.addInterface(targetInterfaces[i]); } } Advisor[] advisors = buildAdvisors(beanName, specificInterceptors); for (int i = 0; i < advisors.length; i++) { proxyFactory.addAdvisor(advisors[i]); } proxyFactory.setTargetSource(targetSource); customizeProxyFactory(proxyFactory); proxyFactory.setFrozen(this.freezeProxy); if (advisorsPreFiltered()) { proxyFactory.setPreFiltered(true); } return proxyFactory.getProxy(this.proxyClassLoader); } /** * Determine whether the given bean should be proxied with its target * class rather than its interfaces. Checks the * {@link #setProxyTargetClass "proxyTargetClass" setting} as well as the * {@link AutoProxyUtils#PRESERVE_TARGET_CLASS_ATTRIBUTE "preserveTargetClass" attribute} * of the corresponding bean definition. * @param beanClass the class of the bean * @param beanName the name of the bean * @return whether the given bean should be proxied with its target class * @see AutoProxyUtils#shouldProxyTargetClass */ protected boolean shouldProxyTargetClass(Class beanClass, String beanName) { return (isProxyTargetClass() || (this.beanFactory instanceof ConfigurableListableBeanFactory && AutoProxyUtils.shouldProxyTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName))); } /** * Return whether the Advisors returned by the subclass are pre-filtered * to match the bean's target class already, allowing the ClassFilter check * to be skipped when building advisors chains for AOP invocations. * <p>Default is <code>false</code>. Subclasses may override this if they * will always return pre-filtered Advisors. * @return whether the Advisors are pre-filtered * @see #getAdvicesAndAdvisorsForBean * @see org.springframework.aop.framework.Advised#setPreFiltered */ protected boolean advisorsPreFiltered() { return false; } /** * Determine the advisors for the given bean, including the specific interceptors * as well as the common interceptor, all adapted to the Advisor interface. * @param beanName the name of the bean * @param specificInterceptors the set of interceptors that is * specific to this bean (may be empty, but not null) * @return the list of Advisors for the given bean */ protected Advisor[] buildAdvisors(String beanName, Object[] specificInterceptors) { // Handle prototypes correctly... Advisor[] commonInterceptors = resolveInterceptorNames(); List allInterceptors = new ArrayList(); if (specificInterceptors != null) { allInterceptors.addAll(Arrays.asList(specificInterceptors)); if (commonInterceptors != null) { if (this.applyCommonInterceptorsFirst) { allInterceptors.addAll(0, Arrays.asList(commonInterceptors)); } else { allInterceptors.addAll(Arrays.asList(commonInterceptors)); } } } if (logger.isDebugEnabled()) { int nrOfCommonInterceptors = (commonInterceptors != null ? commonInterceptors.length : 0); int nrOfSpecificInterceptors = (specificInterceptors != null ? specificInterceptors.length : 0); logger.debug("Creating implicit proxy for bean '" + beanName + "' with " + nrOfCommonInterceptors + " common interceptors and " + nrOfSpecificInterceptors + " specific interceptors"); } Advisor[] advisors = new Advisor[allInterceptors.size()]; for (int i = 0; i < allInterceptors.size(); i++) { advisors[i] = this.advisorAdapterRegistry.wrap(allInterceptors.get(i)); } return advisors; } /** * Resolves the specified interceptor names to Advisor objects. * @see #setInterceptorNames */ private Advisor[] resolveInterceptorNames() { ConfigurableBeanFactory cbf = (this.beanFactory instanceof ConfigurableBeanFactory ? (ConfigurableBeanFactory) this.beanFactory : null); List advisors = new ArrayList(); for (int i = 0; i < this.interceptorNames.length; i++) { String beanName = this.interceptorNames[i]; if (cbf == null || !cbf.isCurrentlyInCreation(beanName)) { Object next = this.beanFactory.getBean(beanName); advisors.add(this.advisorAdapterRegistry.wrap(next)); } } return (Advisor[]) advisors.toArray(new Advisor[advisors.size()]); } /** * Subclasses may choose to implement this: for example, * to change the interfaces exposed. * <p>The default implementation is empty. * @param proxyFactory ProxyFactory that is already configured with * TargetSource and interfaces and will be used to create the proxy * immediably after this method returns */ protected void customizeProxyFactory(ProxyFactory proxyFactory) { } /** * Return whether the given bean is to be proxied, what additional * advices (e.g. AOP Alliance interceptors) and advisors to apply. * @param beanClass the class of the bean to advise * @param beanName the name of the bean * @param customTargetSource the TargetSource returned by the * {@link #getCustomTargetSource} method: may be ignored. * Will be <code>null</code> if no custom target source is in use. * @return an array of additional interceptors for the particular bean; * or an empty array if no additional interceptors but just the common ones; * or <code>null</code> if no proxy at all, not even with the common interceptors. * See constants DO_NOT_PROXY and PROXY_WITHOUT_ADDITIONAL_INTERCEPTORS. * @throws BeansException in case of errors * @see #DO_NOT_PROXY * @see #PROXY_WITHOUT_ADDITIONAL_INTERCEPTORS */ protected abstract Object[] getAdvicesAndAdvisorsForBean( Class beanClass, String beanName, TargetSource customTargetSource) throws BeansException; }