【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> <!-- 圖片樣式名 --> <beanid="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