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

posted @ 2020-09-15 14:13  天宇轩-王  阅读(873)  评论(0编辑  收藏  举报