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.io.Closeable;
020
021import org.springframework.beans.factory.Aware;
022import org.springframework.beans.factory.BeanClassLoaderAware;
023import org.springframework.beans.factory.DisposableBean;
024import org.springframework.beans.factory.InitializingBean;
025import org.springframework.core.Ordered;
026import org.springframework.lang.Nullable;
027import org.springframework.util.ClassUtils;
028import org.springframework.util.ObjectUtils;
029
030/**
031 * Base class with common functionality for proxy processors, in particular
032 * ClassLoader management and the {@link #evaluateProxyInterfaces} algorithm.
033 *
034 * @author Juergen Hoeller
035 * @since 4.1
036 * @see AbstractAdvisingBeanPostProcessor
037 * @see org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator
038 */
039@SuppressWarnings("serial")
040public class ProxyProcessorSupport extends ProxyConfig implements Ordered, BeanClassLoaderAware, AopInfrastructureBean {
041
042        /**
043         * This should run after all other processors, so that it can just add
044         * an advisor to existing proxies rather than double-proxy.
045         */
046        private int order = Ordered.LOWEST_PRECEDENCE;
047
048        @Nullable
049        private ClassLoader proxyClassLoader = ClassUtils.getDefaultClassLoader();
050
051        private boolean classLoaderConfigured = false;
052
053
054        /**
055         * Set the ordering which will apply to this processor's implementation
056         * of {@link Ordered}, used when applying multiple processors.
057         * <p>The default value is {@code Ordered.LOWEST_PRECEDENCE}, meaning non-ordered.
058         * @param order the ordering value
059         */
060        public void setOrder(int order) {
061                this.order = order;
062        }
063
064        @Override
065        public int getOrder() {
066                return this.order;
067        }
068
069        /**
070         * Set the ClassLoader to generate the proxy class in.
071         * <p>Default is the bean ClassLoader, i.e. the ClassLoader used by the containing
072         * {@link org.springframework.beans.factory.BeanFactory} for loading all bean classes.
073         * This can be overridden here for specific proxies.
074         */
075        public void setProxyClassLoader(@Nullable ClassLoader classLoader) {
076                this.proxyClassLoader = classLoader;
077                this.classLoaderConfigured = (classLoader != null);
078        }
079
080        /**
081         * Return the configured proxy ClassLoader for this processor.
082         */
083        @Nullable
084        protected ClassLoader getProxyClassLoader() {
085                return this.proxyClassLoader;
086        }
087
088        @Override
089        public void setBeanClassLoader(ClassLoader classLoader) {
090                if (!this.classLoaderConfigured) {
091                        this.proxyClassLoader = classLoader;
092                }
093        }
094
095
096        /**
097         * Check the interfaces on the given bean class and apply them to the {@link ProxyFactory},
098         * if appropriate.
099         * <p>Calls {@link #isConfigurationCallbackInterface} and {@link #isInternalLanguageInterface}
100         * to filter for reasonable proxy interfaces, falling back to a target-class proxy otherwise.
101         * @param beanClass the class of the bean
102         * @param proxyFactory the ProxyFactory for the bean
103         */
104        protected void evaluateProxyInterfaces(Class<?> beanClass, ProxyFactory proxyFactory) {
105                Class<?>[] targetInterfaces = ClassUtils.getAllInterfacesForClass(beanClass, getProxyClassLoader());
106                boolean hasReasonableProxyInterface = false;
107                for (Class<?> ifc : targetInterfaces) {
108                        if (!isConfigurationCallbackInterface(ifc) && !isInternalLanguageInterface(ifc) &&
109                                        ifc.getMethods().length > 0) {
110                                hasReasonableProxyInterface = true;
111                                break;
112                        }
113                }
114                if (hasReasonableProxyInterface) {
115                        // Must allow for introductions; can't just set interfaces to the target's interfaces only.
116                        for (Class<?> ifc : targetInterfaces) {
117                                proxyFactory.addInterface(ifc);
118                        }
119                }
120                else {
121                        proxyFactory.setProxyTargetClass(true);
122                }
123        }
124
125        /**
126         * Determine whether the given interface is just a container callback and
127         * therefore not to be considered as a reasonable proxy interface.
128         * <p>If no reasonable proxy interface is found for a given bean, it will get
129         * proxied with its full target class, assuming that as the user's intention.
130         * @param ifc the interface to check
131         * @return whether the given interface is just a container callback
132         */
133        protected boolean isConfigurationCallbackInterface(Class<?> ifc) {
134                return (InitializingBean.class == ifc || DisposableBean.class == ifc || Closeable.class == ifc ||
135                                AutoCloseable.class == ifc || ObjectUtils.containsElement(ifc.getInterfaces(), Aware.class));
136        }
137
138        /**
139         * Determine whether the given interface is a well-known internal language interface
140         * and therefore not to be considered as a reasonable proxy interface.
141         * <p>If no reasonable proxy interface is found for a given bean, it will get
142         * proxied with its full target class, assuming that as the user's intention.
143         * @param ifc the interface to check
144         * @return whether the given interface is an internal language interface
145         */
146        protected boolean isInternalLanguageInterface(Class<?> ifc) {
147                return (ifc.getName().equals("groovy.lang.GroovyObject") ||
148                                ifc.getName().endsWith(".cglib.proxy.Factory") ||
149                                ifc.getName().endsWith(".bytebuddy.MockAccess"));
150        }
151
152}