美文网首页
说一说@EnableFeignClients的使用

说一说@EnableFeignClients的使用

作者: 天草二十六_简村人 | 来源:发表于2022-08-17 18:09 被阅读0次

    目标

    本文不对注解EnableFeignClients进行源码分析,网上这方面的文章比较多。先我会给出一个活学活用的示例,再是我们实际的开发过程中,自定义注解仿写就借鉴了它的实现思路。

    • @EnableFeignClients 注解一般是加在XxxApplication.java入口类上,但如遇到@FeignClient所在的包路径不从属于XxxApplication.java所在的package时,会导致feign调用不了。
    • 自定义注解@EnableLock一般是作用于方法上,但是@Lock注解要生效的话,程序会需要知道扫码的包路径。为了减少业务方使用的时候额外配置,我们自动读取XxxApplication.java入口类上的@EnableLock注解所在的包路径,把这个包路径赋值给LockAutoScanProxy.java实现@Lock注解的扫描生效。
    package org.springframework.cloud.openfeign;
    
    import java.lang.annotation.Documented;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    import org.springframework.context.annotation.Import;
    
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @Documented
    @Import(FeignClientsRegistrar.class)
    public @interface EnableFeignClients {
    
        /**
         * Alias for the {@link #basePackages()} attribute. Allows for more concise annotation
         * declarations e.g.: {@code @ComponentScan("org.my.pkg")} instead of
         * {@code @ComponentScan(basePackages="org.my.pkg")}.
         * @return the array of 'basePackages'.
         */
        String[] value() default {};
    
        /**
         * Base packages to scan for annotated components.
         * <p>
         * {@link #value()} is an alias for (and mutually exclusive with) this attribute.
         * <p>
         * Use {@link #basePackageClasses()} for a type-safe alternative to String-based
         * package names.
         * @return the array of 'basePackages'.
         */
        String[] basePackages() default {};
    
        /**
         * Type-safe alternative to {@link #basePackages()} for specifying the packages to
         * scan for annotated components. The package of each class specified will be scanned.
         * <p>
         * Consider creating a special no-op marker class or interface in each package that
         * serves no purpose other than being referenced by this attribute.
         * @return the array of 'basePackageClasses'.
         */
        Class<?>[] basePackageClasses() default {};
    
        /**
         * A custom <code>@Configuration</code> for all feign clients. Can contain override
         * <code>@Bean</code> definition for the pieces that make up the client, for instance
         * {@link feign.codec.Decoder}, {@link feign.codec.Encoder}, {@link feign.Contract}.
         *
         * @see FeignClientsConfiguration for the defaults
         * @return list of default configurations
         */
        Class<?>[] defaultConfiguration() default {};
    
        /**
         * List of classes annotated with @FeignClient. If not empty, disables classpath
         * scanning.
         * @return list of FeignClient classes
         */
        Class<?>[] clients() default {};
    
    }
    

    多个包路径

    对应注解的basePackages属性,也即默认的value属性,下面的三种写法是一样的效果。

    多个类的包路径

    对应注解的basePackageClasses属性

    • @EnableFeignClients(basePackageClasses = {ResponseApplication.class,AuthServiceClient.class})

    总结:无论上面哪种写法,都对业务方不是很友好。使用者只想在XxxApplication.java上加上注解@EnableFeignClients,不关心工具包中的@FeignClient注解在哪个package下。

    一、活学活用

    1.1、JwtFeignPackageClass

    注意它的package是在类AuthServiceClient的package的上一级。

    package com.xhtech.arch.ddd.jwt;
    
    import org.springframework.cloud.openfeign.EnableFeignClients;
    
    /**
     * jwt feign注解生效的包路径.
     *
     */
    @EnableFeignClients
    public class JwtFeignPackageClass {
    }
    

    1.2、AuthServiceClient

    package com.xhtech.arch.ddd.jwt.feign;
    
    import org.springframework.cloud.openfeign.FeignClient;
    
    @FeignClient(name = "auth-service")
    public interface AuthServiceClient {
      // 略
    }
    

    二、自定义注解

    仿写spring-cloud-openfeign-core-2.2.5.RELEASE.jar中的FeignClientsRegistrar,实现自己的注解扫描,初始化并注册Bean。

    2.1、LockAnnotationRegistrar

    import com.xhtech.cloud.redis.lock.aop.LockAutoScanProxy;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.support.BeanDefinitionBuilder;
    import org.springframework.beans.factory.support.BeanDefinitionRegistry;
    import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
    import org.springframework.core.type.AnnotationMetadata;
    import org.springframework.util.ClassUtils;
    
    /**
     * 读取@EnableLock注解下的包路径,作为自定义注解的扫描范围.
     *
     */
    public class LockAnnotationRegistrar implements ImportBeanDefinitionRegistrar {
    
        private static final Logger LOG = LoggerFactory.getLogger(LockAnnotationRegistrar.class);
    
        @Override
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
                                            BeanDefinitionRegistry registry) {
            if (registry.containsBeanDefinition(LockAutoScanProxy.class.getName())) {
                if (LOG.isWarnEnabled()) {
                    LOG.warn("类LockAutoScanProxy已实例化:{}", LockAutoScanProxy.class.getName());
                }
                return;
            }
            // 读取注解EnableLock所在的包路径
            String basePackage = ClassUtils.getPackageName(importingClassMetadata.getClassName());
            if (LOG.isInfoEnabled()) {
                LOG.info("读取注解EnableLock所在的包路径:{}", basePackage);
            }
            BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(LockAutoScanProxy.class);
            // 对构造函数中的入参进行赋值
            definition.addConstructorArgValue(basePackage);
    
            // 注册bean
            registry.registerBeanDefinition(LockAutoScanProxy.class.getName(), definition.getBeanDefinition());
        }
    }
    

    2.2、LockAutoScanProxy

    你自己的类,实例化过程,具体实现各不相同。

    public class LockAutoScanProxy {
        private static final long serialVersionUID = -957037966342626931L;
        public LockAutoScanProxy(String scanPackages) {
            // 略
        }
    
    }
    

    2.3、EnableLock

    注解,注意它的Import了两个类

    import com.xhtech.cloud.redis.lock.LockAnnotationRegistrar;
    import com.xhtech.cloud.redis.lock.aop.LockImportSelector;
    import org.springframework.context.annotation.Import;
    
    import java.lang.annotation.*;
    
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @Import({LockImportSelector.class, LockAnnotationRegistrar.class})
    public @interface EnableLock {
    
    }
    

    2.4、LockImportSelector

    注解的开关类

    import com.xhtech.cloud.aop.selector.AbstractImportSelector;
    import com.xhtech.cloud.aop.selector.RelaxedPropertyResolver;
    import com.xhtech.cloud.redis.common.constant.LockConstant;
    import com.xhtech.cloud.redis.lock.annotation.EnableLock;
    import org.springframework.core.Ordered;
    import org.springframework.core.annotation.Order;
    
    @Order(Ordered.LOWEST_PRECEDENCE - 100)
    public class LockImportSelector extends AbstractImportSelector<EnableLock> {
    
        @Override
        protected boolean isEnabled() {
            return new RelaxedPropertyResolver(getEnvironment()).getProperty(LockConstant.LOCK_ENABLED, Boolean.class, Boolean.TRUE);
        }
    }
    

    相关文章

      网友评论

          本文标题:说一说@EnableFeignClients的使用

          本文链接:https://www.haomeiwen.com/subject/hitsgrtx.html