• SpringBoot:@Scope注解学习


    概述

    先通过注解的javadoc,可以了解到,@Scope在和@Component注解一起修饰在类上,作为类级别注解时,@Scope表示该类实例的范围,在和@Bean一起修饰在方法上,作为方法级别注解时,@Scope表示该方法返回的实例的范围。
    对于@Scope注解,我们常用的属性一般就是:value和proxyMode,value就是指明使用哪种作用域范围,proxyMode指明使用哪种作用域代理。
    @Scope定义提供了的作用域范围一般有:singleton单例、prototype原型、requestweb请求、sessionweb会话,同时我们也可以自定义作用域。

    作用域范围

    • singleton单例范围,这个是比较常见的,Spring中bean的实例默认都是单例的,单例的bean在Spring容器初始化时就被直接创建,不需要通过proxyMode指定作用域代理类型。
    • prototype原型范围,这个使用较少,这种作用域的bean,每次注入调用,Spring都会创建返回不同的实例,但是,需要注意的是,如果未指明代理类型,即不使用代理的情况下,将会在容器启动时创建bean,那么每次并不会返回不同的实例,只有在指明作用域代理类型例如TARGET_CLASS后,才会在注入调用每次创建不同的实例。
    • requestweb请求范围,(最近遇到的问题就是和request作用域的bean有关,才发现之前的理解有偏差),当使用该作用域范围时(包括下面的session作用域),必须指定proxyMode作用域代理类型,否则将会报错,对于request作用域的bean,(之前一直理解的是每次有http请求时都会创建),但实际上并不是这样,而是Spring容器将会创建一个代理用作依赖注入,只有在请求时并且请求的处理中需要调用到它,才会实例化该目标bean。
    • sessionweb会话范围,这个和request类似,同样必须指定proxyMode,而且也是Spring容器创建一个代理用作依赖注入,当有会话创建时,并且在会话中请求的处理中需要调用它,才会实例话该目标bean,由于是会话范围,生命依赖于session。

    作用域代理

    如果指定为proxyMode = ScopedProxyMode.TARGET_CLASS,那么将使用cglib代理创建代理实例;如果指定为proxyMode = ScopedProxyMode.INTERFACE,那么将使用jdk代理创建代理实例;如果不指定,则直接在Spring容器启动时创建该实例。而且使用代理创建代理实例时,只有在注入调用时,才会真正创建类对象。

    除了上述作用域范围,Spring也允许我们自定义范围,主要操作为:

    1. 先实现Scope接口创建自定义作用域范围类
    2. 使用CustomScopeConfigurer注册自定义的作用域范围

    后面写了一个例子实践一下,自定义了一种同一分钟的作用域范围,即同一分钟获取的是相同实例。
    首先自定义作用域范围类TimeScope:

    /**
     * 首先自定义作用域范围类TimeScope:
     * Scope接口提供了五个方法,只有get()和remove()是必须实现,get()中写获取逻辑,
     * 如果已有存储中没有该名称的bean,则通过objectFactory.getObject()创建实例。
     */
    @Slf4j
    public class TimeScope implements Scope {
    
        private static Map<String, Map<Integer, Object>> scopeBeanMap = new HashMap<>();
    
        @Override
        public Object get(String name, ObjectFactory<?> objectFactory) {
            Integer hour = Calendar.getInstance().get(Calendar.HOUR_OF_DAY);
            // 当前是一天内的第多少分钟
            Integer minute = hour * 60 + Calendar.getInstance().get(Calendar.MINUTE);
            log.info("当前是第 {} 分钟", minute);
            Map<Integer, Object> objectMap = scopeBeanMap.get(name);
            Object object = null;
            if (Objects.isNull(objectMap)) {
                objectMap = new HashMap<>();
                object = objectFactory.getObject();
                objectMap.put(minute, object);
                scopeBeanMap.put(name, objectMap);
            } else {
                object = objectMap.get(minute);
                if (Objects.isNull(object)) {
                    object = objectFactory.getObject();
                    objectMap.put(minute, object);
                    scopeBeanMap.put(name, objectMap);
                }
            }
            return object;
        }
    
        @Override
        public Object remove(String name) {
            return scopeBeanMap.remove(name);
        }
    
        @Override
        public void registerDestructionCallback(String name, Runnable callback) {
        }
        @Override
        public Object resolveContextualObject(String key) {
            return null;
        }
        @Override
        public String getConversationId() {
            return null;
        }
    }
    
    /**
     * 然后注册自定义的作用域范围:
     */
    @Configuration
    @Slf4j
    public class BeanScopeConfig {
        @Bean
        public CustomScopeConfigurer customScopeConfigurer() {
            CustomScopeConfigurer customScopeConfigurer = new CustomScopeConfigurer();
            Map<String, Object> map = new HashMap<>();
            map.put("timeScope", new TimeScope());
            customScopeConfigurer.setScopes(map);
            return customScopeConfigurer;
        }
        
        @Bean
        @Scope(value = "timeScope", proxyMode = ScopedProxyMode.TARGET_CLASS)
        public TimeScopeBean timeScopeBean() {
            TimeScopeBean timeScopeBean = new TimeScopeBean();
            timeScopeBean.setCurrentTime(System.currentTimeMillis());
            log.info("time scope bean");
            return timeScopeBean;
        }
    }
    
    然后注入调用timeScopeBean,同一分钟内重复调用,使用相同实例,不同分钟将创建新实例
    

    转载自:秋月:https://www.wetsion.site/spring-boot-annotation-scope.html

  • 相关阅读:
    使用iconv编程进行字符集转换
    Unity3D学习之路 C#学习笔记(一)
    跨平台的游戏客户端Socket封装
    TCP长连接与短连接的区别
    C++中的long long和__int64类型
    基于cocos2dx的游戏客户端优化
    Android NDK带来什么
    strcpy_s与strcpy的比较
    英文字母和中文汉字在不同字符集编码下的字节数
    socket的read和recv函数的区别
  • 原文地址:https://www.cnblogs.com/hyry/p/11987067.html
Copyright © 2020-2023  润新知