• 【spring源码探索】一分钟搞懂RefreshScope的作用及实现原理


    前文

    下述文章完全为个人阅读源码的随笔记录,如有错误,欢迎大家指出。

    过程

    过程很坎坷,而且大家应该都不想看了吧,简而言之就是先写个测试DEMO,然后各种DEBUG。

    结论

    这次先直接上结论,然后再通过测试DEMO给出验证,最后再去跟代码。
    RefreshScope的作用介于Singleton和Prototype之间,你说他是单例啊,他在某些条件下是单例,你说他是多例把,他确实在某些条件下也是多例。
    这个条件就在于Refresh。

    加了RefreshScope注解的bean,每次容器刷新后,都会重新生成一个对象。

    顺便记录一下底层的实现把:

    在IOC容器里面,有一个map类型的scopes的缓存,他会根据scope名称不同,分为request、session、refresh、application(至于为啥没有单例和多例,因为单例的缓存已经放在singleObjects里面了,多例就更不用说,压根就不需要缓存)。

    咱们这次的关注点是refresh,他map里面取出来的对象就是RefreshScope,RefreshScope的父类GenericScope有一个叫BeanLifecycleWrapperCache,这玩意是缓存的包装对象了,里面的ScopeCache其实也就是一个KEY为beanName,Value是bean的Map缓存,只是这个缓存跟单例池唯一的区别就是他对外提供了一个清空方法(其实单例池咱们也可以给他时改造成这样)

    只要调用RefreshScope的refreshAll()方法,就会清空这个缓存,下次再取bean的时候,从缓存里面取不到自然就会重新走getBean()方法重新生成再放进这个缓存里面了。

    最后附一个可以直接开DEBUG的DEMO,对源码感兴趣的同学可以兄弟可以直接拿去开搞,请原谅我反射直接做的搬砖工。

    	public static void main(String[] args) throws Exception {
            ConfigurableApplicationContext run = SpringApplication.run(Consumer03.class, args);
            Object eurekaClient = run.getBean("scopedTarget.eurekaClient");
            Object eurekaClient1 = run.getBean("scopedTarget.eurekaClient");
    
            ConfigurableListableBeanFactory beanFactory = run.getBeanFactory();
            Map<String, Scope> scopes = (Map<String, Scope>) getFieldValueByObject(beanFactory, "scopes");
            RefreshScope refreshScope = (RefreshScope) scopes.get("refresh");
            refreshScope.refreshAll();
    
            Object eurekaClient2 = run.getBean("scopedTarget.eurekaClient");
    
            System.out.println(eurekaClient1 == eurekaClient);
            System.out.println(eurekaClient1 == eurekaClient2);
        }
    
        public static Object getFieldValueByObject(Object object, String targetFieldName) throws Exception {
    
            // 获取该对象的Class
            Class sonClass = object.getClass();
            Class<?> objClass = sonClass.getSuperclass().getSuperclass();
    
            // 初始化返回值
            Object result = null;
    
            // 获取所有的属性数组
            Field[] fields = objClass.getDeclaredFields();
            for (Field field : fields) {
                // 属性名称
                String currentFieldName = "";
    
                try {
                    currentFieldName = field.getName();
                    System.out.println("currentFieldName:"+currentFieldName);
                    if (currentFieldName.equals(targetFieldName)) {
                        field.setAccessible(true);
                        result = field.get(object);
    
                        return result; // 通过反射拿到该属性在此对象中的值(也可能是个对象)
                    }
    
                } catch (SecurityException e) {
                    // 安全性异常
                    e.printStackTrace();
                } catch (IllegalArgumentException e) {
                    // 非法参数
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    // 无访问权限
                    e.printStackTrace();
                }
            }
    
            return result;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
  • 相关阅读:
    动态规划的简单套路(C++描述)
    【JavaEE多线程】线程中断 interrupt()
    java毕业设计在线售药系统Mybatis+系统+数据库+调试部署
    docker容器怎么设置开机启动
    PCL-MAL 聚己内酯马来酰亚胺
    layer 弹窗,enter 、esc 按键监听确定、取消事件
    八、class 与 style 绑定(1)
    分布式计算模型Mapreduce实践与原理剖析(一)
    光路科技:工业以太网交换机引领工业互联网新篇章
    ntfs是什么硬盘?ntfs硬盘如何在苹果电脑使用
  • 原文地址:https://blog.csdn.net/qq_29611427/article/details/125429774