001/*
002 * Copyright 2002-2020 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.context.annotation;
018
019import java.util.Set;
020
021import org.apache.commons.logging.Log;
022import org.apache.commons.logging.LogFactory;
023
024import org.springframework.aop.config.AopConfigUtils;
025import org.springframework.beans.factory.support.BeanDefinitionRegistry;
026import org.springframework.core.annotation.AnnotationAttributes;
027import org.springframework.core.type.AnnotationMetadata;
028
029/**
030 * Registers an auto proxy creator against the current {@link BeanDefinitionRegistry}
031 * as appropriate based on an {@code @Enable*} annotation having {@code mode} and
032 * {@code proxyTargetClass} attributes set to the correct values.
033 *
034 * @author Chris Beams
035 * @since 3.1
036 * @see org.springframework.cache.annotation.EnableCaching
037 * @see org.springframework.transaction.annotation.EnableTransactionManagement
038 */
039public class AutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
040
041        private final Log logger = LogFactory.getLog(getClass());
042
043        /**
044         * Register, escalate, and configure the standard auto proxy creator (APC) against the
045         * given registry. Works by finding the nearest annotation declared on the importing
046         * {@code @Configuration} class that has both {@code mode} and {@code proxyTargetClass}
047         * attributes. If {@code mode} is set to {@code PROXY}, the APC is registered; if
048         * {@code proxyTargetClass} is set to {@code true}, then the APC is forced to use
049         * subclass (CGLIB) proxying.
050         * <p>Several {@code @Enable*} annotations expose both {@code mode} and
051         * {@code proxyTargetClass} attributes. It is important to note that most of these
052         * capabilities end up sharing a {@linkplain AopConfigUtils#AUTO_PROXY_CREATOR_BEAN_NAME
053         * single APC}. For this reason, this implementation doesn't "care" exactly which
054         * annotation it finds -- as long as it exposes the right {@code mode} and
055         * {@code proxyTargetClass} attributes, the APC can be registered and configured all
056         * the same.
057         */
058        @Override
059        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
060                boolean candidateFound = false;
061                Set<String> annTypes = importingClassMetadata.getAnnotationTypes();
062                for (String annType : annTypes) {
063                        AnnotationAttributes candidate = AnnotationConfigUtils.attributesFor(importingClassMetadata, annType);
064                        if (candidate == null) {
065                                continue;
066                        }
067                        Object mode = candidate.get("mode");
068                        Object proxyTargetClass = candidate.get("proxyTargetClass");
069                        if (mode != null && proxyTargetClass != null && AdviceMode.class == mode.getClass() &&
070                                        Boolean.class == proxyTargetClass.getClass()) {
071                                candidateFound = true;
072                                if (mode == AdviceMode.PROXY) {
073                                        AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
074                                        if ((Boolean) proxyTargetClass) {
075                                                AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
076                                                return;
077                                        }
078                                }
079                        }
080                }
081                if (!candidateFound && logger.isInfoEnabled()) {
082                        String name = getClass().getSimpleName();
083                        logger.info(String.format("%s was imported but no annotations were found " +
084                                        "having both 'mode' and 'proxyTargetClass' attributes of type " +
085                                        "AdviceMode and boolean respectively. This means that auto proxy " +
086                                        "creator registration and configuration may not have occurred as " +
087                                        "intended, and components may not be proxied as expected. Check to " +
088                                        "ensure that %s has been @Import'ed on the same class where these " +
089                                        "annotations are declared; otherwise remove the import of %s " +
090                                        "altogether.", name, name, name));
091                }
092        }
093
094}