说一说@EnableFeignClients的使用

2022-08-17  本文已影响0人  天草二十六_简村人

目标

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

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属性

总结:无论上面哪种写法,都对业务方不是很友好。使用者只想在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);
    }
}
上一篇 下一篇

猜你喜欢

热点阅读