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