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.autoproxy;
018
019import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
020import org.springframework.beans.factory.config.BeanDefinition;
021import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
022import org.springframework.core.Conventions;
023import org.springframework.lang.Nullable;
024import org.springframework.util.StringUtils;
025
026/**
027 * Utilities for auto-proxy aware components.
028 * Mainly for internal use within the framework.
029 *
030 * @author Juergen Hoeller
031 * @since 2.0.3
032 * @see AbstractAutoProxyCreator
033 */
034public abstract class AutoProxyUtils {
035
036        /**
037         * Bean definition attribute that may indicate whether a given bean is supposed
038         * to be proxied with its target class (in case of it getting proxied in the first
039         * place). The value is {@code Boolean.TRUE} or {@code Boolean.FALSE}.
040         * <p>Proxy factories can set this attribute if they built a target class proxy
041         * for a specific bean, and want to enforce that bean can always be cast
042         * to its target class (even if AOP advices get applied through auto-proxying).
043         * @see #shouldProxyTargetClass
044         */
045        public static final String PRESERVE_TARGET_CLASS_ATTRIBUTE =
046                        Conventions.getQualifiedAttributeName(AutoProxyUtils.class, "preserveTargetClass");
047
048        /**
049         * Bean definition attribute that indicates the original target class of an
050         * auto-proxied bean, e.g. to be used for the introspection of annotations
051         * on the target class behind an interface-based proxy.
052         * @since 4.2.3
053         * @see #determineTargetClass
054         */
055        public static final String ORIGINAL_TARGET_CLASS_ATTRIBUTE =
056                        Conventions.getQualifiedAttributeName(AutoProxyUtils.class, "originalTargetClass");
057
058
059        /**
060         * Determine whether the given bean should be proxied with its target
061         * class rather than its interfaces. Checks the
062         * {@link #PRESERVE_TARGET_CLASS_ATTRIBUTE "preserveTargetClass" attribute}
063         * of the corresponding bean definition.
064         * @param beanFactory the containing ConfigurableListableBeanFactory
065         * @param beanName the name of the bean
066         * @return whether the given bean should be proxied with its target class
067         */
068        public static boolean shouldProxyTargetClass(
069                        ConfigurableListableBeanFactory beanFactory, @Nullable String beanName) {
070
071                if (beanName != null && beanFactory.containsBeanDefinition(beanName)) {
072                        BeanDefinition bd = beanFactory.getBeanDefinition(beanName);
073                        return Boolean.TRUE.equals(bd.getAttribute(PRESERVE_TARGET_CLASS_ATTRIBUTE));
074                }
075                return false;
076        }
077
078        /**
079         * Determine the original target class for the specified bean, if possible,
080         * otherwise falling back to a regular {@code getType} lookup.
081         * @param beanFactory the containing ConfigurableListableBeanFactory
082         * @param beanName the name of the bean
083         * @return the original target class as stored in the bean definition, if any
084         * @since 4.2.3
085         * @see org.springframework.beans.factory.BeanFactory#getType(String)
086         */
087        @Nullable
088        public static Class<?> determineTargetClass(
089                        ConfigurableListableBeanFactory beanFactory, @Nullable String beanName) {
090
091                if (beanName == null) {
092                        return null;
093                }
094                if (beanFactory.containsBeanDefinition(beanName)) {
095                        BeanDefinition bd = beanFactory.getMergedBeanDefinition(beanName);
096                        Class<?> targetClass = (Class<?>) bd.getAttribute(ORIGINAL_TARGET_CLASS_ATTRIBUTE);
097                        if (targetClass != null) {
098                                return targetClass;
099                        }
100                }
101                return beanFactory.getType(beanName);
102        }
103
104        /**
105         * Expose the given target class for the specified bean, if possible.
106         * @param beanFactory the containing ConfigurableListableBeanFactory
107         * @param beanName the name of the bean
108         * @param targetClass the corresponding target class
109         * @since 4.2.3
110         */
111        static void exposeTargetClass(
112                        ConfigurableListableBeanFactory beanFactory, @Nullable String beanName, Class<?> targetClass) {
113
114                if (beanName != null && beanFactory.containsBeanDefinition(beanName)) {
115                        beanFactory.getMergedBeanDefinition(beanName).setAttribute(ORIGINAL_TARGET_CLASS_ATTRIBUTE, targetClass);
116                }
117        }
118
119        /**
120         * Determine whether the given bean name indicates an "original instance"
121         * according to {@link AutowireCapableBeanFactory#ORIGINAL_INSTANCE_SUFFIX},
122         * skipping any proxy attempts for it.
123         * @param beanName the name of the bean
124         * @param beanClass the corresponding bean class
125         * @since 5.1
126         * @see AutowireCapableBeanFactory#ORIGINAL_INSTANCE_SUFFIX
127         */
128        static boolean isOriginalInstance(String beanName, Class<?> beanClass) {
129                if (!StringUtils.hasLength(beanName) || beanName.length() !=
130                                beanClass.getName().length() + AutowireCapableBeanFactory.ORIGINAL_INSTANCE_SUFFIX.length()) {
131                        return false;
132                }
133                return (beanName.startsWith(beanClass.getName()) &&
134                                beanName.endsWith(AutowireCapableBeanFactory.ORIGINAL_INSTANCE_SUFFIX));
135        }
136
137}