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