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}