001/*
002 * Copyright 2002-2017 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.config;
018
019import java.util.List;
020
021import org.w3c.dom.Node;
022
023import org.springframework.aop.framework.ProxyFactoryBean;
024import org.springframework.beans.factory.config.BeanDefinition;
025import org.springframework.beans.factory.config.BeanDefinitionHolder;
026import org.springframework.beans.factory.support.AbstractBeanDefinition;
027import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
028import org.springframework.beans.factory.support.BeanDefinitionRegistry;
029import org.springframework.beans.factory.support.ManagedList;
030import org.springframework.beans.factory.support.RootBeanDefinition;
031import org.springframework.beans.factory.xml.BeanDefinitionDecorator;
032import org.springframework.beans.factory.xml.ParserContext;
033import org.springframework.util.Assert;
034import org.springframework.util.ClassUtils;
035import org.springframework.util.StringUtils;
036
037/**
038 * Base implementation for
039 * {@link org.springframework.beans.factory.xml.BeanDefinitionDecorator BeanDefinitionDecorators}
040 * wishing to add an {@link org.aopalliance.intercept.MethodInterceptor interceptor}
041 * to the resulting bean.
042 *
043 * <p>This base class controls the creation of the {@link ProxyFactoryBean} bean definition
044 * and wraps the original as an inner-bean definition for the {@code target} property
045 * of {@link ProxyFactoryBean}.
046 *
047 * <p>Chaining is correctly handled, ensuring that only one {@link ProxyFactoryBean} definition
048 * is created. If a previous {@link org.springframework.beans.factory.xml.BeanDefinitionDecorator}
049 * already created the {@link org.springframework.aop.framework.ProxyFactoryBean} then the
050 * interceptor is simply added to the existing definition.
051 *
052 * <p>Subclasses have only to create the {@code BeanDefinition} to the interceptor that
053 * they wish to add.
054 *
055 * @author Rob Harrop
056 * @author Juergen Hoeller
057 * @since 2.0
058 * @see org.aopalliance.intercept.MethodInterceptor
059 */
060public abstract class AbstractInterceptorDrivenBeanDefinitionDecorator implements BeanDefinitionDecorator {
061
062        @Override
063        public final BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder definitionHolder, ParserContext parserContext) {
064                BeanDefinitionRegistry registry = parserContext.getRegistry();
065
066                // get the root bean name - will be the name of the generated proxy factory bean
067                String existingBeanName = definitionHolder.getBeanName();
068                BeanDefinition targetDefinition = definitionHolder.getBeanDefinition();
069                BeanDefinitionHolder targetHolder = new BeanDefinitionHolder(targetDefinition, existingBeanName + ".TARGET");
070
071                // delegate to subclass for interceptor definition
072                BeanDefinition interceptorDefinition = createInterceptorDefinition(node);
073
074                // generate name and register the interceptor
075                String interceptorName = existingBeanName + '.' + getInterceptorNameSuffix(interceptorDefinition);
076                BeanDefinitionReaderUtils.registerBeanDefinition(
077                                new BeanDefinitionHolder(interceptorDefinition, interceptorName), registry);
078
079                BeanDefinitionHolder result = definitionHolder;
080
081                if (!isProxyFactoryBeanDefinition(targetDefinition)) {
082                        // create the proxy definition
083                        RootBeanDefinition proxyDefinition = new RootBeanDefinition();
084                        // create proxy factory bean definition
085                        proxyDefinition.setBeanClass(ProxyFactoryBean.class);
086                        proxyDefinition.setScope(targetDefinition.getScope());
087                        proxyDefinition.setLazyInit(targetDefinition.isLazyInit());
088                        // set the target
089                        proxyDefinition.setDecoratedDefinition(targetHolder);
090                        proxyDefinition.getPropertyValues().add("target", targetHolder);
091                        // create the interceptor names list
092                        proxyDefinition.getPropertyValues().add("interceptorNames", new ManagedList<String>());
093                        // copy autowire settings from original bean definition.
094                        proxyDefinition.setAutowireCandidate(targetDefinition.isAutowireCandidate());
095                        proxyDefinition.setPrimary(targetDefinition.isPrimary());
096                        if (targetDefinition instanceof AbstractBeanDefinition) {
097                                proxyDefinition.copyQualifiersFrom((AbstractBeanDefinition) targetDefinition);
098                        }
099                        // wrap it in a BeanDefinitionHolder with bean name
100                        result = new BeanDefinitionHolder(proxyDefinition, existingBeanName);
101                }
102
103                addInterceptorNameToList(interceptorName, result.getBeanDefinition());
104                return result;
105        }
106
107        @SuppressWarnings("unchecked")
108        private void addInterceptorNameToList(String interceptorName, BeanDefinition beanDefinition) {
109                List<String> list = (List<String>) beanDefinition.getPropertyValues().get("interceptorNames");
110                Assert.state(list != null, "Missing 'interceptorNames' property");
111                list.add(interceptorName);
112        }
113
114        private boolean isProxyFactoryBeanDefinition(BeanDefinition existingDefinition) {
115                return ProxyFactoryBean.class.getName().equals(existingDefinition.getBeanClassName());
116        }
117
118        protected String getInterceptorNameSuffix(BeanDefinition interceptorDefinition) {
119                String beanClassName = interceptorDefinition.getBeanClassName();
120                return (StringUtils.hasLength(beanClassName) ?
121                                StringUtils.uncapitalize(ClassUtils.getShortName(beanClassName)) : "");
122        }
123
124        /**
125         * Subclasses should implement this method to return the {@code BeanDefinition}
126         * for the interceptor they wish to apply to the bean being decorated.
127         */
128        protected abstract BeanDefinition createInterceptorDefinition(Node node);
129
130}