我们在使用 spring cloud
的时候,在使用微服务 openfeign
调用的时候;一般都要写 fallback
的降级逻辑,但是在实现这一块基本都是统一的逻辑,要么是统一抛出异常;要么返回空值;这样的逻辑比较单一,但是又必须要实现,写起来比较麻烦。
这里我们不引用 openfeign
的依赖,自己写一个jar包,模拟 openfeign
的定义;如下图:
首先是 FeignClient
注解,模拟 openfeign
的注解:
package com.feign.api;
import java.lang.annotation.*;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FeignClient {
String value() default "";
Class<?> fallbackFactory() default void.class;
}
然后是 FallbackFactory.java
,模拟 openfeign
的 FallbackFactory
的降级策略:
package com.feign.api;
public interface FallbackFactory<T> {
T create(Throwable var1);
}
最后是 ProcessException.java
,定义所有的降级的方法抛出异常:
package com.feign.api;
public class ProcessException extends RuntimeException {
public ProcessException(String msg) {
super(msg);
}
}
首先我们我们要引入 tools.jar
,此包在我们安装 java 目录的 lib/tools.jar
,如下:
<dependency>
<groupId>com.sungroupId>
<artifactId>toolsartifactId>
<version>1.8version>
<scope>systemscope>
<systemPath>JAVA_HOME/lib/tools.jarsystemPath>
dependency>
其次定义 FallbackClassInfo.java
,用于存储要生成的 FallbackFactory
类结构:
package com.feign.fallback.auto;
import java.util.List;
public class FallbackClassInfo {
/**
* 所在包名
*/
private String pg;
/**
* 需要引入的包
*/
private List<String> ipts;
/**
* 创建的类名称
*/
private String className;
/**
* 泛型的名称
*/
private String genericName;
/**
* 方法列表
*/
private List<String> methods;
// Getter and Setter
然后完成 AutoFallbackProcessor
工具,如下:
package com.feign.fallback.auto;
import com.feign.api.FallbackFactory;
import com.feign.api.FeignClient;
import com.sun.source.util.TreePath;
import com.sun.tools.javac.api.JavacTrees;
import com.sun.tools.javac.processing.JavacProcessingEnvironment;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.tree.TreeTranslator;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.Names;
import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.tools.JavaFileObject;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
@SupportedAnnotationTypes(value = {"com.feign.api.FeignClient"})
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class AutoFallbackProcessor extends AbstractProcessor {
/**
* 文件系统
*/
private Filer filer;
/**
* java AST 编译时代码树
*/
private JavacTrees javacTrees;
/**
* 对 java AST 操作的工具
*/
private TreeMaker treeMaker;
/**
* 符号封装类,统一处理名称
*/
private Names names;
/**
* 打印信息
*/
private Messager messager;
/**
* 初始化编译时操作对象
* @param processingEnv
*/
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
javacTrees = JavacTrees.instance(processingEnv);
final Context context = ((JavacProcessingEnvironment) processingEnv).getContext();
treeMaker = TreeMaker.instance(context);
names = Names.instance(context);
messager = processingEnv.getMessager();
filer = processingEnv.getFiler();
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
final Set<? extends Element> dataAnnotations = roundEnv.getElementsAnnotatedWith(FeignClient.class);
for (Element element : dataAnnotations) {
FeignClient feignClient = element.getAnnotation(FeignClient.class);
String feignClientString = feignClient.toString();
if (feignClientString.contains("fallbackFactory=void")) {
addAnnotationProperty(element);
}
}
return true;
}
/**
* 为注解添加 fallbackFactory 属性
* @param element 节点
*/
public void addAnnotationProperty(Element element) {
// 创建类
FallbackClassInfo classInfo = resolving(element);
createSourceClass(classInfo);
// 创建引入
TreePath treePath = javacTrees.getPath(element);
JCTree.JCCompilationUnit jccu = (JCTree.JCCompilationUnit) treePath.getCompilationUnit();
JCTree.JCImport anImport = treeMaker.Import(
treeMaker.Select(
treeMaker.Ident(names.fromString(classInfo.getPg())),
names.fromString(classInfo.getClassName())
), false);
jccu.defs = jccu.defs.prepend(anImport);
// 重构注解
jccu.accept(new TreeTranslator() {
@Override
public void visitAnnotation(JCTree.JCAnnotation jcAnnotation) {
super.visitAnnotation(jcAnnotation);
jcAnnotation.args = jcAnnotation.args.append(
treeMaker.Assign(
treeMaker.Ident(names.fromString("fallbackFactory")),
treeMaker.Select(
treeMaker.Ident(names.fromString(classInfo.getClassName())),
names.fromString("class")
)
)
);
}
});
}
/**
* 生成 fallbackFactory 类
* @param classInfo FallbackFactory 类配置信息
*/
public void createSourceClass(FallbackClassInfo classInfo) {
StringBuilder builder = new StringBuilder()
.append("package " + classInfo.getPg() + ";\n\n");
for (String ipt : classInfo.getIpts()) {
builder.append("import " + ipt + ";\n");
}
builder.append("\n")
.append("public class " + classInfo.getClassName() + " implements FallbackFactory<" + classInfo.getGenericName() + "> { \n\n")
.append("\tpublic " + classInfo.getGenericName() + " create(Throwable var1) {\n")
.append("\t\treturn new " + classInfo.getGenericName() + "() {\n");
for (String method : classInfo.getMethods()) {
builder.append(method + "\n");
}
builder.append("};\n")
.append("\t}\n")
.append("}\n");
try {
JavaFileObject source = filer.createSourceFile(classInfo.getPg() + "." + classInfo.getClassName());
Writer writer = source.openWriter();
writer.write(builder.toString());
writer.flush();
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public FallbackClassInfo resolving(Element element) {
TreePath treePath = javacTrees.getPath(element);
JCTree.JCCompilationUnit jccu = (JCTree.JCCompilationUnit) treePath.getCompilationUnit();
String className = element.getSimpleName().toString();
String packageName = element.toString().replaceAll("." + className, "");
FallbackClassInfo classInfo = new FallbackClassInfo();
classInfo.setPg(packageName + ".fallback");
classInfo.setClassName(className + "FallbackFactory");
classInfo.setGenericName(className);
List<String> ipts = new ArrayList<>();
classInfo.setIpts(ipts);
List<String> methods = new ArrayList<>();
classInfo.setMethods(methods);
// 格式化引入包名
ipts.add("com.feign.api.FallbackFactory");
ipts.add("com.feign.api.ProcessException");
ipts.add(packageName + "." + className);
JCTree.JCClassDecl classDecl = null;
for (JCTree pTree : jccu.defs) {
if (pTree instanceof JCTree.JCImport) {
ipts.add(((JCTree.JCImport) pTree).getQualifiedIdentifier().toString());
} else if (pTree instanceof JCTree.JCClassDecl){
classDecl = (JCTree.JCClassDecl) pTree;
}
}
if (classDecl == null) {
return null;
}
for (JCTree cTree : classDecl.defs) {
if (!(cTree instanceof JCTree.JCMethodDecl)) {
continue;
}
JCTree.JCMethodDecl methodDecl = (JCTree.JCMethodDecl) cTree;
Set<String> set = methodDecl.getModifiers().getFlags().stream().map(Modifier::toString).collect(Collectors.toSet());
if(set.contains("static") || set.contains("default")) {
continue;
}
String method = "\t\t\tpublic " + methodDecl.getReturnType() + " " + methodDecl.getName() + "(" + methodDecl.getParameters() + ") {\n"
+ "\t\t\t\tthrow new ProcessException(\"系统异常~~\");\n"
+ "\t\t\t}\n";
methods.add(method);
}
return classInfo;
}
}
最后记得 resource
文件夹下的 META-INFO/services/javax.annotation.processing.Processor
的 SPI
文件:
com.feign.fallback.auto.AutoFallbackProcessor
然后,我们创建一个新的项目,引入上面两个依赖;然后定义一个接口,然后添加上 FeignClient
注解,不加 fallbackFactory
属性,如下:
然后进行编译,得到如下结果: