Spring注解@Scope---SessionScope和RequestScope
前言
前两天分析了一波@ComponentScan注解各属性作用,通过源码看到根据类上的@Scope注解为BeanDefinition设置了ScopedProxyMode属性,并对BeanDefinition进行代理操作。大概就是如果这个BeanDefinition有@Scope,就会new一个BeanDefinition将原先的BeanDefition替换,具体看上篇文吧。注意新创建的BeanDefinition的beanClass是ScopedProxyFactoryBean,是一个FactoryBean相关代码如下
// 创建一个新的BeanDefition来作为代理的beanDefition,把原先的对象作为成员设置进去RootBeanDefinition proxyDefinition = new RootBeanDefinition(ScopedProxyFactoryBean.class);proxyDefinition.setDecoratedDefinition(new BeanDefinitionHolder(targetDefinition, targetBeanName));proxyDefinition.setOriginatingBeanDefinition(targetDefinition);
Scope常见的在Web应用中有session、request、application,还有cloud里边的@RefreshScope等。
一个一个来吧,以后碰到哪个分析完再接到这篇上。
@SessionScope和@RequestScope
每个Session或每个Request创建一个对象
先来看获取Bean对象的代码
org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
// 不相关代码都删了if (mbd.isSingleton()) {// 获取单例bean实例}else if (mbd.isPrototype()) {// 获取原型模式实例}else {// 获取BeanDefinition的scope,即session\requestString scopeName = mbd.getScope();// 这个集合中保存了这个字符串“session”对应的哪个Scope对象,用这个Scope对象来进行创建操作Scope scope = this.scopes.get(scopeName);if (scope == null) {throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");}try {// 获取被代理过的BeanDefition对应的FactoryBean对象// 先进去看看对应Scope对象的get操作是什么// createBean方法不用看了,就是根据bean定义创建实例,以前看过,这里不是目标就略过,知道这当作一个参数传进scope.get就可以了Object scopedInstance = scope.get(beanName, () -> {beforePrototypeCreation(beanName);try {return createBean(beanName, mbd, args);}finally {afterPrototypeCreation(beanName);}});// FactoryBean.getObject来获取真正的bean实例bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);}catch (IllegalStateException ex) {// 报异常}
beanFactory中的this.scopes集合怎么来的呢
在AbstractApplicationContext的refresh方法的postProcessBeanFactory()这一步添加的,AbstractApplicationContext这里只是模板方法,具体实现在Web容器的实现类AnnotationConfigServletWebServerApplicationContext#postProcessBeanFactory
@Overrideprotected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {super.postProcessBeanFactory(beanFactory);if (this.basePackages != null && this.basePackages.length > 0) {this.scanner.scan(this.basePackages);}if (!this.annotatedClasses.isEmpty()) {this.reader.register(ClassUtils.toClassArray(this.annotatedClasses));}}
父类方法org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#postProcessBeanFactory
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {beanFactory.addBeanPostProcessor(new WebApplicationContextServletContextAwareProcessor(this));beanFactory.ignoreDependencyInterface(ServletContextAware.class);registerWebApplicationScopes();}private void registerWebApplicationScopes() {ExistingWebApplicationScopes existingScopes = new ExistingWebApplicationScopes(getBeanFactory());WebApplicationContextUtils.registerWebApplicationScopes(getBeanFactory());existingScopes.restore();}// 直接跟到WebApplicationContextUtils.registerWebApplicationScopes(getBeanFactory());public static void registerWebApplicationScopes(ConfigurableListableBeanFactory beanFactory,@Nullable ServletContext sc) {beanFactory.registerScope(WebApplicationContext.SCOPE_REQUEST, new RequestScope());beanFactory.registerScope(WebApplicationContext.SCOPE_SESSION, new SessionScope());
}
那此时beanFactory有了session和request的Scope对象,来看SessionScope的get方法
org.springframework.web.context.request.SessionScope#get
@Overridepublic Object get(String name, ObjectFactory<?> objectFactory) {Object mutex = RequestContextHolder.currentRequestAttributes().getSessionMutex();synchronized (mutex) {return super.get(name, objectFactory);}}// 进父类@Overridepublic Object get(String name, ObjectFactory<?> objectFactory) {// 从ThreadLocal变量中获取当前request的属性对象RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();// 从属性中根据bean名获取bean对象Object scopedObject = attributes.getAttribute(name, getScope());if (scopedObject == null) {// 如果没有会调用CreateBean方法新建一个,并设置到当前请求上下文属性中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;}
跟进去再看看RequestAttributes这个请求属性对象哪来的
public abstract class RequestContextHolder {private static final ThreadLocal<RequestAttributes> requestAttributesHolder =new NamedThreadLocal<>("Request attributes");private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder =new NamedInheritableThreadLocal<>("Request context");public static RequestAttributes getRequestAttributes() {RequestAttributes attributes = requestAttributesHolder.get();if (attributes == null) {attributes = inheritableRequestAttributesHolder.get();}return attributes;}
}
从请求对象RequestAttribute中根据beanName获取实例代码
@Overridepublic Object getAttribute(String name, int scope) {// scope是requestif (scope == SCOPE_REQUEST) {// 如果请求已经处理完毕,判断了一个boolean值,下边有介绍if (!isRequestActive()) {throw new IllegalStateException("Cannot ask for request attribute - request is not active anymore!");}return this.request.getAttribute(name);}else {HttpSession session = getSession(false);if (session != null) {try {Object value = session.getAttribute(name);if (value != null) {this.sessionAttributesToUpdate.put(name, value);}return value;}catch (IllegalStateException ex) {// Session invalidated - shouldn't usually happen.}}return null;}}
好家伙,又来新知识了,这个RequestAttribute对象很关键,从线程ThreadLocal变量获取的,这可以理解,一个请求一个线程来处理嘛,那这个对象啥时候被设置进去的呢。思考了下,估计是DispatchServlet接受请求分发的时候处理,我们继续跟代码
DispatchServlet那肯定先看service(),先走爷爷类HttpServlet#service,这里没有,再看父类FrameworkServlet#service,有了
org.springframework.web.servlet.FrameworkServlet#service
@Overrideprotected void service(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());if (httpMethod == HttpMethod.PATCH || httpMethod == null) {// 这里processRequest(request, response);}else {super.service(request, response);}}protected final void processRequest(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {// 忽略了无关代码// 获取原先的RequestAttributeRequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();// new一个新的ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);// 设置到RequestContextHolder的ThreadLocal中initContextHolders(request, localeContext, requestAttributes);// diService()执行try {doService(request, response);}catch (ServletException | IOException ex) {failureCause = ex;throw ex;}catch (Throwable ex) {failureCause = ex;throw new NestedServletException("Request processing failed", ex);}finally {// 重新设置回原来的resetContextHolders(request, previousLocaleContext, previousAttributes);if (requestAttributes != null) {// 请求处理完毕,把刚才new的那个requestAttributes请求标志置为完成// 上边判断请求是否存活isRequestActive(),就是跟这个有关requestAttributes.requestCompleted();}logResult(request, response, failureCause, asyncManager);publishRequestHandledEvent(request, response, startTime, failureCause);}}protected ServletRequestAttributes buildRequestAttributes(HttpServletRequest request,@Nullable HttpServletResponse response, @Nullable RequestAttributes previousAttributes) {if (previousAttributes == null || previousAttributes instanceof ServletRequestAttributes) {// 实际上RequestAttributes对象是一个ServletRequestAttributes对象// 绑定了request和responsereturn new ServletRequestAttributes(request, response);}else {return null; // preserve the pre-bound RequestAttributes instance}}// 把ServletRequestAttributes 设置到当前的ThreadLocal变量中private void initContextHolders(HttpServletRequest request,@Nullable LocaleContext localeContext, @Nullable RequestAttributes requestAttributes) {if (localeContext != null) {LocaleContextHolder.setLocaleContext(localeContext, this.threadContextInheritable);}if (requestAttributes != null) {RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable);}}
看到这,这个BeanDefinition对应的bean实例化出来了,那开篇说过,有@Scope注解的beanDefinition是被狸猫换太子的,是一个FactoryBean(ScopedProxyFactoryBean),还得继续看ScopedProxyFactoryBean.getObject()
ScopedProxyFactoryBean实现了BeanFactoryAware,setBeanFactory方法执行的时候创建的代理对象,getObject返回代理对象。
怎么代理的就先不管了,单写一篇学习代理。代理对象的方法拦截器DynamicAdvisedInterceptor中会再getBean()从beanFactory获取最初的beanDefinition的对象进行invoke,最初的beanDefinition也是个scope类型,跟上边的ScopedProxyFactoryBean实例化走一个路子。
public class ScopedProxyFactoryBean extends ProxyConfigimplements FactoryBean<Object>, BeanFactoryAware, AopInfrastructureBean {@Overridepublic 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());}@Overridepublic Object getObject() {if (this.proxy == null) {throw new FactoryBeanNotInitializedException();}return this.proxy;}}
到这基本就完事了,总结:
1、每次请求上来将新建一个ServletRequestAttribute对象绑定request和response,并注册到RequestContextHolder类中的ThreadLocal变量上
2、在Web容器refresh方法,ServletWebServerApplicationContext#postProcessBeanFactory中注册工具类SessionScope和RequestScope用来实例化bean时调用
3、解析beanDefinition时,@SessionScope或@RequestScope注解的beanDefinition的Scope属性设置为request或session,并且原先的beanDefinition已经被替换(ScopedProxyFactoryBean)
4、doGetBean获取Scope属性为request或session类型的bean类实例时使用SessionScope.get(beanName, ObjectFactory)或RequestScope.get(beanName, ObjectFactory)来获取,先获取这个ServletRequestAttribute对象,如果这对象中对应的HttpServletRequest或request对应的Session没有这个bean实例,再调用ObjectFactory.getObject()方法创建,getObject()具体工作是由AbstractBeanFactory#createBean来创建
5、上一步创建的东西只是ScopedProxyFactoryBean,FactoryBean.getObject() 获取代理对象,代理对象拦截器方法中再getBean()获取真正的bean对象执行操作
Spring注解@Scope---SessionScope和RequestScope相关推荐
- Spring注解@Scope
Spring注解@Scope 一.@Scope注解 1.@Scope注解作用 @Scope注解用于设置实例的作用域. 默认值是单实例,即当IOC容器启动后就调用该方法创建对象放到IOC容器中,以后每次 ...
- 一分钟学会spring注解之@Scope注解
今天主要从以下几方面来介绍一下@Scope注解 @Scope注解是什么 @Scope注解怎么使用 @Scope注解的使用场景 1,@Scope注解是什么 @Scope注解是springIoc容器中的一 ...
- 【Spring注解驱动开发】使用@Scope注解设置组件的作用域
写在前面 Spring容器中的组件默认是单例的,在Spring启动时就会实例化并初始化这些对象,将其放到Spring容器中,之后,每次获取对象时,直接从Spring容器中获取,而不再创建对象.如果每次 ...
- 浅谈Spring注解
Spring目前的趋势是使用注解结合Java代码而不是配置来定义行为.属性.功能.规则和扩展点,因此梳理注解也是梳理Spring功能点的很好的方式,全面的梳理可以补足我们知识点的漏洞. 查找所有注解 ...
- java spring注解教程,spring注解
spring注解 扫描指定的包中的类上的注解,常用的注解有: @Controller 声明Action组件 @Service 声明Service组件 @Service("myMo ...
- java spring框架 注解_史上最全的java spring注解
史上最全的java spring注解,没有之一 注解是个好东西,但好东西我们也是看见过,整理过,理解过,用过才知道好.不求我们每个都记住,但求保有印象,在需要的时候能提取出来再查找相关资料,平时工作就 ...
- spring中间scope详细解释
0.思维导图 1. scope概论 spring中scope是一个很关键的概念.简单说就是对象在spring容器(IOC容器)中的生命周期,也能够理解为对象在spring容器中的创建方式. 2. sc ...
- Spring注解Annotation
1. 使用Spring注解来注入属性 1.1. 使用注解以前我们是怎样注入属性的 类的实现: Java代码 public class UserManagerImpl implements Use ...
- 注解赋值可以是方法_P7笔记,把Spring注解讲的明明白白
关注公众号领取海量架构师资料 环境搭建 注解的方式是通过配置类的方式来注入组件,注解注入要比XML注入的方式简单,注解注入也需要在前者的基础上,添加一个spring-context的包,也是实际开发中 ...
最新文章
- Eclipse编辑jsp、js文件时,经常出现卡死现象解决汇总
- Qt Creator使用灯光
- mysql笔记 doc_Mysql DOC阅读笔记
- 【转】C++ win32窗口创建详解
- 如此沙雕的代码注释,还是程序员会玩!
- Python绘图,圣诞树,花,爱心 | Turtle篇
- python3数字类型_python3笔记八:python数据类型-Number数字
- python无法使用decode,Python处理文件名或路径名中无法decode代码问题
- SQLserver nText和varchar 不兼容
- 手工matlab下K-means聚类算法实现而不是调用库函数
- 日本将推出“隐形列车”,你要去体验一番吗?
- rtx服务器插件的作用,腾讯通RTX手机版插件介绍 - 有度帮助中心
- Receptive Field Block Net for Accurate and Fast Object Detection
- 2022年的电视评判标准,为何还要强调画质?
- [附源码]java毕业设计大学生兼职招聘网站
- 物联网解决方案架构及其流程
- 正面硬刚Beats!这款耳机从美国红回中国,细腻音质千元内无敌手!
- C++11 boost::spirit::qi简单的XML解析器示例
- jstl标签c:choose,c:when,c:otherwise用法
- Mob短信验证码的继承