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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM