springboot插件模式
自定义一个validator
package com.runachina.sc.designer.domain.validator; import com.runachina.sc.designer.domain.validator.annotations.IconExist; import com.runachina.sc.designer.domain.validator.utils.IconExistDataStore; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; /** * lcx * 校验icon的引用是否存在 - icon存在性校验 */ public class IconExistValidator implements ConstraintValidator<IconExist, String> { /** * initialize 在注解设置参数时会调用,如果没有设置参数,只调用一次默认的初始化 * @IconExist 只调用一次 initialize,默认的初始化 * @IconExist(message = "123") 每设置一次调用一次,多个对象实例的校验也只会调用一次 */ @Override public void initialize(IconExist iconExist) { ConstraintValidator.super.initialize(iconExist); } @Override public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) { if (value == null || value.trim().length() == 0) { return true; } return IconExistDataStore.iconCodeExist(value); } }
package com.runachina.sc.designer.domain.validator.annotations; import com.runachina.sc.designer.domain.validator.IconExistValidator; import javax.validation.Constraint; import javax.validation.Payload; import java.lang.annotation.*; import static java.lang.annotation.RetentionPolicy.RUNTIME; @Target({ ElementType.FIELD}) @Retention(RUNTIME) @Constraint(validatedBy = IconExistValidator.class) public @interface IconExist { String message() default "图标不存在"; Class<?>[] groups() default { }; Class<? extends Payload>[] payload() default { }; }
自定义的validator需要查库,判断数据是否存在,每次查库的效率比较低,这里采用一次性全部查出来,放到线程变量中,为了保证线程数据一定会被清空,写了一个filter
package com.runachina.sc.designer.filter; import com.runachina.sc.designer.domain.validator.utils.IconExistDataStore; import org.springframework.stereotype.Component; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import java.io.IOException; @WebFilter(filterName = "baseFilter", urlPatterns = {"/*"}) @Component public class BaseFilter implements Filter { @Override public void init(FilterConfig filterConfig) { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { filterChain.doFilter(servletRequest, servletResponse); IconExistDataStore.removeThreadData(); } @Override public void destroy() { } }
说实话,我自己也觉得这么做不好,因为不好维护,谁也不会知道这里会有这样一行代码,作为这样一个功能。
而插件式开发,就可以用插件的方式维护好自己的功能,不牵扯任何其他地方
https://www.cnblogs.com/strongmore/p/15248730.html
<dependency> <groupId>org.springframework.plugin</groupId> <artifactId>spring-plugin-core</artifactId> <version>2.0.0.RELEASE</version> </dependency>
package com.runachina.sc.designer.plugin; import com.runachina.sc.designer.core.event.hooks.AppSchemaOperateContext; import com.runachina.sc.designer.core.event.hooks.AppSchemaOperateFilter; import com.runachina.sc.designer.core.event.hooks.AppSchemaOperateType; import com.runachina.sc.designer.domain.validator.utils.IconExistDataStore; import com.runachina.sc.material.client.feign.IconClient; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import java.util.List; @Slf4j @AllArgsConstructor @Component public class IconExistValidationPlugin implements AppSchemaOperateFilter { private final IconClient iconClient; @Override public boolean supports(AppSchemaOperateType delimiter) { return AppSchemaOperateType.VALIDATE.equals(delimiter); } @Override public void onBeforeOperate(AppSchemaOperateContext context) { List<String> list = iconClient.getAllIconCode(); IconExistDataStore.setIconCodeList(list); } @Override public void onAfterOperate(AppSchemaOperateContext context) { IconExistDataStore.removeThreadData(); } }
public List<ValidateProblem> validateApp(ApplicationAggregate aggregate) { // 校验 AppSchemaOperateContext context = AppSchemaOperateContext.validate(Collections.singletonList(aggregate), designerPluginManager); List<ValidateProblem> problems = context.getValidateProblems(aggregate.getBaseInfo().getCode()); return problems; }
package com.runachina.sc.designer.core.event.hooks; import com.runachina.cloud.starter.base.session.RunaCloudSession; import com.runachina.sc.designer.core.configuration.DesignerPluginManager; import com.runachina.sc.designer.core.entity.AppVersionEntity; import com.runachina.sc.designer.core.entity.ReleaseLogEntity; import com.runachina.sc.designer.core.validate.ValidateProblem; import com.runachina.sc.designer.core.validate.ValidateUtil; import com.runachina.sc.designer.domain.aggregate.ApplicationAggregate; import com.runachina.sc.designer.domain.enums.event.release.ReleaseStatus; import lombok.Getter; import org.springframework.util.CollectionUtils; import java.util.*; public class AppSchemaOperateContext { /** * Current Session */ @Getter private final RunaCloudSession session; /** * App schemas to operate! */ private final Map<String, ApplicationAggregate> appSchemas = new HashMap<>(); /** * Key:AppCode * Value:APP validate problems */ private final Map<String, List<ValidateProblem>> validateProblems = new HashMap<>(); /** * 应用保存记录 * Key: APpCode * Value: ReleaseLogEntity */ private Map<String, AppVersionEntity> saveLogs = new HashMap<>(); /** * 应用发布记录 * Key: APpCode * Value: ReleaseLogEntity */ private Map<String, ReleaseLogEntity> releaseLogs = new HashMap<>(); public AppSchemaOperateContext(RunaCloudSession session) { this.session = session; } public static AppSchemaOperateContext validate(List<ApplicationAggregate> appSchemas, DesignerPluginManager plugins) { AppSchemaOperateContext context = new AppSchemaOperateContext(RunaCloudSession.get()); context.setAppSchemas(appSchemas); plugins.getOperateFilters().getPluginsFor(AppSchemaOperateType.VALIDATE).forEach(hoos -> hoos.onBeforeOperate(context)); for (ApplicationAggregate appSchema : appSchemas) { context.setValidateProblems(appSchema.getCode(), ValidateUtil.getAppProblems(appSchema)); } plugins.getOperateFilters().getPluginsFor(AppSchemaOperateType.VALIDATE).forEach(hoos -> hoos.onAfterOperate(context)); return context; } public Collection<ApplicationAggregate> getAppSchemas(){ return appSchemas.values(); } public void setAppSchemas(List<ApplicationAggregate> apps){ apps.forEach(app -> appSchemas.put(app.getCode(), app)); } public void setValidateProblems(String appCode, List<ValidateProblem> appProblems) { validateProblems.put(appCode, appProblems); if(saveLogs.containsKey(appCode)){ saveLogs.get(appCode).setProblems(appProblems); } } public void addValidateProblems(String appCode, List<ValidateProblem> problems) { List<ValidateProblem> oldProblems = validateProblems.get(appCode); if(CollectionUtils.isEmpty(oldProblems)){ setValidateProblems(appCode, problems); } else { oldProblems.addAll(problems); setValidateProblems(appCode, oldProblems); } } public List<ValidateProblem> getValidateProblems(String appCode) { return validateProblems.get(appCode); } public ReleaseLogEntity addReleaseLog(ApplicationAggregate application) { //保存发布记录 ReleaseLogEntity releaseLog = new ReleaseLogEntity(); releaseLog.setId(UUID.randomUUID().toString()); releaseLog.setAppCode(application.getCode()); releaseLog.setAppName(application.getBaseInfo().getName()); releaseLog.setAppVersion(application.getBaseInfo().getVersion()); releaseLog.setStatus(ReleaseStatus.ON_RELEASE); releaseLogs.put(application.getCode(), releaseLog); return releaseLog; } public AppVersionEntity addSaveLog(ApplicationAggregate application) { // 保存版本记录 AppVersionEntity appVersion = new AppVersionEntity(); appVersion.setAppVersion(application.getBaseInfo().getVersion()); appVersion.setAppCode(application.getCode()); appVersion.setAppName(application.getBaseInfo().getName()); appVersion.setProblems(validateProblems.get(application.getCode())); saveLogs.put(application.getCode(), appVersion); return appVersion; } public Collection<AppVersionEntity> getSaveLogs() { return saveLogs.values(); } public AppVersionEntity getSaveLog(String appCode) { return saveLogs.get(appCode); } public ReleaseLogEntity getReleaseLog(String appCode) { return releaseLogs.get(appCode); } public ApplicationAggregate getAppSchema(String appCode) { return appSchemas.get(appCode); } }
package com.runachina.sc.designer.core.configuration; import com.runachina.sc.designer.core.event.hooks.AppEventListener; import com.runachina.sc.designer.core.event.hooks.AppEventType; import com.runachina.sc.designer.core.event.hooks.AppSchemaOperateFilter; import com.runachina.sc.designer.core.event.hooks.AppSchemaOperateType; import com.runachina.sc.designer.core.domain.EntityDesigner; import com.runachina.sc.designer.core.repository.SettingsRepository; import com.runachina.sc.designer.core.entity.SettingsEntity; import com.runachina.sc.designer.domain.model.EntityType; import lombok.AllArgsConstructor; import lombok.Getter; import org.springframework.boot.CommandLineRunner; import org.springframework.context.annotation.Configuration; import org.springframework.plugin.core.PluginRegistry; import org.springframework.plugin.core.config.EnablePluginRegistries; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; @Configuration @EnablePluginRegistries({AppSchemaOperateFilter.class, AppEventListener.class, EntityDesigner.class}) @AllArgsConstructor @Getter public class DesignerPluginManager implements CommandLineRunner { private final PluginRegistry<AppSchemaOperateFilter, AppSchemaOperateType> operateFilters; private final PluginRegistry<AppEventListener, AppEventType> eventListeners; private final PluginRegistry<EntityDesigner, String> entityDesigners; private final SettingsRepository settingsRepository; public List<AppSchemaOperateFilter> getOperateFilters(AppSchemaOperateType operateType) { return operateFilters.getPluginsFor(operateType); } public List<AppEventListener> getEventListeners(AppEventType eventType) { return eventListeners.getPluginsFor(eventType); } @Override public void run(String... args) { List<SettingsEntity> settingsItems = settingsRepository.findAll(); if (settingsItems.isEmpty()) { settingsItems = new ArrayList<>(); settingsItems.add(newSettingsEntity()); } settingsItems.stream().map(settings -> appendEntityTypeConfig(settings)).collect(Collectors.toSet()); settingsRepository.saveAll(settingsItems); } private SettingsEntity newSettingsEntity() { SettingsEntity settings = new SettingsEntity(); return settings; } private SettingsEntity appendEntityTypeConfig(SettingsEntity settings) { final List<EntityType> entityTypes = settings.getEntityTypes() == null ? new ArrayList<>() : settings.getEntityTypes(); List<EntityType> newTypes = entityDesigners.getPlugins().stream().map(p -> p.getTypeConfig()).collect(Collectors.toList()); entityTypes.removeIf(t -> newTypes.stream().anyMatch(nt -> nt.getCode().equals(t.getCode()))); entityTypes.addAll(newTypes); settings.setEntityTypes(entityTypes); return settings; } }