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);
    }
}

 

posted @ 2020-10-08 18:19  归去如风  阅读(174)  评论(0编辑  收藏  举报