• java 设计模式责任链模式应用(二)


    责任链模式

    概念就不必细说,该模式主要特点就是像链接式流一样,一步一步执行。

    可以应用的例子,比如用来验证用户信息,用户登录后,判断是否用户等级,用户禁用,用户信息等内容。

    不止可以用来验证用户信息,还有很多验证,打算做个框架jar包,来体现在spring boot上。

    核心关键点:

    java 无法反射直接根据一个接口获取所有的实现类,需要指定扫描包的路径才可以,所以类似mybatis plus需要scan注解填入basePackage。

    需要借用reflections工具包来反射获取扫描包,需要结合guava一起用,版本要对应上。

    <dependency>
        <groupId>com.google.guava</groupId>
        <artifactId>guava</artifactId>
        <version>21.0</version>
    </dependency>
    
    <dependency>
        <groupId>org.reflections</groupId>
        <artifactId>reflections</artifactId>
        <version>0.9.11</version>
    </dependency>

    pom.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.2.5.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>com.qt</groupId>
        <artifactId>duty-pattern-app</artifactId>
        <version>1.0-SNAPSHOT</version>
        <packaging>jar</packaging>
    
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>1.2.44</version>
            </dependency>
    
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.18.12</version>
            </dependency>
    
            <dependency>
                <groupId>com.google.guava</groupId>
                <artifactId>guava</artifactId>
                <version>21.0</version>
            </dependency>
    
            <dependency>
                <groupId>org.reflections</groupId>
                <artifactId>reflections</artifactId>
                <version>0.9.11</version>
            </dependency>
        </dependencies>
    
    
    </project>

    定义一个接口IValidator,主要操作验证。

    public interface IValidator {
        /**
         * 验证
         * @param req
         * @param validator
         * @param arg
         * @param <T>
         * @param <E>
         */
        <T extends BaseValidatorDto, E extends IValidator> void doValidate(T req, IValidator validator, Class<E> arg);
    }

    再定义一个IBaseValidator继承IValidator,来添加其他功能,如是否可以验证,顺序。

    public interface IBaseValidator extends IValidator {
        /**
         * 顺序
         * @return
         */
        int getOrder();
    
        /**
         * 是否验证
         * @return
         */
        boolean isValidate();
    }

    关键类:ValidatorChain

    它的作用就是先获取所有的执行器,然后依次用来分配到哪个对象需要执行。

    下面的代码中init初始化中根据Reflections工具获取接口所有实现的类,然后根据接口来分类存入map。

    @Data
    public class ValidatorScannerConfigurer {
        private String basePackage;
    }
    public final class ValidatorChain implements IValidator {
        @Autowired(required = false)
        private List<IBaseValidator> validatorList;
    
        private Map<Class<? extends IBaseValidator>, List<IBaseValidator>> validatorMap;
    
        @Autowired
        private ApplicationContext applicationContext;
    
        @Autowired(required = false)
        private ValidatorScannerConfigurer scannerConfigurer;
    
        @PostConstruct
        public void init() {
            if (!CollectionUtils.isEmpty(validatorList)) {
                validatorList = validatorList.stream().filter(x -> x.isValidate()).sorted(Comparator.comparing(IBaseValidator::getOrder)).collect(Collectors.toList());
            }
            validatorMap = new HashMap<>();
            String[] packages = Optional.ofNullable(scannerConfigurer)
                    .map(x -> Optional.ofNullable(scannerConfigurer.getBasePackage())
                            .map(m -> m.split(",")).orElse(null)).orElse(null);
            if (packages == null || packages.length == 0) {
                return;
            }
            Reflections reflections = new Reflections(new ConfigurationBuilder()
                    .forPackages(packages) // 指定扫描包路径
                    .addScanners(new SubTypesScanner()) // 添加子类扫描工具
                    .addScanners(new FieldAnnotationsScanner()) // 添加 属性注解扫描工具
                    .addScanners(new MethodAnnotationsScanner()) // 添加 方法注解扫描工具
                    .addScanners(new MethodParameterScanner()) // 添加方法参数扫描工具
            );
            Set<Class<? extends IBaseValidator>> subValidators = reflections.getSubTypesOf(IBaseValidator.class);
            //获取接口的类型
            subValidators.stream().filter(x -> x.isInterface()).collect(Collectors.toSet()).forEach(x -> {
                Map<String, ? extends IBaseValidator> beansOfType = applicationContext.getBeansOfType(x);
                List<IBaseValidator> validators = beansOfType.entrySet().stream().map(m -> m.getValue()).collect(Collectors.toList());
                validatorMap.put(x, validators);
            });
    
        }
    
        /**
         * 验证
         *
         * @param req
         * @param validator
         * @param arg
         */
        @Override
        public <T extends BaseValidatorDto, E extends IValidator> void doValidate(T req, IValidator validator, Class<E> arg) {
            List<IBaseValidator> baseValidators = validatorMap.get(arg);
            if (req.getIndex() == baseValidators.size()) {
                return;
            }
            System.out.println("当前位置:" + req.getIndex() + "... 对应类型:" + arg.getName());
            IBaseValidator baseValidator = baseValidators.get(req.getIndex());
            req.setIndex(req.getIndex() + 1);
            baseValidator.doValidate(req, validator, arg);
        }
    }

    ValidatorScannerConfigurer的设置

    之前说了,扫描包需要指定包名,所以我参照mybatis plus的模式来实现自定义注解。关键点@Import来实现手动将ValidatorScannerConfigurer注入到容器中。

    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.TYPE})
    @Documented
    @Import(ValidatorScannerRegistrar.class)
    public @interface ValidatorScan {
        String[] value() default {};
    
        String[] basePackages() default {};
    }

    注入容器中有3种方式,可参照https://blog.csdn.net/tuoni123/article/details/80213050

    • 直接注入
    • 实现 ImportBeanDefinitionRegistrar 接口 注入
    • 实现 ImportSelector 注入

    对比功能实用,我选择用ImportBeanDefinitionRegistrar来实现。registerBeanDefinition方法用来注入容器

    public class ValidatorScannerRegistrar implements ImportBeanDefinitionRegistrar {
        /**
         * Register bean definitions as necessary based on the given annotation metadata of
         * the importing {@code @Configuration} class.
         * <p>Note that {@link BeanDefinitionRegistryPostProcessor} types may <em>not</em> be
         * registered here, due to lifecycle constraints related to {@code @Configuration}
         * class processing.
         *
         * @param importingClassMetadata annotation metadata of the importing class
         * @param registry               current bean definition registry
         */
        @Override
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
            AnnotationAttributes validatorScanAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(ValidatorScan.class.getName()));
            if (validatorScanAttrs != null) {
                this.registerBeanDefinitions(importingClassMetadata, validatorScanAttrs, registry, generateBaseBeanName(importingClassMetadata, 0));
            }
        }
    
        void registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes annoAttrs, BeanDefinitionRegistry registry, String beanName) {
            BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(ValidatorScannerConfigurer.class);
            List<String> basePackages = new ArrayList();
            basePackages.addAll(Arrays.stream(annoAttrs.getStringArray("value")).filter(StringUtils::hasText).collect(Collectors.toList()));
            basePackages.addAll(Arrays.stream(annoAttrs.getStringArray("basePackages")).filter(StringUtils::hasText).collect(Collectors.toList()));
            builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(basePackages));
            registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
        }
    
        private static String generateBaseBeanName(AnnotationMetadata importingClassMetadata, int index) {
            return importingClassMetadata.getClassName() + "#" + ValidatorScannerRegistrar.class.getSimpleName() + "#" + index;
        }
    }

    配置依赖注入

    @Configuration
    public class ValidatorBeanConfig {
    
        @Bean
        public ValidatorChain validatorChain() {
            return new ValidatorChain();
        }
    
        @Bean
        public IValidatorService validatorService() {
            return new ValidatorServiceImpl();
        }
    }

    客户端暴露使用接口

    public interface IValidatorService {
        /**
         * 验证
         *
         * @param req
         * @param arg
         * @param <T>
         * @param <E>
         */
        <T extends BaseValidatorDto, E extends IValidator> void doValidate(T req, Class<E> arg);
    }

    实现方式如下,其中设置index为0,防止被修改

    public final class ValidatorServiceImpl implements IValidatorService {
        @Qualifier("validatorChain")
        @Autowired
        private IValidator validator;
    
        /**
         * 验证
         *
         * @param req
         * @param arg
         */
        @Override
        public <T extends BaseValidatorDto, E extends IValidator> void doValidate(T req, Class<E> arg) {
            req.setIndex(0);
            validator.doValidate(req, validator, arg);
        }
    }

    使用方法

    1.添加接口继承IBaseValidator

    public interface ILoginValidator extends IBaseValidator {
    }

    2.实现类

    @Service
    public class LoginNameValidator implements ILoginValidator {
    
        @PostConstruct
        public void init(){
            System.out.println("LoginNameValidator init");
        }
    
        @Override
        public int getOrder() {
            return 0;
        }
    
        @Override
        public boolean isValidate() {
            return true;
        }
    
        @Override
        public <T extends BaseValidatorDto, E> void doValidate(T req, IValidator validator, Class<E> arg) {
            System.out.println("login name validator");
            validator.doValidate(req, validator, arg);
        }
    }

    添加扫描包

    @Configuration
    @ValidatorScan({"com.validator"})
    public class ValidatorConfig {
    }

    客户端调用IValidatorService

    @Service
    public class LoginServiceImpl implements LoginService {
        @Autowired
        private IValidatorService validatorService;
    
        @Override
        public void loginAction() {
            LoginValidatorDto dto = new LoginValidatorDto();
            dto.setUserName("wangsio");
            validatorService.doValidate(dto, ILoginValidator.class);
        }
    }
  • 相关阅读:
    Java实现寻找最小的k个数
    Java实现寻找最小的k个数
    foruok安晓辉的《程序员,你好哇》,都很不错
    DataSnap的如果网络断线,如何恢复?
    配置QSslConfiguration让客户端程序跳过本地SSL验证
    Linux升级OpenSSL版本
    FMX+Win32,窗口无法保持原样,应该是个bug
    [Nhibernate]二级缓存
    EventBus(事件总线)
    elasticsearch集群搭建实例
  • 原文地址:https://www.cnblogs.com/zjtao/p/13778782.html
Copyright © 2020-2023  润新知