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}