zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    迭代器在LinkedList上的删除
    java多线程:CopyOnWriteArrayList
    vs中代码编译通过,但还是有红色波浪线
    vs中项目属性配置
    TortoiseGit安装与配置
    DC(device context)
    weak_ptr 使用
    C++ 中shared_ptr循环引用计数问题
    for_each与lambda表达式联合使用
    new 和 make_shared 在内存上的区别
  • 原文地址:https://www.cnblogs.com/dalianpai/p/13672781.html
Copyright © 2011-2022 走看看