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