160
fizz-plugin/plugin-core.md
Executable file
160
fizz-plugin/plugin-core.md
Executable file
@@ -0,0 +1,160 @@
|
|||||||
|
---
|
||||||
|
home: false
|
||||||
|
title: plugin core
|
||||||
|
---
|
||||||
|
|
||||||
|
## 主要封装说明
|
||||||
|
|
||||||
|
- 1、引入核心包后,不影响以前编写的插件,只是提供更加便捷的开发方式;
|
||||||
|
- 2、插件名字编写规范。在保留原有编写方式的前提下,强制实现 pluginName() 方法,对开发更加友好,减少失误;
|
||||||
|
- 3、配置获取更加容易。配置主要有3个:路由配置、插件全局配置、插件在路由里的个性配置。现在都可以直接获取相应的配置实体对象,而不是默认提供的 Map 或者 String ;
|
||||||
|
|
||||||
|
## 使用说明
|
||||||
|
|
||||||
|
核心包是用于开发插件的基础包。主要是简化操作,方便开发,使开发人员更专注于业务代码对编写。
|
||||||
|
|
||||||
|
**1、编写2个配置实体类:插件在路由里的个性配置、插件全局配置,并添加注解 @FizzConfig**
|
||||||
|
|
||||||
|
> @FizzConfig 参数说明:
|
||||||
|
>
|
||||||
|
> contentParser :配置内容解析器。选填,默认是 json 解析器 JsonParser 。也可以自定义解析器,只需实现 we.plugin.core.filter.config.ContentParser 接口
|
||||||
|
|
||||||
|
注意:默认解析器 JsonParser 的 parseRouterCfg 方法只对第一层的 json string 做了增强,但这也足够用了。如(注意 varJson 是个 json 字符串,并不是 json 对象):
|
||||||
|
```groovy
|
||||||
|
void parseRouterCfg() {
|
||||||
|
String varJson = "{\n" +
|
||||||
|
" \"var1\": \"var1\",\n" +
|
||||||
|
" \"var2\": \"var2\",\n" +
|
||||||
|
" \"var3\": \"var3\"\n" +
|
||||||
|
"}";
|
||||||
|
// String varJson = "";
|
||||||
|
// String varJson = null;
|
||||||
|
Map<String, String> config = Maps.newHashMap();
|
||||||
|
config.put("codeSource", "this is code source");
|
||||||
|
config.put("var", varJson);
|
||||||
|
RouterConfig routerConfig = parser.parseRouterCfg(config, RouterConfig.class);
|
||||||
|
System.out.println(routerConfig);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
示例:
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Data
|
||||||
|
@FizzConfig
|
||||||
|
public class PluginConfig {
|
||||||
|
private String id;
|
||||||
|
private Var var;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@FizzConfig
|
||||||
|
public class RouterConfig {
|
||||||
|
private String codeSource;
|
||||||
|
private Var var;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class Var {
|
||||||
|
private String var1;
|
||||||
|
private Integer var2;
|
||||||
|
private Long var3;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**3、编写插件逻辑**
|
||||||
|
继承 we.plugin.core.filter.AbstractFizzPlugin ,并实现 pluginName 和 doFilter 方法
|
||||||
|
|
||||||
|
> pluginName 方法:获取插件名称。无参,返回插件名称,要与网关后台配置的插件名称一致
|
||||||
|
>
|
||||||
|
> doFilter 方法:插件主要逻辑方法。入参是 ServerWebExchange,出参是 Mono<Void>
|
||||||
|
|
||||||
|
直接调用父类方法获取各种配置:
|
||||||
|
> routerConfig:获取路由级别插件配置
|
||||||
|
>
|
||||||
|
> pluginConfig:获取插件级别插件配置
|
||||||
|
>
|
||||||
|
> originRouterCfg:获取原始路由级别插件配置
|
||||||
|
>
|
||||||
|
> originPluginCfg:获取原始插件级别插件配置
|
||||||
|
>
|
||||||
|
> apiConfig:获取路由配置
|
||||||
|
|
||||||
|
示例:
|
||||||
|
|
||||||
|
```java
|
||||||
|
package we.fizz.plugin.example.plugin;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.server.ServerWebExchange;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
import we.plugin.core.filter.AbstractFizzPlugin;
|
||||||
|
import we.plugin.core.filter.config.FizzConfig;
|
||||||
|
import we.plugin.auth.ApiConfig;
|
||||||
|
import we.util.WebUtils;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static we.fizz.plugin.example.plugin.ExamplePlugin.PluginConfig;
|
||||||
|
import static we.fizz.plugin.example.plugin.ExamplePlugin.RouterConfig;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
public class ExamplePlugin extends AbstractFizzPlugin<RouterConfig, PluginConfig> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 插件名称
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String pluginName() {
|
||||||
|
return "examplePlugin";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* filter逻辑
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Mono<Void> doFilter(ServerWebExchange exchange) {
|
||||||
|
RouterConfig routerConfig = routerConfig(exchange);
|
||||||
|
PluginConfig pluginConfig = pluginConfig(exchange);
|
||||||
|
Map<String, Object> originRouterCfg = originRouterCfg(exchange);
|
||||||
|
String originPluginCfg = originPluginCfg(exchange);
|
||||||
|
ApiConfig apiConfig = apiConfig(exchange);
|
||||||
|
if (log.isTraceEnabled()) {
|
||||||
|
log.trace("routerConfig : {}", routerConfig);
|
||||||
|
log.trace("pluginConfig : {}", pluginConfig);
|
||||||
|
log.trace("originRouterCfg : {}", originRouterCfg);
|
||||||
|
log.trace("originPluginCfg : {}", originPluginCfg);
|
||||||
|
log.trace("apiConfig : {}", apiConfig);
|
||||||
|
}
|
||||||
|
return WebUtils.buildDirectResponse(exchange, HttpStatus.OK, null, "success");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@FizzConfig
|
||||||
|
public static class PluginConfig {
|
||||||
|
private String id;
|
||||||
|
private Var var;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class Var {
|
||||||
|
private String var1;
|
||||||
|
private String var2;
|
||||||
|
private String var3;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@FizzConfig
|
||||||
|
public static class RouterConfig {
|
||||||
|
private String codeSource;
|
||||||
|
private Var var;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package we.plugin.core.config;
|
||||||
|
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.ComponentScan;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import we.plugin.core.filter.config.parser.JsonParser;
|
||||||
|
import we.plugin.core.spring.FizzPluginAliasProcessor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author huanghua
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
@ComponentScan({"we.config", "we.fizz", "we.plugin", "we.filter", "we.proxy", "we.stats"/*, "we.plugin.core"*/})
|
||||||
|
public class WeFizzPluginAutoConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public FizzPluginAliasProcessor fizzPluginAliasProcess(ApplicationContext context) {
|
||||||
|
return new FizzPluginAliasProcessor(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public JsonParser jsonParser() {
|
||||||
|
return new JsonParser();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,144 @@
|
|||||||
|
package we.plugin.core.filter;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.core.annotation.AnnotationUtils;
|
||||||
|
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||||
|
import org.springframework.web.server.ServerWebExchange;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
import we.plugin.FizzPluginFilter;
|
||||||
|
import we.plugin.PluginConfig;
|
||||||
|
import we.plugin.auth.ApiConfig;
|
||||||
|
import we.plugin.auth.ApiConfigService;
|
||||||
|
import we.plugin.core.filter.config.ContentParser;
|
||||||
|
import we.plugin.core.filter.config.FizzConfig;
|
||||||
|
import we.plugin.core.filter.config.parser.JsonParser;
|
||||||
|
import we.plugin.core.spring.util.FizzPluginAppContextUtils;
|
||||||
|
import we.util.WebUtils;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import java.lang.reflect.ParameterizedType;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author huanghua
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public abstract class AbstractFizzPlugin<RouterCfg, PluginCfg> implements FizzPluginFilter {
|
||||||
|
// api 配置
|
||||||
|
public final Function<String, String> nameExApiCfg = in -> "fizz.pl.api.cfg";
|
||||||
|
// 路由上的插件配置(原始)
|
||||||
|
public final Function<String, String> nameExRtCfg = in -> "fizz.pl.rt.cfg." + pluginName();
|
||||||
|
// 插件级别配置(原始)
|
||||||
|
public final Function<String, String> nameExPlCfg = in -> "fizz.pl.pl.cfg." + pluginName();
|
||||||
|
// 路由上的插件配置(解析后)
|
||||||
|
public final Function<String, String> nameExRtCfgParsed = in -> "fizz.pl.rt.cfg.parsed." + pluginName();
|
||||||
|
// 插件级别配置(解析后)
|
||||||
|
public final Function<String, String> nameExPlCfgParsed = in -> "fizz.pl.pl.cfg.parsed." + pluginName();
|
||||||
|
@Resource
|
||||||
|
private ApiConfigService apiConfigService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<Void> filter(ServerWebExchange exchange, Map<String, Object> config) {
|
||||||
|
if (log.isTraceEnabled()) {
|
||||||
|
log.trace("{} doFilter execute...", this.pluginName());
|
||||||
|
}
|
||||||
|
initConfig(exchange, config);
|
||||||
|
return this.doFilter(exchange);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取路由级别插件配置
|
||||||
|
*/
|
||||||
|
public RouterCfg routerConfig(ServerWebExchange exchange) {
|
||||||
|
if (originRouterCfg(exchange) == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
RouterCfg routerCfgInAttr = exchange.getAttribute(nameExRtCfgParsed.apply(pluginName()));
|
||||||
|
if (routerCfgInAttr != null) {
|
||||||
|
return routerCfgInAttr;
|
||||||
|
}
|
||||||
|
Class<RouterCfg> cfgClass = (Class<RouterCfg>) ((ParameterizedType) getClass().getGenericSuperclass())
|
||||||
|
.getActualTypeArguments()[0];
|
||||||
|
FizzConfig fizzConfig = AnnotationUtils.findAnnotation(cfgClass, FizzConfig.class);
|
||||||
|
Class<? extends ContentParser> cfgParser = fizzConfig == null ? JsonParser.class : fizzConfig.contentParser();
|
||||||
|
RouterCfg routerCfg = FizzPluginAppContextUtils.getBean(cfgParser).parseRouterCfg(originRouterCfg(exchange), cfgClass);
|
||||||
|
putAttr2exchange(exchange, nameExRtCfgParsed.apply(pluginName()), routerCfg);
|
||||||
|
return routerCfg;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取插件级别插件配置
|
||||||
|
*/
|
||||||
|
public PluginCfg pluginConfig(ServerWebExchange exchange) {
|
||||||
|
if (originPluginCfg(exchange) == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
PluginCfg pluginCfgInAttr = exchange.getAttribute(nameExPlCfgParsed.apply(pluginName()));
|
||||||
|
if (pluginCfgInAttr != null) {
|
||||||
|
return pluginCfgInAttr;
|
||||||
|
}
|
||||||
|
Class<PluginCfg> cfgClass = (Class<PluginCfg>) ((ParameterizedType) getClass().getGenericSuperclass())
|
||||||
|
.getActualTypeArguments()[1];
|
||||||
|
FizzConfig fizzConfig = AnnotationUtils.findAnnotation(cfgClass, FizzConfig.class);
|
||||||
|
Class<? extends ContentParser> cfgParser = fizzConfig == null ? JsonParser.class : fizzConfig.contentParser();
|
||||||
|
PluginCfg pluginCfg = FizzPluginAppContextUtils.getBean(cfgParser).parsePluginCfg(originPluginCfg(exchange), cfgClass);
|
||||||
|
putAttr2exchange(exchange, nameExPlCfgParsed.apply(pluginName()), pluginCfg);
|
||||||
|
return pluginCfg;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取原始路由级别插件配置
|
||||||
|
*/
|
||||||
|
public <T> T originRouterCfg(ServerWebExchange exchange) {
|
||||||
|
return exchange.getAttribute(nameExRtCfg.apply(pluginName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取原始插件级别插件配置
|
||||||
|
*/
|
||||||
|
public <T> T originPluginCfg(ServerWebExchange exchange) {
|
||||||
|
return exchange.getAttribute(nameExPlCfg.apply(pluginName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取路由配置
|
||||||
|
*/
|
||||||
|
public ApiConfig apiConfig(ServerWebExchange exchange) {
|
||||||
|
return exchange.getAttribute(nameExApiCfg.apply(pluginName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void putAttr2exchange(ServerWebExchange exchange, String key, Object val) {
|
||||||
|
if (exchange == null || key == null || val == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
exchange.getAttributes().put(key, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initConfig(ServerWebExchange exchange, Map<String, Object> config) {
|
||||||
|
ServerHttpRequest req = exchange.getRequest();
|
||||||
|
ApiConfig apiConfig = apiConfigService.getApiConfig(WebUtils.getAppId(exchange),
|
||||||
|
WebUtils.getClientService(exchange), req.getMethod(), WebUtils.getClientReqPath(exchange));
|
||||||
|
String fixedConfig = (String) config.get(PluginConfig.CUSTOM_CONFIG);
|
||||||
|
if (log.isTraceEnabled()) {
|
||||||
|
log.trace("api config : {}", apiConfig);
|
||||||
|
log.trace("router config : {}", config);
|
||||||
|
log.trace("plugin config : {}", fixedConfig);
|
||||||
|
}
|
||||||
|
putAttr2exchange(exchange, nameExApiCfg.apply(pluginName()), apiConfig);
|
||||||
|
putAttr2exchange(exchange, nameExRtCfg.apply(pluginName()), config);
|
||||||
|
putAttr2exchange(exchange, nameExPlCfg.apply(pluginName()), fixedConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 插件名称
|
||||||
|
*/
|
||||||
|
public abstract String pluginName();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* filter逻辑
|
||||||
|
*/
|
||||||
|
public abstract Mono<Void> doFilter(ServerWebExchange exchange);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package we.plugin.core.filter.config;
|
||||||
|
|
||||||
|
import com.google.common.base.CharMatcher;
|
||||||
|
import com.google.common.base.Splitter;
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author huanghua
|
||||||
|
*/
|
||||||
|
public abstract class ConfigUtils {
|
||||||
|
public static final String DEFAULT_CHAR_MATCHER_ANY_OF = ",\n";
|
||||||
|
|
||||||
|
public static Set<String> string2set(String strVal, String charMatcherAnyOf) {
|
||||||
|
Set<String> finalSet = Sets.newHashSet();
|
||||||
|
if (StringUtils.isBlank(strVal)) {
|
||||||
|
return finalSet;
|
||||||
|
}
|
||||||
|
charMatcherAnyOf = StringUtils.isBlank(charMatcherAnyOf) ? DEFAULT_CHAR_MATCHER_ANY_OF : charMatcherAnyOf;
|
||||||
|
Set<String> set = Sets.newHashSet(
|
||||||
|
Splitter.on(CharMatcher.anyOf(charMatcherAnyOf)).trimResults().split(strVal));
|
||||||
|
set = set.stream().filter(StringUtils::isNotBlank).collect(Collectors.toSet());
|
||||||
|
for (String s : set) {
|
||||||
|
if (StringUtils.isBlank(s)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
finalSet.add(StringUtils.trimToEmpty(s));
|
||||||
|
}
|
||||||
|
return finalSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Set<String> string2set(String strVal) {
|
||||||
|
return string2set(strVal, DEFAULT_CHAR_MATCHER_ANY_OF);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package we.plugin.core.filter.config;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author huanghua
|
||||||
|
*/
|
||||||
|
public interface ContentParser {
|
||||||
|
|
||||||
|
default <T> T parseRouterCfg(Map<String, Object> config, Class<T> toValueType) {
|
||||||
|
throw new RuntimeException();
|
||||||
|
}
|
||||||
|
|
||||||
|
default <T> T parsePluginCfg(String source, Class<T> toValueType) {
|
||||||
|
throw new RuntimeException();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package we.plugin.core.filter.config;
|
||||||
|
|
||||||
|
import we.plugin.core.filter.config.parser.JsonParser;
|
||||||
|
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
|
import static java.lang.annotation.ElementType.TYPE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author huanghua
|
||||||
|
*/
|
||||||
|
@Target(TYPE)
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Documented
|
||||||
|
@Inherited
|
||||||
|
public @interface FizzConfig {
|
||||||
|
/**
|
||||||
|
* 配置内容解析器
|
||||||
|
*/
|
||||||
|
Class<? extends ContentParser> contentParser() default JsonParser.class;
|
||||||
|
}
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
package we.plugin.core.filter.config.parser;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import we.plugin.core.filter.config.ContentParser;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author huanghua
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class JsonParser implements ContentParser {
|
||||||
|
private static final Set<Class<?>> IGNORE_CONVERT_CLASS = Sets.newHashSet(
|
||||||
|
String.class
|
||||||
|
, Long.class
|
||||||
|
, Integer.class
|
||||||
|
, Double.class
|
||||||
|
, Short.class
|
||||||
|
, CharSequence.class
|
||||||
|
, Character.class
|
||||||
|
, BigDecimal.class
|
||||||
|
, Boolean.class
|
||||||
|
);
|
||||||
|
private static final Map<Class<?>, List<Field>> FIELD_CACHE = Maps.newHashMap();
|
||||||
|
@Resource
|
||||||
|
private ObjectMapper objectMapper;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> T parseRouterCfg(Map<String, Object> config, Class<T> toValueType) {
|
||||||
|
ObjectNode jsonNode = objectMapper.convertValue(config, ObjectNode.class);
|
||||||
|
convertConfig(jsonNode, toValueType);
|
||||||
|
return objectMapper.convertValue(jsonNode, toValueType);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> T parsePluginCfg(String source, Class<T> toValueType) {
|
||||||
|
try {
|
||||||
|
return objectMapper.readValue(source, toValueType);
|
||||||
|
} catch (JsonProcessingException e) {
|
||||||
|
log.warn(e.getMessage(), e);
|
||||||
|
throw new RuntimeException(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void convertConfig(ObjectNode jsonNode, Class<?> toValueType) {
|
||||||
|
List<Field> cacheFields = fields(toValueType);
|
||||||
|
for (Field field : cacheFields) {
|
||||||
|
String fn = field.getName();
|
||||||
|
if (jsonNode.has(fn) && !IGNORE_CONVERT_CLASS.contains(field.getType())) {
|
||||||
|
List<Field> fs = fields(field.getType());
|
||||||
|
if (fs.size() > 0) {
|
||||||
|
JsonNode node = jsonNode.get(fn);
|
||||||
|
JsonNode readTree = null;
|
||||||
|
try {
|
||||||
|
readTree = objectMapper.readTree(node.asText());
|
||||||
|
} catch (JsonProcessingException e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
if (readTree != null) {
|
||||||
|
jsonNode.put(fn, readTree);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Field> fields(Class<?> toValueType) {
|
||||||
|
List<Field> cacheFields = FIELD_CACHE.get(toValueType);
|
||||||
|
if (cacheFields == null) {
|
||||||
|
// returns all members including private members but not inherited members.
|
||||||
|
Field[] fields = toValueType.getDeclaredFields();
|
||||||
|
cacheFields = Lists.newArrayList(fields);
|
||||||
|
FIELD_CACHE.put(toValueType, cacheFields);
|
||||||
|
}
|
||||||
|
return cacheFields;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
package we.plugin.core.spring;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.config.BeanDefinition;
|
||||||
|
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||||
|
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
|
import org.springframework.context.support.GenericApplicationContext;
|
||||||
|
import org.springframework.util.CollectionUtils;
|
||||||
|
import we.plugin.core.filter.AbstractFizzPlugin;
|
||||||
|
|
||||||
|
import javax.annotation.PostConstruct;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author huanghua
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class FizzPluginAliasProcessor {
|
||||||
|
private ApplicationContext applicationContext;
|
||||||
|
|
||||||
|
public FizzPluginAliasProcessor(ApplicationContext applicationContext) {
|
||||||
|
this.applicationContext = applicationContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
public void postProcessPluginAlias() {
|
||||||
|
Map<String, AbstractFizzPlugin> serviceBeanMap = applicationContext.getBeansOfType(AbstractFizzPlugin.class);
|
||||||
|
if (CollectionUtils.isEmpty(serviceBeanMap)) {
|
||||||
|
log.debug("not found fizz plugin. skip!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!(applicationContext instanceof GenericApplicationContext)) {
|
||||||
|
log.error("ApplicationContext is not instance of GenericApplicationContext. skip!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
serviceBeanMap.forEach((s, o) -> registerAlias(
|
||||||
|
((GenericApplicationContext) applicationContext).getBeanFactory(), s, o));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void registerAlias(ConfigurableListableBeanFactory beanFactory,
|
||||||
|
String beanName, AbstractFizzPlugin fizzPlugin) {
|
||||||
|
log.debug("register bean : {}", fizzPlugin.getClass().getName());
|
||||||
|
BeanDefinition bd = beanFactory.getBeanDefinition(beanName);
|
||||||
|
if (bd instanceof AbstractBeanDefinition) {
|
||||||
|
AbstractBeanDefinition abd = (AbstractBeanDefinition) bd;
|
||||||
|
abd.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_AUTODETECT);
|
||||||
|
}
|
||||||
|
beanFactory.registerAlias(beanName, fizzPlugin.pluginName());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package we.plugin.core.spring.util;
|
||||||
|
|
||||||
|
import org.springframework.beans.BeansException;
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
|
import org.springframework.context.ApplicationContextAware;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author huanghua
|
||||||
|
* @deprecated and use {@link we.Fizz} instead
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class FizzPluginAppContextUtils implements ApplicationContextAware {
|
||||||
|
private static ApplicationContext applicationContext;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setApplicationContext(ApplicationContext appContext) throws BeansException {
|
||||||
|
applicationContext = appContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> T getBean(Class<T> requiredType) throws BeansException {
|
||||||
|
return FizzPluginAppContextUtils.getApplicationContext().getBean(requiredType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ApplicationContext getApplicationContext() {
|
||||||
|
if (applicationContext == null) {
|
||||||
|
String msg = "The applicationContext is not yet available. "
|
||||||
|
+ "Please ensure that the spring applicationContext is completely created before calling this method!";
|
||||||
|
throw new IllegalStateException(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
return applicationContext;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
package we.plugin.myplugin;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.server.ServerWebExchange;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
import we.plugin.auth.ApiConfig;
|
||||||
|
import we.plugin.core.filter.AbstractFizzPlugin;
|
||||||
|
import we.plugin.core.filter.config.FizzConfig;
|
||||||
|
import we.util.WebUtils;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
public class MyPluginPlus extends AbstractFizzPlugin<MyPluginPlus.RouterConfig, MyPluginPlus.PluginConfig> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 插件名称
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String pluginName() {
|
||||||
|
return "myPluginPlus";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* filter逻辑
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Mono<Void> doFilter(ServerWebExchange exchange) {
|
||||||
|
RouterConfig routerConfig = routerConfig(exchange);
|
||||||
|
PluginConfig pluginConfig = pluginConfig(exchange);
|
||||||
|
Map<String, Object> originRouterCfg = originRouterCfg(exchange);
|
||||||
|
String originPluginCfg = originPluginCfg(exchange);
|
||||||
|
ApiConfig apiConfig = apiConfig(exchange);
|
||||||
|
if (log.isTraceEnabled()) {
|
||||||
|
log.trace("routerConfig : {}", routerConfig);
|
||||||
|
log.trace("pluginConfig : {}", pluginConfig);
|
||||||
|
log.trace("originRouterCfg : {}", originRouterCfg);
|
||||||
|
log.trace("originPluginCfg : {}", originPluginCfg);
|
||||||
|
log.trace("apiConfig : {}", apiConfig);
|
||||||
|
}
|
||||||
|
return WebUtils.buildDirectResponse(exchange, HttpStatus.OK, null, "success");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@FizzConfig
|
||||||
|
public static class PluginConfig {
|
||||||
|
private String id;
|
||||||
|
private PluginItem pluginItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class PluginItem {
|
||||||
|
private String p1;
|
||||||
|
private String p2;
|
||||||
|
private String p3;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@FizzConfig
|
||||||
|
public static class RouterConfig {
|
||||||
|
private String codeSource;
|
||||||
|
private RouterItem routerItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class RouterItem {
|
||||||
|
private String r1;
|
||||||
|
private String r2;
|
||||||
|
private String r3;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
3
fizz-plugin/src/main/resources/META-INF/spring.factories
Normal file
3
fizz-plugin/src/main/resources/META-INF/spring.factories
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Auto Configure
|
||||||
|
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||||
|
we.plugin.core.config.WeFizzPluginAutoConfiguration
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
package we.plugin.core.filter.config.parser;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import we.plugin.core.filter.config.FizzConfig;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
|
||||||
|
public class JsonParserTest {
|
||||||
|
|
||||||
|
private static JsonParser parser = new JsonParser();
|
||||||
|
|
||||||
|
@BeforeAll
|
||||||
|
public static void init() throws NoSuchFieldException, IllegalAccessException {
|
||||||
|
Class<?> clazz = parser.getClass();
|
||||||
|
Field field = clazz.getDeclaredField("objectMapper");
|
||||||
|
field.setAccessible(true);
|
||||||
|
field.set(parser, new ObjectMapper());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void parseRouterCfg() {
|
||||||
|
String varJson = "{\n" +
|
||||||
|
" \"var1\": \"var1\",\n" +
|
||||||
|
" \"var2\": \"var2\",\n" +
|
||||||
|
" \"var3\": \"var3\"\n" +
|
||||||
|
"}";
|
||||||
|
// String varJson = "";
|
||||||
|
// String varJson = null;
|
||||||
|
Map<String, Object> config = Maps.newHashMap();
|
||||||
|
config.put("codeSource", "this is code source");
|
||||||
|
config.put("var", varJson);
|
||||||
|
RouterConfig routerConfig = parser.parseRouterCfg(config, RouterConfig.class);
|
||||||
|
assertNotNull(routerConfig, "未解析出routerConfig");
|
||||||
|
assertNotNull( routerConfig.getVar(), "未解析出routerConfig.var");
|
||||||
|
assertEquals("var1", routerConfig.getVar().getVar1(), "routerConfig.var.var1不匹配");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void parsePluginCfg() {
|
||||||
|
String json = "{\n" +
|
||||||
|
" \"id\": \"123\",\n" +
|
||||||
|
" \"var\": {\n" +
|
||||||
|
" \"var1\": \"var1\",\n" +
|
||||||
|
" \"var2\": \"var2\",\n" +
|
||||||
|
" \"var3\": \"var3\"\n" +
|
||||||
|
" }\n" +
|
||||||
|
"}";
|
||||||
|
PluginConfig config = parser.parsePluginCfg(json, PluginConfig.class);
|
||||||
|
assertNotNull(config, "未解析出config");
|
||||||
|
assertEquals("123", config.getId(), "id不匹配");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@FizzConfig
|
||||||
|
public static class PluginConfig {
|
||||||
|
private String id;
|
||||||
|
private Var var;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class Var {
|
||||||
|
private String var1;
|
||||||
|
private String var2;
|
||||||
|
private String var3;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@FizzConfig
|
||||||
|
public static class RouterConfig {
|
||||||
|
private String codeSource;
|
||||||
|
private Var var;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user