springboot启动时动态修改feignClient注解的值,实现微服务本地debug

话不多说上代码
注解代码

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Import(FeignAnnotationUpdateRegistrar.class)
public @interface AEnableUpdateFeignRegistrar {
    /**
     * 需要修改 扫描的包,默认会使用当前类上面的EnableFeignClients里头的包
     * @return
     */
    String[] value() default {"net","com"};
}

反射修改类的代码

@Slf4j
public class FeignAnnotationUpdateRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {

    private ResourceLoader resourceLoader;
    private Environment environment;

    private final Map<String, String> map = MapUtil.builder("scrm-component-server", "scrm-comonent-pro")
            .put("scrm-user", "tcloud-user-pro").put("scrm-crm-customer", "tcloud-customer-pro")
            // todo 这里添加需要替换的微服务名和网关对应的负载均衡名(也可动态修改为url,需要改动点代码,找到使用这个map的地方修改下)
            .put("scrm-paas-common", "http://*******")
            .map();

    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        ClassPathScanningCandidateComponentProvider scanner = this.getScanner();
        scanner.setResourceLoader(this.resourceLoader);
        scanner.addIncludeFilter(new AnnotationTypeFilter(FeignClient.class));
        Set<String> basePackages = this.getBasePackages(metadata);
        Iterator var8 = basePackages.iterator();
        LinkedHashSet<BeanDefinition> candidateComponents = new LinkedHashSet();
        while (var8.hasNext()) {
            String basePackage = (String) var8.next();
            candidateComponents.addAll(scanner.findCandidateComponents(basePackage));
        }
        for (BeanDefinition definition : candidateComponents) {
            try {
                if (definition instanceof AnnotatedBeanDefinition) {
                    AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition)definition;
                    AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
                    MergedAnnotations annotations = annotationMetadata.getAnnotations();
                    Class<?> feignClass = Class.forName(beanDefinition.getBeanClassName());
                    FeignClient feignClient = feignClass.getAnnotation(FeignClient.class);
                    Object value = feignClient.value();
                    String url = this.map.get(value);
                    if (url != null) {
                        InvocationHandler h = Proxy.getInvocationHandler(feignClient);
                        // 获取 AnnotationInvocationHandler 的 memberValues 字段
                        Field hField = h.getClass().getDeclaredField("memberValues");
                        // 因为这个字段事 private final 修饰,所以要打开权限
                        hField.setAccessible(true);
                        // 获取 memberValues
                        Map memberValues = (Map) hField.get(h);
                        // 修改 value 属性值
                        memberValues.put("url",url);
                    }
                    for (MergedAnnotation<Annotation> annotation : annotations) {
                        Class<Annotation> aClass = annotation.getType();
                        if (aClass.isAssignableFrom(FeignClient.class)) {
                            Class<? extends MergedAnnotation> annotationClass = annotation.getClass();
                            Field rootAttributes = annotationClass.getDeclaredField("rootAttributes");
                            rootAttributes.setAccessible(true);
                            Object attributes = rootAttributes.get(annotation);
                            if (attributes instanceof Map) {
                                Map<String, String> map = (Map<String, String>) attributes;
                                Object value = map.get("value");
                                String url = this.map.get(value);
                                if (url != null) {
                                // 这里就是替换feignClient注解里头的url,后面的值可以自定义填写
                                    map.put("url", "https://preapiconsole.71360.com/api/app/" + url);
                                }
                            }
                        }
                    }
                }
            } catch (Exception e) {
                log.error("反射修改feign的url失败", e);
            }
        }
    }

    private ClassPathScanningCandidateComponentProvider getScanner() {
        return new ClassPathScanningCandidateComponentProvider(false, this.environment) {
            protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
                boolean isCandidate = false;
                if (beanDefinition.getMetadata().isIndependent() && !beanDefinition.getMetadata().isAnnotation()) {
                    isCandidate = true;
                }

                return isCandidate;
            }
        };
    }

    protected Set<String> getBasePackages(AnnotationMetadata importingClassMetadata) {
        Map<String, Object> attributes = importingClassMetadata.getAnnotationAttributes(EnableFeignClients.class.getCanonicalName());
        Set<String> basePackages = new HashSet();
        String[] var4 = (String[]) ((String[]) attributes.get("value"));
        int var5 = var4.length;

        int var6;
        String pkg;
        for (var6 = 0; var6 < var5; ++var6) {
            pkg = var4[var6];
            if (StringUtils.hasText(pkg)) {
                basePackages.add(pkg);
            }
        }

        var4 = (String[]) ((String[]) attributes.get("basePackages"));
        var5 = var4.length;

        for (var6 = 0; var6 < var5; ++var6) {
            pkg = var4[var6];
            if (StringUtils.hasText(pkg)) {
                basePackages.add(pkg);
            }
        }

        Class[] var8 = (Class[]) ((Class[]) attributes.get("basePackageClasses"));
        var5 = var8.length;

        for (var6 = 0; var6 < var5; ++var6) {
            Class<?> clazz = var8[var6];
            basePackages.add(ClassUtils.getPackageName(clazz));
        }

        if (basePackages.isEmpty()) {
            basePackages.add(ClassUtils.getPackageName(importingClassMetadata.getClassName()));
        }

        return basePackages;
    }

    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }

    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }
}

特别需要注意的事项:
EnableUpdateFeignRegistrar 这个注解必须要放到EnableFeignClients前头
如下
springboot启动时动态修改feignClient注解的值,实现微服务本地debug_第1张图片

6月29 更新,可能会有从其它地方引入的feign导致修改不到 比如在其它的配置类中加EnableFeignClients,或者用Import
所以在注解中加了value,value中的包都会被扫描然后反射修改注解的值,从根源上解决问题

你可能感兴趣的:(spring,boot,微服务,java)