目标
本文不对注解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属性,下面的三种写法是一样的效果。
- @EnableFeignClients(basePackages = {"com.xxx.crplatform", "com.xx.service.order"})
- @EnableFeignClients(value = {"com.xxx.crplatform", "com.xx.service.order"})
- @EnableFeignClients({"com.xxx.crplatform", "com.xx.service.order"})
多个类的包路径
对应注解的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);
}
}
网友评论