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.scope;
018
019import java.lang.reflect.Modifier;
020
021import org.springframework.aop.framework.AopInfrastructureBean;
022import org.springframework.aop.framework.ProxyConfig;
023import org.springframework.aop.framework.ProxyFactory;
024import org.springframework.aop.support.DelegatingIntroductionInterceptor;
025import org.springframework.aop.target.SimpleBeanTargetSource;
026import org.springframework.beans.factory.BeanFactory;
027import org.springframework.beans.factory.BeanFactoryAware;
028import org.springframework.beans.factory.FactoryBean;
029import org.springframework.beans.factory.FactoryBeanNotInitializedException;
030import org.springframework.beans.factory.config.ConfigurableBeanFactory;
031import org.springframework.util.ClassUtils;
032
033/**
034 * Convenient proxy factory bean for scoped objects.
035 *
036 * <p>Proxies created using this factory bean are thread-safe singletons
037 * and may be injected into shared objects, with transparent scoping behavior.
038 *
039 * <p>Proxies returned by this class implement the {@link ScopedObject} interface.
040 * This presently allows for removing the corresponding object from the scope,
041 * seamlessly creating a new instance in the scope on next access.
042 *
043 * <p>Please note that the proxies created by this factory are
044 * <i>class-based</i> proxies by default. This can be customized
045 * through switching the "proxyTargetClass" property to "false".
046 *
047 * @author Rod Johnson
048 * @author Juergen Hoeller
049 * @since 2.0
050 * @see #setProxyTargetClass
051 */
052@SuppressWarnings("serial")
053public class ScopedProxyFactoryBean extends ProxyConfig
054                implements FactoryBean<Object>, BeanFactoryAware, AopInfrastructureBean {
055
056        /** The TargetSource that manages scoping */
057        private final SimpleBeanTargetSource scopedTargetSource = new SimpleBeanTargetSource();
058
059        /** The name of the target bean */
060        private String targetBeanName;
061
062        /** The cached singleton proxy */
063        private Object proxy;
064
065
066        /**
067         * Create a new ScopedProxyFactoryBean instance.
068         */
069        public ScopedProxyFactoryBean() {
070                setProxyTargetClass(true);
071        }
072
073
074        /**
075         * Set the name of the bean that is to be scoped.
076         */
077        public void setTargetBeanName(String targetBeanName) {
078                this.targetBeanName = targetBeanName;
079                this.scopedTargetSource.setTargetBeanName(targetBeanName);
080        }
081
082        @Override
083        public void setBeanFactory(BeanFactory beanFactory) {
084                if (!(beanFactory instanceof ConfigurableBeanFactory)) {
085                        throw new IllegalStateException("Not running in a ConfigurableBeanFactory: " + beanFactory);
086                }
087                ConfigurableBeanFactory cbf = (ConfigurableBeanFactory) beanFactory;
088
089                this.scopedTargetSource.setBeanFactory(beanFactory);
090
091                ProxyFactory pf = new ProxyFactory();
092                pf.copyFrom(this);
093                pf.setTargetSource(this.scopedTargetSource);
094
095                Class<?> beanType = beanFactory.getType(this.targetBeanName);
096                if (beanType == null) {
097                        throw new IllegalStateException("Cannot create scoped proxy for bean '" + this.targetBeanName +
098                                        "': Target type could not be determined at the time of proxy creation.");
099                }
100                if (!isProxyTargetClass() || beanType.isInterface() || Modifier.isPrivate(beanType.getModifiers())) {
101                        pf.setInterfaces(ClassUtils.getAllInterfacesForClass(beanType, cbf.getBeanClassLoader()));
102                }
103
104                // Add an introduction that implements only the methods on ScopedObject.
105                ScopedObject scopedObject = new DefaultScopedObject(cbf, this.scopedTargetSource.getTargetBeanName());
106                pf.addAdvice(new DelegatingIntroductionInterceptor(scopedObject));
107
108                // Add the AopInfrastructureBean marker to indicate that the scoped proxy
109                // itself is not subject to auto-proxying! Only its target bean is.
110                pf.addInterface(AopInfrastructureBean.class);
111
112                this.proxy = pf.getProxy(cbf.getBeanClassLoader());
113        }
114
115
116        @Override
117        public Object getObject() {
118                if (this.proxy == null) {
119                        throw new FactoryBeanNotInitializedException();
120                }
121                return this.proxy;
122        }
123
124        @Override
125        public Class<?> getObjectType() {
126                if (this.proxy != null) {
127                        return this.proxy.getClass();
128                }
129                return this.scopedTargetSource.getTargetClass();
130        }
131
132        @Override
133        public boolean isSingleton() {
134                return true;
135        }
136
137}