SpringBoot:@Scope註解學習

概述

先經過註解的javadoc,能夠了解到,@Scope在和@Component註解一塊兒修飾在類上,做爲類級別註解時,@Scope表示該類實例的範圍,在和@Bean一塊兒修飾在方法上,做爲方法級別註解時,@Scope表示該方法返回的實例的範圍。
對於@Scope註解,咱們經常使用的屬性通常就是:value和proxyMode,value就是指明使用哪一種做用域範圍,proxyMode指明使用哪一種做用域代理。

@Scope定義提供了的做用域範圍通常有:singleton單例、prototype原型、requestweb請求、sessionweb會話,同時咱們也能夠自定義做用域。html

做用域範圍

  • 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:java

/**
 * 首先自定義做用域範圍類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.htmlweb

相關文章
相關標籤/搜索