效果类似于
spring:
cloud:
gateway:
routes:
- id: my-service-path
uri: lb://my-service
filters:
- RedirectTo=302, https://acme.org
在yml文件中配置自定义的filter
步骤1:寻找线索
通过IDEA的CTRL+左键单击 发现spring. cloud. gateway.routes指向一个类
@ConfigurationProperties("spring.cloud.gateway")
@Validated
public class GatewayProperties
然后,继续CTRL+左键单击发现GatewayProperties被一个类所依赖,即
public class RouteDefinitionRouteLocator
implements RouteLocator, BeanFactoryAware, ApplicationEventPublisherAware
然后稍微粗糙的浏览一下里面的方法,属性。发现loadGatewayFilters方法非常符合我们要找的线索:
List<GatewayFilter> loadGatewayFilters(String id,List<FilterDefinition> filterDefinitions) {
ArrayList<GatewayFilter> ordered = new ArrayList<>(filterDefinitions.size());
for (int i = 0; i < filterDefinitions.size(); i++) {
FilterDefinition definition = filterDefinitions.get(i);
GatewayFilterFactory factory = this.gatewayFilterFactories.get(definition.getName());
...
Map<String, Object> properties = factory.shortcutType().normalize(args,factory, this.parser, this.beanFactory);
Object configuration = factory.newConfig();
ConfigurationUtils.bind(configuration, properties,factory.shortcutFieldPrefix(), definition.getName(), validator);
GatewayFilter gatewayFilter = factory.apply(configuration);
...
}
return ordered;
}
@Validated
public class FilterDefinition {
@NotNull
private String name;
private Map<String, String> args = new LinkedHashMap<>();
......
}
仔细观察这个方法,发现有一句代码容易引起注意
GatewayFilterFactory factory = this.gatewayFilterFactories.get(definition.getName());
属性是一个HashMap:
private final Map<String, GatewayFilterFactory> gatewayFilterFactories = new HashMap<>();
可以大胆的猜测,这个属性是过滤器名字和过滤器工厂的映射。
步骤二:通过线索,寻找已有实现
通过类似的类进行分析,首先随便找一个观察。在官方文档中找到一个合适的类,例如
public class RedirectToGatewayFilterFactory
extends AbstractGatewayFilterFactory<RedirectToGatewayFilterFactory.Config>
含有一个内部类
public static class Config {
String status;
String url;
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}
根据官网的示例,大胆猜测:
spring:
cloud:
gateway:
routes:
- id: prefixpath_route
uri: https://example.org
filters:
- RedirectTo=302, https://acme.org
对于示例中的过滤器RedirectToGatewayFilter,是通过某种方式得到的name=RedirectTo,args=302, acme.org
默认内部类Config定义属性的顺序就是args字符串按,分割后的数组顺序,也可以重写AbstractGatewayFilterFactory的
@Override
public List<String> shortcutFieldOrder()
步骤三:FilterDefinition的Name是怎么来的?
根据线索回溯到属性
private final Map<String, GatewayFilterFactory> gatewayFilterFactories = new HashMap<>();
看它是怎么put进去就知道了。利用CTRL+左键寻找代码调用该属性,最终发现
public final class NameUtils {
public static String normalizeFilterFactoryName(
Class<? extends GatewayFilterFactory> clazz) {
return removeGarbage(clazz.getSimpleName()
.replace(GatewayFilterFactory.class.getSimpleName(), ""));
}
}
答案就是,通过类名进行字符串切割,所以我们起名字的时候要起成XXXGatewayFilterFactory
步骤四:自定义GatewayFilterFactory
@Component
@Slf4j
public class LogHeaderGatewayFilterFactory extends AbstractGatewayFilterFactory {
public LogHeaderGatewayFilterFactory() {
super();
}
@Override
public GatewayFilter apply(Object config) {
return (exchange, chain) -> {
ServerHttpRequest request = exchange.getRequest();
HttpHeaders headers = request.getHeaders();
headers.entrySet().forEach(entry -> {
log.info("request header name:{},values{}",entry.getKey(),entry.getValue().toString());
});
return chain.filter(exchange);
};
}
}
自定义GatewayFilterFactory报类型转换异常,无法将Object转换成内部类?
步骤一:分析异常堆栈
分析异常堆栈,把控制台打印的堆栈顶开始分析,定位问题。发现:
GatewayFilter gatewayFilter = factory.apply(configuration);
是这个对象configuration出现了类型转换异常。溯源,找到创建对象的代码,
Object configuration = factory.newConfig();
断点打上,发现初始化时调用
BeanUtils.instantiateClass(this.configClass);
而debugger中限制this.configClass确实是Object类型,当然就不能强转
步骤二:寻找错误代码
一直CTRL+左键单击溯源configClass属性是怎么初始化的,最终发现是
public AbstractGatewayFilterFactory() {
super((Class<C>) Object.class);
}
发现这个就是我们自定义GatewayFilterFactory的父类,重写这个方法,类似于
public RedirectToGatewayFilterFactory() {
super(Config.class);
}