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