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

 

posted @ 2022-09-12 14:23  1156740846  阅读(398)  评论(0编辑  收藏  举报