莫言丰乳肥臀txt下载@Scope作⽤域代理的应⽤:@RefreshScope注解实现动态刷
新配置的底层原理与实现
英语基础入门
@RefreshScope这个注解存在于spring-cloud-context规范包中,它的作⽤就是允许在服务运⾏的过程中,在加了@Value注解的类上加了@RefreshScope注解,那么这个属性就能够实时地动态刷新其属性值,通常⽤于服务整合配置中⼼的场景。如果认真地想⼀下就会觉得这个功能很神奇,因为正常来说⼀个bean在服务启动的时候经历了⾃⼰的⽣命周期,包括属性注⼊,之后在服务运⾏期间这个bean就基本不会做关于⽣命周期的任何事情了,也就是说也不会再次经历属性注⼊这个阶段了,那么这样的话动态刷新属性值是如何实现的?接下来我们就来看⼀下它的底层实现是如何做到这个神奇的功能的
扇贝实现原理
@RefreshScope的实现原理其实主要就是基于@Scope注解的作⽤域代理的基础上进⾏扩展实现的,因为当配置中⼼的配置发⽣改变的时候,对应加了@RefreshScope直接的类中加了@Value注解的属性值能够发⽣改变就说明肯定是⼜⽣成了⼀个新的bean(只有重新⾛⼀遍bean的⽣命周期才有可能使得加了@Value注解的属性重新被初始化为新的值),所以作⽤域代理就能够适⽤于解决这种场景了:当⼀个普通的bean依赖了⼀个加了@RefreshScope注解的bean之后,通过作⽤域代理使得这个普通bean依赖的是⼀个代理对象,每当调⽤这个代理对象的⽬标⽅法的时候就从⽬标源中获取到原始bean实例,⽽获取
mykonos
的⽅式就是通过调⽤getBean⽅法,在getBean⽅法中会寻找refresh 作⽤域的scope处理类,通过这个scope处理类去⽣成原始bean,⽽这个scope处理类⽣成原始bean有两种⽅式,⼀种是直接取bean缓存,⼀种是从bean⼯⼚中创建,当从bean⼯⼚中创建出来⽿钉bean实例就会放到bean缓存中,只要配置中⼼不发⽣更改,那么这个缓存就⼀直存在,但是如果配置中⼼发⽣了变更,会把变更的配置更新到spring容器的Environment中,并且同事bean缓存就会被清空,从⽽就会从bean⼯⼚中创建bean实例了,⽽这次创建bean实例的时候就会继续经历这个bean的⽣命周期,使得@Value属性值能够从Environment中获取到最新的属性值,这样整个过程就达到了动态刷新配置的效果
源码分析
(1)@RefreshScope
小清新英文歌曲
expander
可以看到@RefreshScope注解其实就是给@Scope注解包了⼀层,并且指定了refresh这个⾃定义的scope作⽤域,同时也声明开启了proxyMode,也就是开启了scope的作⽤域代理
(2)RefreshAutoConfiguration
在spring-cloud-context包的spring.factories⽂件中,可以看到有⼀个叫RefreshAutoConfiguration的⾃动配置类,该配置类就是与⾃动刷新有关,打开这个类可以看到有⼀个⽐较重要的组件RefreshScope:
carbon black
拜拜的英文
(3)GenericScope
通过继承图可以看出,GenericScope继承了BeanDefinitionRegistryPostprocessor,熟悉spring的应该知道这个组件是spring中很重要的⼀个组件,对于⾃定义的BeanDefinitionRegistryPostprocessor的执⾏时机是在spring扫描到所有的BeanDefinition之后去执⾏的,也就是说在⾃定义BeanDefinitionRegistryPostprocessor的postProcessBeanDefinitionRegistry⽅法中就可以获取到spring容器
中的所有BeanDefinition了,那么我们看⼀下GenericScope是如何实现postProcessBeanDefinitionRegistry⽅法的:
在该⽅法中会去获取到spring容器中所有的BeanDefinition进⾏遍历,遍历的时候会有个很重要的条件进⾏判断找到decoratedDefinition 属性不为空并且beanClass等于ScopedProxyFactory的BeanDefinition,那么什么BeanDefinition能符合这两个条件呢?答案就是使⽤了scope作⽤域代理的BeanDefinition,其中就包括了我们这⾥说的加了@RefreshScope注解的bean。当找到了这个bean之后,就把beanClass改成LockedScopedProxyFactoryBean类型,然后还把当前的RefreshScope对象作为LockedScopedProxyFactoryBean 实例化构造⽅法的参数,看下LockedScopedProxyFactoryBean的构造⽅法:
其中这个泛型S就是RefreshScope实例。我们再回到GenericScope的继承图,它还实现了⼀个⽐较重要的接⼝,那就是Scope接⼝,Scope接⼝就是spring给⽤户⾃⼰拓展scope作⽤域的时候去使⽤的,因为上⾯我们知道@RefreshScope注解所声明的scope作⽤域是refresh,所以⾃然也需要有⼀个负责⽣成refresh作⽤域的bean的处理类,该处理类就需要实现Scope接⼝,以上都是关于spring的⾃定义scope的知识,这⾥就不再细说了。所以⾃然地我们就需要去关注实现了scope接⼝的⽅法,但是我们这⾥先不⽤去看,我们关注的是
这个scope是如何注册上spring的
在GenericScope实现的postProcessBeanFactory⽅法中就可以找到通过BeanFactory去把⾃⾝这个scope注册到容器中了,并且注意的是这个注册scope的阶段是在postProcessBeanDefinitionRegistry⽅法执⾏之后执⾏的
(4)LockedScopedProxyFactoryBean
通过上⾯我们可以知道加了@RefreshScope注解的bean的classBean会变成LockedScopedProxyFactoryBean这个类型,⾸先看下它的继承图
chinapay
可以看到它继承了ScopedProxyFactoryBean,这个类很熟悉,就是处理scope作⽤域代理的时候⽣成代理对象的⼀个关键类,对于这个类的作⽤这⾥就不再阐述了,之前讲spring的scope作⽤域代理的时候已经说过了。并且LockedScopedProxyFactoryBean还实现了MethodInterceptor接⼝,表⽰⾃⼰是⼀个拦截器,接下来看下LockedScopedProxyFactoryBean的tBeanFactory⽅法:
⾸先会调⽤⽗类ScopedProxyFactoryBean的tBeanFactory⽅法,执⾏完⽗类的⽅法之后此时就可以通过getObject⽅法拿到⽣成的代理对象了,并且如果代理对象实现了Advid接⼝,那么就会把当前⾃⼰这个拦截器放⼊到候选的拦截器列表中(在代理对象执⾏代理⽅法的时候会从候选的拦截器列表中通过pointcut去筛选出待执⾏的拦截器,⽽这⾥通过addAdvice⽅法直接添加的拦截器默认的pointcut就是全部⽅法都经过该拦截器)
在invoke⽅法中:
1.当调⽤equal⽅法,toString⽅法,hashCode⽅法以及ScopedObject的getTargetObject⽅法的时候就继续进⾏后⾯拦截器的链式调⽤
2.获取⼀把读锁
3.加读锁,从spring容器中获取到对应最新的bean,然后在这个bean中执⾏代理⽅法
demon什么意思很明显,这个拦截器主要做的事情就是从⽬标源中获取到spring容器中最新的bean,然后对这个bean执⾏⽬标代理⽅法,这样每次都从spring容器中重新获取⼀次最新的bean就可以保证这个bean中带有@Value注解的属性值是最新的,达到我们动态刷新属性配置的⽬的
(5)获取refresh作⽤域的bean实例
我们知道当作⽤域代理对象调⽤代理⽅法的时候会从⽬标源中获取到⼀个当前spring容器最新的bean的作为⽬标代理对象,也就是调⽤getBean⽅法,在调⽤getBean⽅法的过程中因为这个bean加了@RefreshScope注解,所以spring会去寻找处理refresh作⽤域的scope 组件,此时就会来到GenericScope的get⽅法
可以看到cache这个map中会存放每⼀个refresh作⽤域的beanName及其对应的BeanLifecycleWrapper对象,然后这个BeanLifecycleWrapper对象⾥⾯就会存储refresh作⽤域的那个bean,但是这⾥是做了⼀个缓存的,就是缓存了代理对象第⼀次调⽤代理⽅法的时候从spring容器中获取到的bean实例,这个缓存的作⽤就是防⽌⼀直从spring中去获取这个bean实例,⽐如说这个bean中带有@Value的属性值⼀直都没有在配置中⼼中进⾏修改,那么每⼀次代理对象在执⾏代理⽅法时都会通过⽬标源从spring容器中去拿这个bean 实例显然是没必要的。