草庐IT

Spring三级缓存

小唐编程 2023-05-07 原文

1.spring的循环依赖简介

什么是spring的循环依赖?

spring容器的bean互相依赖形成闭环,称为spring的循环依赖。

spring循环依赖只能用于set方法和成员变量注入实现,且要求是单例bean,多例(原型bean)是不能实现循环依赖的。

构造方法注入是不能实现循环依赖的,会抛出异常(Requested bean is currently in creation: Is there an unresolvable circular reference?)

@service
class UserServiceImpl implments UserService{
@Autowried
private EmailService emailService;
}
@service
class EmailServiceImpl implments EmailService{
@Autowried
private UserService userService;
}
为什么Spring解决循环依赖比较麻烦呢?因为Spring创建一个Bean是需要通过反射来构建的,构建过程中无法感知这个类具体是什么类型的,它只能够实例化一个填充一个实体!于是:

创建 UserServiceImpl完成后发现依赖EmailServiceImpl !

于是创建EmailServiceImpl ,但是创建完成后又发现依赖于UserServiceImpl!

于是又去创建UserServiceImpl,又发现EmailServiceImpl !

然后我又去创建EmailServiceImpl

2.如何解决循环依赖?

实例化:调用构造函数将对象创建出来

初始化:调用构造函数将对象创建出来后,给对象的属性也被赋值

可以创建两个容器(Map),一个起名为singletonObjects,一个起名为earlySingletonObjects!

「singletonObjects」:单例池,我们去存放已经创建完成,并且属性也注入完毕的对象!

「earlySingletonObjects」:提前暴露的对象,存放已经创建完成,但是没有注入好的对象!

我们有了这两个Map对象,再次试图创建一个被循环依赖的bean!

创建 UserServiceImpl完成后,把自己存到「earlySingletonObjects」里面去,然后发现依赖EmailServiceImpl !

于是试图从「singletonObjects」寻找,很显然是没有的,然后到「earlySingletonObjects」里面寻找发现也没有,开始新建!

创建EmailServiceImpl 完成后,把自己存放到「earlySingletonObjects」里面去,然后发现依赖UserServiceImpl!

于是试图从「singletonObjects」寻找,很显然是没有的,然后到「earlySingletonObjects」里面寻找,发现了UserServiceImpl对象!

将「earlySingletonObjects」返回的对象UserServiceImpl设置到EmailServiceImpl 中去,创建完成!

把自己放置到「singletonObjects」里面,然后把自己从「earlySingletonObjects」删除掉!返回!

UserServiceImpl将返回的EmailServiceImpl 设置到对应的属性中,创建完成!

把自己放置到「singletonObjects」里面,然后把自己从「earlySingletonObjects」删除掉!返回!

3.Spring为什么使用三级缓存解决呢?

通过上面的解释我们大概明白了循环依赖的解决方案,明明采用二级缓存就能够解决循环依赖,但是Spring为什么使用了三级缓存呢?

我们先来了解一下Spring每个缓存的名字及其作用:

「singletonObjects」:单例池,我们去存放已经创建完成,并且属性也注入完毕的对象!

「earlySingletonObjects」:提前暴露的对象,存放已经创建完成,但是没有注入好的对象!

「singletonFactories」:提前暴露的对象,存放已经创建完成,但是还没有注入好的对象的工厂对象!通过这个工厂可以返回这个对象!

为什么明明使用二级缓存就能够解决的问题,spring偏偏要使用三级缓存去解决呢?

上面的设计方案二级缓存是能够很好的解决循环依赖所带来的问题,但是请大家思考一个问题:

我们创建的bean所依赖的对象是一个需要被Aop代理的对象,怎么办?遇到这种情况,我们肯定不能够直接把创建完成的对象放到缓存中去的!为什么,因为我们期望的注入的是一个被代理后的对象,而不是一个原始对象! 所以这里并不能够直接将一个原始对象放置到缓存中,我们可以直接进行判断,如果需要Aop的话进行代理之后放入缓存!

但是Aop的操作是在哪里做的?是在Spring声明周期的最后一步来做的!如果我们进行判断创建的话,Aop的代理逻辑就会在创建实例的时候就进行Aop的代理了,这明显是不符合Spring对于Bean生命周期的定义的! 所以,Spring重新定义了一个缓存【「singletonFactories」】用来存放一个Bean的工厂对象,创建的对象之后,填充属性之前会把创建好的对象放置到【「singletonFactories」】缓存中去,并不进行实例化,只有在发生了循环引用,或者有对象依赖他的时候,才会调用工厂方法返回一个代理对象,从而保证了Spring对于Bean生命周期的定义!

以下是spring关于三级缓存的定义:

主要原理是利用三级缓存机制:

Map<String, Object> singletonObjects: 一级缓存,也就是我们平常理解的单例池,存放已经完整经历了完整生命周期的bean对象。

Map<String, Object> earlySingletonObjects: 二级缓存,存储早期暴露出来的bean对象,bean的生命周期未结束。(属性还未填充完)

Map<String,ObjectFactory<?> > singletonFactories: 三级缓存,存储生成bean的工厂。

注意:只有单例bean会通过三级缓存提前暴露出来解决循环依赖的问题,而非单例的bean,每次从容器中获取都是一个新的对象,都会重新创建,所以非单例bean是没有缓存的,不会将期放到三级缓存中。

4.spring三级缓存的大致流程

A在创建过程中需要B,于是A先将自己放到三级缓存里面,去实例化B

B实例化的时候发现需要A,于是B先查一级缓存,没有再查二级缓存,还是没有,再查三级缓存,找到了A;然后把三级缓存里面的这个A放到二级缓存里面,并删除三级缓存里面的A

B顺利初始化完毕,将自己放到一级缓存里面(此时B里面的A依然是创建中状态);然后回来接着创建A,此时B已经创建结束,直接从一级缓存里面拿到B,然后完成创建,并将A放入到一级缓存中

有关Spring三级缓存的更多相关文章

  1. ruby-on-rails - 带 Spring 锁的 Rails 4 控制台 - 2

    我正在使用Ruby2.1.1和Rails4.1.0.rc1。当执行railsc时,它被锁定了。使用Ctrl-C停止,我得到以下错误日志:~/.rvm/gems/ruby-2.1.1/gems/spring-1.1.2/lib/spring/client/run.rb:47:in`gets':Interruptfrom~/.rvm/gems/ruby-2.1.1/gems/spring-1.1.2/lib/spring/client/run.rb:47:in`verify_server_version'from~/.rvm/gems/ruby-2.1.1/gems/spring-1.1.

  2. ruby - 如何在 Ubuntu 中清除 Ruby Phusion Passenger 的缓存? - 2

    我试过重新启动apache,缓存的页面仍然出现,所以一定有一个文件夹在某个地方。我没有“公共(public)/缓存”,那么我还应该查看哪些其他地方?是否有一个URL标志也可以触发此效果? 最佳答案 您需要触摸一个文件才能清除phusion,例如:touch/webapps/mycook/tmp/restart.txt参见docs 关于ruby-如何在Ubuntu中清除RubyPhusionPassenger的缓存?,我们在StackOverflow上找到一个类似的问题:

  3. ruby-on-rails - Ruby on Rails 计数器缓存错误 - 2

    尝试在我的RoR应用程序中实现计数器缓存列时出现错误Unknownkey(s):counter_cache。我在这个问题中实现了模型关联:Modelassociationquestion这是我的迁移:classAddVideoVotesCountToVideos0Video.reset_column_informationVideo.find(:all).eachdo|p|p.update_attributes:videos_votes_count,p.video_votes.lengthendenddefself.downremove_column:videos,:video_vot

  4. spring.profiles.active和spring.profiles.include的使用及区别说明 - 2

    转自:spring.profiles.active和spring.profiles.include的使用及区别说明下文笔者讲述spring.profiles.active和spring.profiles.include的区别简介说明,如下所示我们都知道,在日常开发中,开发|测试|生产环境都拥有不同的配置信息如:jdbc地址、ip、端口等此时为了避免每次都修改全部信息,我们则可以采用以上的属性处理此类异常spring.profiles.active属性例:配置文件,可使用以下方式定义application-${profile}.properties开发环境配置文件:application-dev

  5. ruby-on-rails - Spring 不起作用。 [未初始化常量 Spring::SID::DL] - 2

    我无法运行Spring。这是错误日志。myid-no-MacBook-Pro:myid$spring/Users/myid/.rbenv/versions/1.9.3-p484/lib/ruby/gems/1.9.1/gems/spring-0.0.10/lib/spring/sid.rb:17:in`fiddle_func':uninitializedconstantSpring::SID::DL(NameError)from/Users/myid/.rbenv/versions/1.9.3-p484/lib/ruby/gems/1.9.1/gems/spring-0.0.10/li

  6. 【云原生】SpringCloud-Spring Boot Starter使用测试 - 2

    目录SpringBootStarter是什么?以前传统的做法使用SpringBootStarter之后starter的理念:starter的实现: 创建SpringBootStarter步骤在idea新建一个starter项目、直接执行下一步即可生成项目。 在xml中加入如下配置文件:创建proterties类来保存配置信息创建业务类:创建AutoConfiguration测试如下:SpringBootStarter是什么? SpringBootStarter是在SpringBoot组件中被提出来的一种概念、简化了很多烦琐的配置、通过引入各种SpringBootStarter包可以快速搭建出一

  7. ruby-on-rails - bundle 安装尝试使用缓存文件 - 2

    当我尝试进行bundle安装时,我的gem_path和gem_home指向/usr/local/rvm/gems/我没有写入权限,并且由于权限无效而失败。因此,我已将两个路径都更改为我具有写入权限的本地目录。这样做时,我进行了bundle安装,我得到:bruno@test6:~$bundleinstallFetchinggemmetadatafromhttps://rubygems.org/.........Fetchinggemmetadatafromhttps://rubygems.org/..Bundler::GemspecError:Couldnotreadgemat/afs/

  8. ruby-on-rails - Heroku Action 缓存似乎不起作用 - 2

    我一直在Heroku上尝试不同的缓存策略,并添加了他们的memcached附加组件,目的是为我的应用程序添加Action缓存。但是,当我在我当前的应用程序上查看Rails.cache.stats时(安装了memcached并使用dalligem),在执行应该缓存的操作后,我得到current和total_items为0。在Controller的顶部,我想缓存我有的Action:caches_action:show此外,我修改了我的环境配置(对于在Heroku上运行的配置)config.cache_store=:dalli_store我是否可以查看其他一些统计数据,看看它是否有效或我做错

  9. ruby-on-rails - rails expire_page 没有删除缓存的文件 - 2

    我有一个具有页面缓存的ControllerAction,我制作了一个清扫程序,它使用Controller和指定的Action调用expire_page...Controller操作呈现一个js.erb模板,所以我试图确保expire_page删除public/javascripts中的.js文件,但它没有这样做。classJavascriptsController"javascripts",:action=>"lol",:format=>'js')endend...所以,我访问javascripts/lol.js并呈现我的模板。我验证了public/javascripts/lol.js

  10. ruby-on-rails - rails 3 缓存 : expire action for named route - 2

    我的Controller有这个:caches_action:render_ticker_for_channel,:expires_in=>30.seconds在我的路由文件中我有这个:match'/render_c_t/:channel_id'=>'render#render_ticker_for_channel',:as=>:render_channel_ticker在日志文件中我看到了这个:Writefragmentviews/mcr3.dev/render_c_t/63(11.6ms)我如何手动使它过期?我需要从与渲染Controller不同的Controller使它过期,但即使

随机推荐