寫在前面
Spring的強大之處不僅僅是提供了IOC容器,能夠通過過濾規則指定排除和只包含哪些元件,它還能夠通過自定義TypeFilter來指定過濾規則。如果Spring內建的過濾規則不能夠滿足我們的需求,那麼我們就可以通過自定義TypeFilter來實現我們自己的過濾規則。
專案工程原始碼已經提交到GitHub:https://github.com/sunshinelyz/spring-annotation
FilterType中常用的規則
在使用@ComponentScan註解實現包掃描時,我們可以使用@Filter指定過濾規則,在@Filter中,通過type指定過濾的型別。而@Filter註解的type屬性是一個FilterType列舉,如下所示。
package org.springframework.context.annotation;
public enum FilterType {
ANNOTATION,
ASSIGNABLE_TYPE,
ASPECTJ,
REGEX,
CUSTOM
}
每個列舉值的含義如下所示。
(1)ANNOTATION:按照註解進行過濾。
例如,使用@ComponentScan註解進行包掃描時,按照註解只包含標註了@Controller註解的元件,如下所示。
@ComponentScan(value = "io.mykit.spring", includeFilters = {
@Filter(type = FilterType.ANNOTATION, classes = {Controller.class})
}, useDefaultFilters = false)
(2)ASSIGNABLE_TYPE:按照給定的型別進行過濾。
例如,使用@ComponentScan註解進行包掃描時,按照給定的型別只包含PersonService類(介面)或其子類(實現類或子介面)的元件,如下所示。
@ComponentScan(value = "io.mykit.spring", includeFilters = {
@Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {PersonService.class})
}, useDefaultFilters = false)
此時,只要是PersonService型別的元件,都會被載入到容器中。也就是說:當PersonService是一個Java類時,Person類及其子類都會被載入到Spring容器中;當PersonService是一個介面時,其子介面或實現類都會被載入到Spring容器中。
(3)ASPECTJ:按照ASPECTJ表示式進行過濾
例如,使用@ComponentScan註解進行包掃描時,按照ASPECTJ表示式進行過濾,如下所示。
@ComponentScan(value = "io.mykit.spring", includeFilters = {
@Filter(type = FilterType.ASPECTJ, classes = {AspectJTypeFilter.class})
}, useDefaultFilters = false)
(4)REGEX:按照正規表示式進行過濾
例如,使用@ComponentScan註解進行包掃描時,按照正規表示式進行過濾,如下所示。
@ComponentScan(value = "io.mykit.spring", includeFilters = {
@Filter(type = FilterType.REGEX, classes = {RegexPatternTypeFilter.class})
}, useDefaultFilters = false)
(5)CUSTOM:按照自定義規則進行過濾。
如果實現自定義規則進行過濾時,自定義規則的類必須是org.springframework.core.type.filter.TypeFilter介面的實現類。
例如,按照自定義規則進行過濾,首先,我們需要建立一個org.springframework.core.type.filter.TypeFilter介面的實現類MyTypeFilter,如下所示。
public class MyTypeFilter implements TypeFilter {
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
return false;
}
}
當我們實現TypeFilter介面時,需要實現TypeFilter介面中的match()方法,match()方法的返回值為boolean型別。當返回true時,表示符合規則,會包含在Spring容器中;當返回false時,表示不符合規則,不會包含在Spring容器中。另外,在match()方法中存在兩個引數,分別為MetadataReader型別的引數和MetadataReaderFactory型別的引數,含義分別如下所示。
- metadataReader:讀取到的當前正在掃描的類的資訊。
- metadataReaderFactory:可以獲取到其他任務類的資訊。
接下來,使用@ComponentScan註解進行如下配置。
@ComponentScan(value = "io.mykit.spring", includeFilters = {
@Filter(type = FilterType.CUSTOM, classes = {MyTypeFilter.class})
}, useDefaultFilters = false)
在FilterType列舉中,ANNOTATION和ASSIGNABLE_TYPE是比較常用的,ASPECTJ和REGEX不太常用,如果FilterType列舉中的型別無法滿足我們的需求時,我們也可以通過實現org.springframework.core.type.filter.TypeFilter介面來自定義過濾規則,此時,將@Filter中的type屬性設定為FilterType.CUSTOM,classes屬性設定為自定義規則的類對應的Class物件。
實現自定義過濾規則
在專案的io.mykit.spring.plugins.register.filter包下新建MyTypeFilter,並實現org.springframework.core.type.filter.TypeFilter介面。此時,我們先在MyTypeFilter類中列印出當前正在掃描的類名,如下所示。
package io.mykit.spring.plugins.register.filter;
import org.springframework.core.io.Resource;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.TypeFilter;
import java.io.IOException;
/**
* @author binghe
* @version 1.0.0
* @description 自定義過濾規則
*/
public class MyTypeFilter implements TypeFilter {
/**
* metadataReader:讀取到的當前正在掃描的類的資訊
* metadataReaderFactory:可以獲取到其他任務類的資訊
*/
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
//獲取當前類註解的資訊
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
//獲取當前正在掃描的類的資訊
ClassMetadata classMetadata = metadataReader.getClassMetadata();
//獲取當前類的資源資訊,例如:類的路徑等資訊
Resource resource = metadataReader.getResource();
//獲取當前正在掃描的類名
String className = classMetadata.getClassName();
//列印當前正在掃描的類名
System.out.println("-----> " + className);
return false;
}
}
接下來,我們在PersonConfig類中配置自定義過濾規則,如下所示。
@Configuration
@ComponentScans(value = {
@ComponentScan(value = "io.mykit.spring", includeFilters = {
@Filter(type = FilterType.CUSTOM, classes = {MyTypeFilter.class})
}, useDefaultFilters = false)
})
public class PersonConfig {
@Bean("person")
public Person person01(){
return new Person("binghe001", 18);
}
}
接下來,我們執行SpringBeanTest類中的testComponentScanByAnnotation()方法進行測試,輸出的結果資訊如下所示。
-----> io.mykit.spring.test.SpringBeanTest
-----> io.mykit.spring.bean.Person
-----> io.mykit.spring.plugins.register.controller.PersonController
-----> io.mykit.spring.plugins.register.dao.PersonDao
-----> io.mykit.spring.plugins.register.filter.MyTypeFilter
-----> io.mykit.spring.plugins.register.service.PersonService
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
personConfig
person
可以看到,已經輸出了當前正在掃描的類的名稱,同時,除了Spring內建的bean名稱外,只輸出了personConfig和person,沒有輸出使用@Repository、@Service、@Controller註解標註的元件名稱。這是因為當前PersonConfig上標註的@ComponentScan註解是使用自定義的規則,而在MyTypeFilter自定義規則的實現類中,直接返回了false值,將所有的bean都排除了。
我們可以在MyTypeFilter類中簡單的實現一個規則,例如,當前掃描的類名稱中包含有字串Person,就返回true,否則返回false。此時,MyTypeFilter類中match()方法的實現如下所示。
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
//獲取當前類註解的資訊
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
//獲取當前正在掃描的類的資訊
ClassMetadata classMetadata = metadataReader.getClassMetadata();
//獲取當前類的資源資訊,例如:類的路徑等資訊
Resource resource = metadataReader.getResource();
//獲取當前正在掃描的類名
String className = classMetadata.getClassName();
//列印當前正在掃描的類名
System.out.println("-----> " + className);
return className.contains("Person");
}
此時,在io.mykit.spring包下的所有類都會通過MyTypeFilter類的match()方法,來驗證類名是否包含Person,如果包含則返回true,否則返回false。
我們再次執行SpringBeanTest類中的testComponentScanByAnnotation()方法進行測試,輸出的結果資訊如下所示。
-----> io.mykit.spring.test.SpringBeanTest
-----> io.mykit.spring.bean.Person
-----> io.mykit.spring.plugins.register.controller.PersonController
-----> io.mykit.spring.plugins.register.dao.PersonDao
-----> io.mykit.spring.plugins.register.filter.MyTypeFilter
-----> io.mykit.spring.plugins.register.service.PersonService
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
personConfig
person
personController
personDao
personService
此時,結果資訊中輸出了使用@Repository、@Service、@Controller註解標註的元件名稱,分別為:personDao、personService和personController。
好了,我們們今天就聊到這兒吧!別忘了給個在看和轉發,讓更多的人看到,一起學習一起進步!!
專案工程原始碼已經提交到GitHub:https://github.com/sunshinelyz/spring-annotation
寫在最後
如果覺得文章對你有點幫助,請微信搜尋並關注「 冰河技術 」微信公眾號,跟冰河學習Spring註解驅動開發。公眾號回覆“spring註解”關鍵字,領取Spring註解驅動開發核心知識圖,讓Spring註解驅動開發不再迷茫。