Spring自定義TypeFilter


Spring自定義TypeFilter

1. FilterType枚舉

public enum FilterType {

	/**
	 * Filter candidates marked with a given annotation.
	 * @see org.springframework.core.type.filter.AnnotationTypeFilter
	 */
	ANNOTATION,

	/**
	 * Filter candidates assignable to a given type.
	 * @see org.springframework.core.type.filter.AssignableTypeFilter
	 */
	ASSIGNABLE_TYPE,

	/**
	 * Filter candidates matching a given AspectJ type pattern expression.
	 * @see org.springframework.core.type.filter.AspectJTypeFilter
	 */
	ASPECTJ,

	/**
	 * Filter candidates matching a given regex pattern.
	 * @see org.springframework.core.type.filter.RegexPatternTypeFilter
	 */
	REGEX,

	/** Filter candidates using a given custom
	 * {@link org.springframework.core.type.filter.TypeFilter} implementation.
	 */
	CUSTOM

}

2.TypeFilter

@FunctionalInterface
public interface TypeFilter {

	/**
	 * Determine whether this filter matches for the class described by
	 * the given metadata.
	 * @param metadataReader the metadata reader for the target class
	 * @param metadataReaderFactory a factory for obtaining metadata readers
	 * for other classes (such as superclasses and interfaces)
	 * @return whether this filter matches
	 * @throws IOException in case of I/O failure when reading metadata
	 */
    /**
     * 此方法返回一個boolean類型的值。
     * 當返回true時,表示加入到spring的容器中。返回false時,不加入容器。
     * 參數metadataReader:表示讀取到的當前正在掃描的類的信息
     * 參數metadataReaderFactory:表示可以獲得到其他任何類的信息
     */
	boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
			throws IOException;

}

3.自定義過濾

場景:
  在實際開發中,有很多下面這種業務場景:一個業務需求根據環境的不同可能會有很多種實現。針對不同
的環境,要加載不同的實現。我們看下面這個案例:
我們現在是一個汽車銷售集團,在成立之初,只是在北京銷售汽車,我們的項目研發完成后只在北京部署
上線。但隨着公司的業務發展,現在全國各地均有銷售大區,總部設在北京。各大區有獨立的項目部署,但是
每個大區的業績計算和績效提成的計算方式並不相同。
例如:
  在華北區銷售一台豪華級轎車績效算5,提成銷售額1%,銷售豪華級SUV績效算3,提成是0.5%。
  在西南區銷售一台豪華級轎車績效算3,提成銷售額0.5%,銷售豪華級SUV績效算5,提成是1.5%。
這時,我們如果針對不同大區對項目源碼進行刪減替換,會帶來很多不必要的麻煩。而如果加入一些
if/else的判斷,顯然過於簡單粗暴。此時應該考慮采用橋接設計模式,把將涉及到區域性差異的模塊功能單
獨抽取到代表區域功能的接口中。針對不同區域進行實現。並且在掃描組件注冊到容器中時,采用哪個區域的
具體實現,應該采用配置文件配置起來。而自定義TypeFilter就可以實現注冊指定區域的組件到容器中。

代碼實現

/**
 * @author WGR
 * @create 2020/9/15 -- 13:23
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface District {

    /**
       * 指定區域的名稱
       * @return
       */
    String value() default "";
}

接口:

/**
 * @author WGR
 * @create 2020/9/15 -- 13:26
 */
public interface DistrictPercentage {

    /**
     * 不同車型的提成
     * @param carType
     */
    void salePercentage(String carType);
}

/**
 * @author WGR
 * @create 2020/9/15 -- 13:27
 */
public interface DistrictPerformance {

    /**
       * 計算績效
       * @param carType
       */
     void calcPerformance(String carType);
}

實現:


/**
 * @author WGR
 * @create 2020/9/15 -- 13:28
 */
@Component("districtPercentage")
@District("north")
public class NorthDistrictPercentage implements DistrictPercentage {

    @Override
    public void salePercentage(String carType) {
        if ("SUV".equalsIgnoreCase(carType)) {
            System.out.println("華北區" + carType + "提成1%");
        } else if ("car".equalsIgnoreCase(carType)) {
            System.out.println("華北區" + carType + "提成0.5%");
        }
    }
}
/**
 * @author WGR
 * @create 2020/9/15 -- 13:36
 */
@Component("districtPerformance")
@District("north")
public class NorthDistrictPerformance implements DistrictPerformance {
    @Override
    public void calcPerformance(String carType) {
        if ("SUV".equalsIgnoreCase(carType)) {
            System.out.println("華北區" + carType + "績效3");
        } else if ("car".equalsIgnoreCase(carType)) {
            System.out.println("華北區" + carType + "績效5");
        }
    }
}
/**
 * @author WGR
 * @create 2020/9/15 -- 13:34
 */
@Component("districtPercentage")
@District("southwest")
public class SouthwestDistrictPercentage implements DistrictPercentage {

    @Override
    public void salePercentage(String carType) {
        if ("SUV".equalsIgnoreCase(carType)) {
            System.out.println("區" + carType + "提成1.5%");
        } else if ("car".equalsIgnoreCase(carType)) {
            System.out.println("華北區" + carType + "提成0.5%");
        }
    }
}
/**
 * @author WGR
 * @create 2020/9/15 -- 13:37
 */
@Component("districtPerformance")
@District("southwest")
public class SouthwestDistrictPerformance implements DistrictPerformance {
    @Override
    public void calcPerformance(String carType) {
        if ("SUV".equalsIgnoreCase(carType)) {
            System.out.println("西南區" + carType + "績效5");
        } else if ("car".equalsIgnoreCase(carType)) {
            System.out.println("西南區" + carType + "績效3");
        }
    }
}

過濾類:

/**
 * @author WGR
 * @create 2020/9/15 -- 13:44
 */
public class DistrictTypeFilter extends AbstractTypeHierarchyTraversingFilter {
    protected DistrictTypeFilter(boolean considerInherited, boolean considerInterfaces) {
        super(considerInherited, considerInterfaces);
    }

    //定義路徑校驗類對象
    private PathMatcher pathMatcher;
    //注意:使用@Value注解的方式是獲取不到配置值的。
    //因為Spring的生命周期里,負責填充屬性值的InstantiationAwareBeanPostProcessor 與TypeFilter的實例化過程壓根搭不上邊。
    // @Value("${common.district.name}")
    private String districtName;

    /**
     * 默認構造函數
     */
    public DistrictTypeFilter() {
        //1.第一個參數:不考慮基類。2.第二個參數:不考慮接口上的信息
        super(false, false);
        //借助Spring默認的Resource通配符路徑方式
        pathMatcher = new AntPathMatcher();
       //硬編碼讀取配置信息
        try {
            Properties loadAllProperties =
                    PropertiesLoaderUtils.loadAllProperties("district.properties");
            districtName =
                    loadAllProperties.getProperty("common.district.name");
        } catch (IOException e) {
           // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    //注意本類將注冊為Exclude, 返回true代表拒絕
    @Override
    protected boolean matchClassName(String className) {
        try {
            if (!isPotentialPackageClass(className)) {
                return false;
            }
// 判斷當前區域是否和所配置的區域一致, 不一致則阻止載入Spring容器
            Class<?> clazz = ClassUtils.forName(className,
                    DistrictTypeFilter.class.getClassLoader());
            District districtAnnotation = clazz.getAnnotation(District.class);
            if (null == districtAnnotation) {
                return false;
            }
            final String districtValue = districtAnnotation.value();
            return (!districtName.equalsIgnoreCase(districtValue));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    // 潛在的滿足條件的類的類名, 指定package下
    private static final String PATTERN_STANDARD = ClassUtils.convertClassNameToResourcePath("com.dalianpai.spring5.typefilter.*");

    // 本類邏輯中可以處理的類 -- 指定package下的才會進行邏輯判斷,
    private boolean isPotentialPackageClass(String className) {
    // 將類名轉換為資源路徑, 以進行匹配測試
        final String path = ClassUtils.convertClassNameToResourcePath(className);
        System.out.println(path);
        System.out.println(PATTERN_STANDARD);
        return pathMatcher.match(PATTERN_STANDARD, path);
    }

}

測試類:

/**
 * @author WGR
 * @create 2020/9/15 -- 13:51
 */
public class SpringAnnotationTypeFilterTest {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
        DistrictPerformance districtPerformance = ac.getBean("districtPerformance", DistrictPerformance.class);
        districtPerformance.calcPerformance("SUV");
        DistrictPercentage districtPercentage = ac.getBean("districtPercentage", DistrictPercentage.class);
        districtPercentage.salePercentage("car");
    }
}

image-20200915141156755


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM