Spring中的Scope概念和原理解析

Scope的基本概念

Scope定义了 Bean 在容器中的生命周期和可见范围。Scope决定了:

  • 容器何时创建该 Bean?
  • 创建多少个实例?
  • 这些实例在什么上下文中共享?
  • 何时销毁?

Spring内置的标准Scope

Spring提供了6种标准Scope

1. singleton

默认的Scope。在Spring Ioc容器中,每个Bean定义只对应一个实例。平时使用的@Service、@Controller、@Component创建的Bean都是这个Scope。

2. prototype

一个Bean的定义可以有多个实例。每次从容器中获取Bean时都创建一个新实例。
使用方式:

@Component
@org.springframework.context.annotation.Scope("prototype")
public class RefreshScope implements Scope {
}

Spring 容器不会管理prototype实例的销毁工作。Bean的销毁和资源释放需要由客户端代码来完成。

3. request

仅在Web应用中有效;每个Http请求创建一个实例。
使用方式:

@RequestScope
@Component
public class LoginAction {
    // ...
}

4. session

每个HttpSession对应一个实例
使用方式:

@SessionScope
@Component
public class UserPreferences {
    // ...
}

5. application

一个web application对应一个实例。使用方式:

@ApplicationScope
@Component
public class AppPreferences {
   // ...
}

6. websocket

对应WebSocket Session范围。
使用方式:

@Component
@Scope(scopeName = "websocket", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class MyBean {

  @PostConstruct
  public void init() {
      // Invoked after dependencies injected
  }

  // ...

  @PreDestroy
  public void destroy() {
      // Invoked when the WebSocket session ends
  }
}

实现原理

1. Scope接口

public interface Scope {

	/**
	 * 获得bean,通过调用ObjectFactory#getObject()来创建bean
	 */
	Object get(String name, ObjectFactory<?> objectFactory);

	/**
	 * 移除一个bean
	 */
	@Nullable
	Object remove(String name);

	/**
	 * 注册一个bean的销毁函数
	 */
	void registerDestructionCallback(String name, Runnable callback);

	/**
	 * 解析给定键的上下文对象
	 */
	@Nullable
	Object resolveContextualObject(String key);

	/**
	 * 会话ID
     */
	@Nullable
	String getConversationId();

}

子类通过实现接口中的方法来实现不同的功能。例如RequestScope中的get方法为:

	@Override
	public Object get(String name, ObjectFactory<?> objectFactory) {
		RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();
		Object scopedObject = attributes.getAttribute(name, getScope());
		if (scopedObject == null) {
			scopedObject = objectFactory.getObject();
			attributes.setAttribute(name, scopedObject, getScope());
			// Retrieve object again, registering it for implicit session attribute updates.
			// As a bonus, we also allow for potential decoration at the getAttribute level.
			Object retrievedObject = attributes.getAttribute(name, getScope());
			if (retrievedObject != null) {
				// Only proceed with retrieved object if still present (the expected case).
				// If it disappeared concurrently, we return our locally created instance.
				scopedObject = retrievedObject;
			}
		}
		return scopedObject;
	}

RequestScope将Bean存储在一个线程变量中,以此来实现Bean在一个Http Request中传播,需要配合RequestContextListener或者RequestContextFilter或者DispatcherServlet来删除线程变量中保存的Bean。

2. Scope中的方法何时被调用?

Scope#get方法在调用BeanFactory#getBean方法时被调用,逻辑如下:

	protected <T> T doGetBean() {
		String beanName = transformedBeanName(name);
		Object beanInstance;
		//尝试从缓存中获取单例,如果bean是非单例模式,返回的是null
		Object sharedInstance = getSingleton(beanName);
		if (sharedInstance != null && args == null) {
			//代码省略,非singlton模式的bean不会走到这个分支
		}

		else {
			//如果出现了循环依赖则报错
			if (isPrototypeCurrentlyInCreation(beanName)) {
				throw new BeanCurrentlyInCreationException(beanName);
			}

			//尝试从父BeanFactory中获取该Bean
			BeanFactory parentBeanFactory = getParentBeanFactory();
			if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
				// Not found -> check parent.
				//代码省略
			}

			RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
			checkMergedBeanDefinition(mbd, beanName, args);

			//代码省略
			//开始创建Bean
			//单例模式
			if (mbd.isSingleton()) {
				//代码简化
				sharedInstance = getSingleton();
			}
			//原型模式
			else if (mbd.isPrototype()) {
				//代码简化
				createBean();
			}
			//使用Scope获取Bean
			else {
				String scopeName = mbd.getScope();
				if (!StringUtils.hasLength(scopeName)) {
					throw new IllegalStateException();
				}
				Scope scope = this.scopes.get(scopeName);
				if (scope == null) {
					throw new IllegalStateException();
				}
				//调用scope#get获取Bean,第二个参数是一个bean创建的lambda表达式
				Object scopedInstance = scope.get(beanName, () -> {
					beforePrototypeCreation(beanName);
					try {
						return createBean(beanName, mbd, args);
					}
					finally {
						afterPrototypeCreation(beanName);
					}
				});
				beanInstance = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
			}
		}
		return beanInstance;
	}

3. @Scope注解

与@Component搭配使用,标记一个类的scope名称以及proxyMode 代理模式。
proxyMode是一个枚举值,有4种取值:

	/**
	 * 默认类型,通常等同于NO
	 */
	DEFAULT,

	/**
	 * 不创建代理
	 */
	NO,

	/**
	 * 创建JDK动态代理,基于接口
	 */
	INTERFACES,

	/**
	 * 基于类创建代理,使用CGLIB
	 */
	TARGET_CLASS

这个注解在何时被读取呢?
在扫描bean时被读取,具体方法为ClassPathBeanDefinitionScanner#doScan。
a81cd8e44567478c9cef917c3f827aef
bean在扫描时,如果指定了Scope并且,proxyMode为INTERFACES或TARGET_CLASS时会创建代理类。创建的代理类为ScopedProxyFactoryBean,主要的代理逻辑是:每次方法执行前都从BeanFactory中获取bean,然后将方法绑定到真正的Bean上执行。

4. scope bean的代理

生成scope代理类的目的是为了使用的方便。比如在一个singleton的类中使用一个scope为request的类,在每次使用时你都需要从scope中获取该Bean,有了代理后将简化该过程。例子:

@Component
@RequestScope
public class MyService {

    public String getUser() {
        return "";
    }
}
@Component
public class AnotherService {

	@Autowired
 	private MyService myservice;//这里注入的是一个代理类,每次方法调用前从beanFactory中获取bean,并将方法绑定到真正的bean上,不用每次都从BeanFactory中获取
}

ScopedProxyFactoryBean源码分析:

ScopedProxyFactoryBean是一个FacotryBean,getObject方法实现为:

	@Override
	public Object getObject() {
		if (this.proxy == null) {
			throw new FactoryBeanNotInitializedException();
		}
		return this.proxy;
	}

返回的是一个proxy。
proxy的生成:

	@Override
	public void setBeanFactory(BeanFactory beanFactory) {
		if (!(beanFactory instanceof ConfigurableBeanFactory)) {
			throw new IllegalStateException("Not running in a ConfigurableBeanFactory: " + beanFactory);
		}
		ConfigurableBeanFactory cbf = (ConfigurableBeanFactory) beanFactory;

		this.scopedTargetSource.setBeanFactory(beanFactory);

		ProxyFactory pf = new ProxyFactory();
		pf.copyFrom(this);
		//最关键的是这一行
		pf.setTargetSource(this.scopedTargetSource);

		Assert.notNull(this.targetBeanName, "Property 'targetBeanName' is required");
		Class<?> beanType = beanFactory.getType(this.targetBeanName);
		if (beanType == null) {
			throw new IllegalStateException("Cannot create scoped proxy for bean '" + this.targetBeanName +
					"': Target type could not be determined at the time of proxy creation.");
		}
		if (!isProxyTargetClass() || beanType.isInterface() || Modifier.isPrivate(beanType.getModifiers())) {
			pf.setInterfaces(ClassUtils.getAllInterfacesForClass(beanType, cbf.getBeanClassLoader()));
		}

		// Add an introduction that implements only the methods on ScopedObject.
		ScopedObject scopedObject = new DefaultScopedObject(cbf, this.scopedTargetSource.getTargetBeanName());
		pf.addAdvice(new DelegatingIntroductionInterceptor(scopedObject));

		// Add the AopInfrastructureBean marker to indicate that the scoped proxy
		// itself is not subject to auto-proxying! Only its target bean is.
		pf.addInterface(AopInfrastructureBean.class);

		this.proxy = pf.getProxy(cbf.getBeanClassLoader());
	}

最关键的是这行代码:

 pf.setTargetSource(this.scopedTargetSource);

targetSource的类为:

public class SimpleBeanTargetSource extends AbstractBeanFactoryBasedTargetSource {

	@Override
	public Object getTarget() throws Exception {
		return getBeanFactory().getBean(getTargetBeanName());
	}

}

getTarget方法在每次进入代理方法时被调用,也即每次都从beanFacotory中获取bean,这个bean实际从scope子类中获取,之后方法的执行会绑定到新获取的bean上执行。

应用

Spring Cloud中的RefreshScope就是基于Scope实现的,每次配置更新后,清除缓存中的Bean,然后重新创建、组装bean,之后就能获取最新的配置项。

posted @ 2026-08-10 16:32  Hekk丶  阅读(8)  评论(0)    收藏  举报