天天看點

【Spring注解驅動開發】自定義TypeFilter指定@ComponentScan注解的過濾規則

【Spring注解驅動開發】自定義TypeFilter指定@ComponentScan注解的過濾規則

寫在前面

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類(接口)或其子類(實作類或子接口)的元件,如下所示。

@Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {PersonService.class})           

此時,隻要是PersonService類型的元件,都會被加載到容器中。也就是說:當PersonService是一個Java類時,Person類及其子類都會被加載到Spring容器中;當PersonService是一個接口時,其子接口或實作類都會被加載到Spring容器中。

(3)ASPECTJ:按照ASPECTJ表達式進行過濾

例如,使用@ComponentScan注解進行包掃描時,按照ASPECTJ表達式進行過濾,如下所示。

@Filter(type = FilterType.ASPECTJ, classes = {AspectJTypeFilter.class})           

(4)REGEX:按照正規表達式進行過濾

例如,使用@ComponentScan注解進行包掃描時,按照正規表達式進行過濾,如下所示。

@Filter(type = FilterType.REGEX, classes = {RegexPatternTypeFilter.class})           

(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注解進行如下配置。

@Filter(type = FilterType.CUSTOM, classes = {MyTypeFilter.class})           

在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 自定義過濾規則

    */

/**
 * 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()方法進行測試,輸出的結果資訊如下所示。

personController

personDao

personService

此時,結果資訊中輸出了使用@Repository、@Service、@Controller注解标注的元件名稱,分别為:personDao、personService和personController。

好了,咱們今天就聊到這兒吧!别忘了給個在看和轉發,讓更多的人看到,一起學習一起進步!!

寫在最後

如果覺得文章對你有點幫助,請微信搜尋并關注「 冰河技術 」微信公衆号,跟冰河學習Spring注解驅動開發。公衆号回複“spring注解”關鍵字,領取Spring注解驅動開發核心知識圖,讓Spring注解驅動開發不再迷茫。

原文位址

https://www.cnblogs.com/binghe001/p/13063046.html