|
@@ -1,7 +1,7 @@
|
|
|
package com.chelvc.framework.feign.circuitbreaker;
|
|
|
|
|
|
+import java.util.Arrays;
|
|
|
import java.util.Collection;
|
|
|
-import java.util.Collections;
|
|
|
import java.util.Enumeration;
|
|
|
import java.util.Iterator;
|
|
|
import java.util.Map;
|
|
@@ -9,8 +9,8 @@ import java.util.Set;
|
|
|
|
|
|
import com.chelvc.framework.common.util.ObjectUtils;
|
|
|
import com.chelvc.framework.common.util.StringUtils;
|
|
|
-import com.google.common.collect.Sets;
|
|
|
import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;
|
|
|
+import javassist.CannotCompileException;
|
|
|
import javassist.ClassPool;
|
|
|
import javassist.CtClass;
|
|
|
import javassist.CtMethod;
|
|
@@ -22,10 +22,10 @@ import javassist.bytecode.ConstPool;
|
|
|
import javassist.bytecode.MethodInfo;
|
|
|
import javassist.bytecode.annotation.Annotation;
|
|
|
import javassist.bytecode.annotation.StringMemberValue;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
|
|
import org.springframework.boot.SpringApplication;
|
|
|
import org.springframework.boot.SpringApplicationRunListener;
|
|
|
-import org.springframework.cloud.openfeign.EnableFeignClients;
|
|
|
import org.springframework.context.ConfigurableApplicationContext;
|
|
|
import org.springframework.core.type.AnnotationMetadata;
|
|
|
import org.springframework.util.ClassUtils;
|
|
@@ -36,6 +36,7 @@ import org.springframework.util.ClassUtils;
|
|
|
* @author Woody
|
|
|
* @date 2025/4/14
|
|
|
*/
|
|
|
+@Slf4j
|
|
|
public class FeignCircuitBreakerInitializer implements SpringApplicationRunListener {
|
|
|
/**
|
|
|
* 是否已初始化
|
|
@@ -43,20 +44,13 @@ public class FeignCircuitBreakerInitializer implements SpringApplicationRunListe
|
|
|
private static volatile boolean INITIALIZED;
|
|
|
|
|
|
/**
|
|
|
- * 服务名称集合
|
|
|
+ * 服务熔断器注解
|
|
|
*/
|
|
|
- private static volatile Set<String> SERVERS;
|
|
|
-
|
|
|
- private final boolean enabled;
|
|
|
+ private static FeignCircuitBreaker MAIN_BREAKER;
|
|
|
|
|
|
public FeignCircuitBreakerInitializer(SpringApplication application, String[] args) {
|
|
|
Class<?> main = application.getMainApplicationClass();
|
|
|
- this.enabled = main != null && main.isAnnotationPresent(EnableFeignClients.class)
|
|
|
- && main.isAnnotationPresent(EnableFeignCircuitBreaker.class);
|
|
|
- if (this.enabled && !INITIALIZED) {
|
|
|
- EnableFeignCircuitBreaker configuration = main.getAnnotation(EnableFeignCircuitBreaker.class);
|
|
|
- SERVERS = ObjectUtils.ifEmpty(configuration.servers(), Sets::newHashSet, Collections::emptySet);
|
|
|
- }
|
|
|
+ MAIN_BREAKER = main.getAnnotation(FeignCircuitBreaker.class);
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -156,6 +150,21 @@ public class FeignCircuitBreakerInitializer implements SpringApplicationRunListe
|
|
|
return CtNewMethod.make(body, clazz);
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 查找接口@EnableFeignCircuitBreaker注解
|
|
|
+ *
|
|
|
+ * @param method 接口方法
|
|
|
+ * @return @EnableFeignCircuitBreaker注解实例
|
|
|
+ * @throws Exception 操作异常
|
|
|
+ */
|
|
|
+ private static FeignCircuitBreaker findFeignCircuitBreaker(CtMethod method) throws Exception {
|
|
|
+ Object breaker = method.getAnnotation(FeignCircuitBreaker.class);
|
|
|
+ if (breaker == null) {
|
|
|
+ breaker = method.getDeclaringClass().getAnnotation(FeignCircuitBreaker.class);
|
|
|
+ }
|
|
|
+ return breaker == null ? MAIN_BREAKER : (FeignCircuitBreaker) breaker;
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* 初始化Feign调用方法@CircuitBreaker注解
|
|
|
*
|
|
@@ -196,7 +205,8 @@ public class FeignCircuitBreakerInitializer implements SpringApplicationRunListe
|
|
|
|
|
|
// 忽略不需要熔断服务调用
|
|
|
String server = getFeignClientName(attributes);
|
|
|
- if (StringUtils.isEmpty(server) || (ObjectUtils.notEmpty(SERVERS) && !SERVERS.contains(server))) {
|
|
|
+ if (MAIN_BREAKER != null && ObjectUtils.notEmpty(MAIN_BREAKER.servers())
|
|
|
+ && !Arrays.asList(MAIN_BREAKER.servers()).contains(server)) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
@@ -208,7 +218,9 @@ public class FeignCircuitBreakerInitializer implements SpringApplicationRunListe
|
|
|
CtMethod[] methods = clazz.getDeclaredMethods();
|
|
|
if (ObjectUtils.notEmpty(methods)) {
|
|
|
for (CtMethod method : methods) {
|
|
|
- if (Modifier.isAbstract(method.getModifiers()) && !method.hasAnnotation(CircuitBreaker.class)) {
|
|
|
+ FeignCircuitBreaker breaker = findFeignCircuitBreaker(method);
|
|
|
+ if (Modifier.isAbstract(method.getModifiers()) && !method.hasAnnotation(CircuitBreaker.class)
|
|
|
+ && breaker != null && breaker.enabled()) {
|
|
|
initializeFeignCircuitBreaker(server, method);
|
|
|
bound = true;
|
|
|
}
|
|
@@ -217,6 +229,8 @@ public class FeignCircuitBreakerInitializer implements SpringApplicationRunListe
|
|
|
if (bound) {
|
|
|
clazz.toClass();
|
|
|
}
|
|
|
+ } catch (CannotCompileException e) {
|
|
|
+ log.warn("Feign circuit breaker initialize failed: {}", e.getMessage());
|
|
|
} catch (Exception e) {
|
|
|
throw new RuntimeException(e);
|
|
|
}
|
|
@@ -224,19 +238,21 @@ public class FeignCircuitBreakerInitializer implements SpringApplicationRunListe
|
|
|
|
|
|
@Override
|
|
|
public void contextLoaded(ConfigurableApplicationContext context) {
|
|
|
- if (this.enabled && !INITIALIZED) {
|
|
|
- synchronized (FeignCircuitBreakerInitializer.class) {
|
|
|
- if (!INITIALIZED) {
|
|
|
- INITIALIZED = true;
|
|
|
-
|
|
|
- ClassPool pool = ClassPool.getDefault();
|
|
|
- pool.appendClassPath(new LoaderClassPath(Thread.currentThread().getContextClassLoader()));
|
|
|
-
|
|
|
- try {
|
|
|
- this.initializeFeignClientRegisterListener(pool);
|
|
|
- } catch (Exception e) {
|
|
|
- throw new RuntimeException(e);
|
|
|
- }
|
|
|
+ if (INITIALIZED || (MAIN_BREAKER != null && !MAIN_BREAKER.enabled())) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ synchronized (FeignCircuitBreakerInitializer.class) {
|
|
|
+ if (!INITIALIZED) {
|
|
|
+ INITIALIZED = true;
|
|
|
+
|
|
|
+ ClassPool pool = ClassPool.getDefault();
|
|
|
+ pool.appendClassPath(new LoaderClassPath(Thread.currentThread().getContextClassLoader()));
|
|
|
+
|
|
|
+ try {
|
|
|
+ this.initializeFeignClientRegisterListener(pool);
|
|
|
+ } catch (Exception e) {
|
|
|
+ throw new RuntimeException(e);
|
|
|
}
|
|
|
}
|
|
|
}
|