1. 程式人生 > 實用技巧 >【spring bean】@Resource註解的自動注入策略 , 以 專案中注入多個執行緒池的Bean為例 附加自定義SpringBeanSupport

【spring bean】@Resource註解的自動注入策略 , 以 專案中注入多個執行緒池的Bean為例 附加自定義SpringBeanSupport

@Resource和@Autowired註解都是用來實現依賴注入的。只是@AutoWried按by type自動注入,而@Resource預設按byName自動注入。

@Resource有兩個重要屬性,分別是name和type

spring將name屬性解析為bean的名字,而type屬性則被解析為bean的型別。所以如果使用name屬性,則使用byName的自動注入策略,如果使用type屬性則使用byType的自動注入策略。如果都沒有指定,則通過反射機制使用byName自動注入策略。

@Resource依賴注入時查詢bean的規則:(以用在field上為例)

1. 既不指定name屬性,也不指定type屬性,則自動按byName方式進行查詢。如果沒有找到符合的bean,則回退為一個原始型別進行查詢,如果找到就注入。

此時name是變數名

錯誤示例:

@Resource
    private String bucketName;
    @Resource
    private String styleName;

此時的name值是配置bean裡的name屬性指定的值,而不是id的值

<bean id="bucketName " class="java.lang.String"> 
    <constructor-arg value="${oos.bucketName}"/> 
</bean> 
<!-- 圖片樣式名 --> 
<bean 
id="styleName " class="java.lang.String"> <constructor-arg value="${oos.styleName}"/> </bean>

這裡為什麼要重新理解,是因為之前我一直認為對應的是配置檔案的id屬性的值,直到在配置上面兩個String型別的bean的時候,居然會報錯,如下: No qualifying bean of type [java.lang.String] is defined: expected single matching bean but found 2: bucketName,styleName 這是因為spring會去找bean元素裡name屬性值和變數名一致的bean,但是因為都沒有指定name屬性,所以找不到然後就按照原始型別String去查詢,結果一下找到了兩個,所以就報錯。

2. 只是指定了@Resource註解的name,則按name後的名字去bean元素裡查詢有與之相等的name屬性的bean。

正確示例

    @Resource(name="bucket")
    private String bucketName;
    @Resource(name="style")
    private String styleName;

<bean name="bucket" class="java.lang.String"> 
    <constructor-arg value="${oos.bucketName}"/> 
</bean> 
<!-- 圖片樣式名 --> 
<bean name="style" class="java.lang.String"> 
    <constructor-arg value="${oos.styleName}"/> 
</bean>

3. 只指定@Resource註解的type屬性,則從上下文中找到型別匹配的唯一bean進行裝配,找不到或者找到多個,都會丟擲異常

4. 既指定了@Resource的name屬性又指定了type,則從Spring上下文中找到唯一匹配的bean進行裝配,找不到則丟擲異常

===========================以一份程式中 隔離多個業務執行緒池為例===================

1.yml檔案

# 執行緒池配置
thread:
  pool:
    core:
      size: 10
    max:
      size: 10
    queue:
      capacity: 10000
    alive:
      seconds: 1000

2.java檔案

執行緒池注入為Bean交給Spring管理

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.ThreadPoolExecutor;



@Configuration
@ConditionalOnProperty(name = "thread.pool.core.size")
public class ThreadPoolConfiguration {

    @Value("${thread.pool.core.size}")
    private Integer coreSize;

    @Value("${thread.pool.max.size}")
    private Integer maxSize;

    @Value("${thread.pool.queue.capacity}")
    private Integer queueCapacity;

    @Value("${thread.pool.alive.seconds}")
    private Integer keepAliveSeconds;


    @Bean(name = "taskExecutor1")
    public ThreadPoolTaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor poolTaskExecutor = new ThreadPoolTaskExecutor();
        poolTaskExecutor.setCorePoolSize(coreSize);
        poolTaskExecutor.setMaxPoolSize(maxSize);
        poolTaskExecutor.setQueueCapacity(queueCapacity);
        poolTaskExecutor.setKeepAliveSeconds(keepAliveSeconds);
        return poolTaskExecutor;
    }

    @Bean(name = "taskExecutor2")
    public ThreadPoolTaskExecutor monitorTaskExecutor() {
        ThreadPoolTaskExecutor poolTaskExecutor = new ThreadPoolTaskExecutor();
        poolTaskExecutor.setCorePoolSize(coreSize);
        poolTaskExecutor.setMaxPoolSize(maxSize);
        poolTaskExecutor.setQueueCapacity(queueCapacity);
        poolTaskExecutor.setKeepAliveSeconds(keepAliveSeconds);
        poolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardOldestPolicy());
        poolTaskExecutor.setThreadNamePrefix("plat-form-monitor-pool-");
        return poolTaskExecutor;
    }

    @Bean(name = "taskExecutor3")
    public ThreadPoolTaskExecutor reportTaskExecutor() {
        ThreadPoolTaskExecutor reportpoolTaskExecutor = new ThreadPoolTaskExecutor();
        reportpoolTaskExecutor.setCorePoolSize(coreSize);
        reportpoolTaskExecutor.setMaxPoolSize(maxSize);
        reportpoolTaskExecutor.setQueueCapacity(queueCapacity);
        reportpoolTaskExecutor.setKeepAliveSeconds(keepAliveSeconds);
        reportpoolTaskExecutor.setThreadNamePrefix("report-export-pool-");
        return reportpoolTaskExecutor;
    }
}
View Code

3.使用場景

3.1使用的地方[如果都是如下這樣使用,則沒有任何問題]:

A.Class中

@Resource
ThreadPoolTaskExecutor taskExecutor1;



B.Class中

@Resource
ThreadPoolTaskExecutor taskExecutor2;


C.Class中

@Resource
ThreadPoolTaskExecutor taskExecutor3;

如上的方式使用,則沒有任何問題,因為 @Resource是byName自動注入的。

3.2但是如果是下面這種使用方式,則會出現問題:

A.Class中

@Resource
ThreadPoolTaskExecutor taskExecutor1;




B.Class中

public class B  {
    
    private static final ThreadPoolTaskExecutor taskExecutor;


    static {
        taskExecutor2 = SpringBeanSupport.getBean(ThreadPoolTaskExecutor.class);
    }

}

如上使用方式就會有問題,因為 B類中是按照byClass去注入的,而咱們隔離的三個獨立的執行緒池,都是ThreadPoolTaskExecutor型別的,所以啟動會報錯,因為這個getBean會找到三個。

3.3但是如果是修改為下面這種使用方式,則問題解決:

A.Class中

@Resource
ThreadPoolTaskExecutor taskExecutor1;




public class B  {
    
    private static final ThreadPoolTaskExecutor taskExecutor;


    static {
        taskExecutor2 = SpringBeanSupport.getBean("taskExecutor2"); 
    }

}

附加附上SpringBeanSupport原始碼:

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

@Component
public class SpringBeanSupport implements ApplicationContextAware {

    /** Spring應用上下文環境 */    
    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        SpringBeanSupport.applicationContext = applicationContext;
    }


    /**   
     * @return ApplicationContext   
     */
    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    /**
     * 獲取物件
     * @param name
     * @return Object 一個以所給名字註冊的bean的例項
     * @throws BeansException
     */
    public static <T> T getBean(String name) throws BeansException {
        return (T) applicationContext.getBean(name);
    }


    /**   
     * 獲取型別為requiredType的物件   
     * 如果bean不能被型別轉換,相應的異常將會被丟擲(BeanNotOfRequiredTypeException)   
     * @param name       bean註冊名   
     * @param requiredType 返回物件型別   
     * @return Object 返回requiredType型別物件   
     * @throws BeansException
     */
    public static Object getBean(String name, Class<?> requiredType) throws BeansException {
        return applicationContext.getBean(name, requiredType);
    }

    /**
     * 獲取class對應的bean
     * @param tClass
     * @return
     */
    public static <T> T getBean(Class<T> tClass) {
        return applicationContext.getBean(tClass);
    }

    /**   
     * 如果BeanFactory包含一個與所給名稱匹配的bean定義,則返回true    
     * @param name   
     * @return boolean   
     */
    public static boolean containsBean(String name) {
        return applicationContext.containsBean(name);
    }

    /**   
     * 判斷以給定名字註冊的bean定義是一個singleton還是一個prototype。   
     * 如果與給定名字相應的bean定義沒有被找到,將會丟擲一個異常(NoSuchBeanDefinitionException)      
     * @param name   
     * @return boolean   
     * @throws NoSuchBeanDefinitionException
     */
    public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException {
        return applicationContext.isSingleton(name);
    }

    /**   
     * @param name   
     * @return Class 註冊物件的型別   
     * @throws NoSuchBeanDefinitionException
     */
    public static Class<?> getType(String name) throws NoSuchBeanDefinitionException {
        return applicationContext.getType(name);
    }

    /**   
     * 如果給定的bean名字在bean定義中有別名,則返回這些別名      
     * @param name   
     * @return   
     * @throws NoSuchBeanDefinitionException
     */
    public static String[] getAliases(String name) throws NoSuchBeanDefinitionException {
        return applicationContext.getAliases(name);
    }

}
View Code