Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.zodiac.autoconfigure.feign.annotation.FeignConsumersRegistrar Maven / Gradle / Ivy
package org.zodiac.autoconfigure.feign.annotation;
import java.io.IOException;
import java.util.Arrays;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
import org.slf4j.Logger;
import org.springframework.beans.BeansException;
import org.springframework.beans.FatalBeanException;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.AnnotationBeanNameGenerator;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.context.annotation.ScannedGenericBeanDefinition;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.annotation.MergedAnnotation;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.zodiac.commons.logging.SmartSlf4jLoggerFactory;
import org.zodiac.commons.util.ArrayUtil;
import org.zodiac.commons.util.Classes;
import org.zodiac.commons.util.Colls;
import org.zodiac.commons.util.lang.Numbers;
import org.zodiac.commons.util.lang.Strings;
import org.zodiac.commons.util.spring.Springs;
import org.zodiac.feign.core.annotation.FeignConsumer;
import org.zodiac.feign.core.config.FeignConsumerConfigInfo;
import org.zodiac.feign.core.constants.FeignSystemPropertiesConstants;
public class FeignConsumersRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware,
ApplicationContextAware {
private static final Logger LOG = SmartSlf4jLoggerFactory.getLogger(FeignConsumersRegistrar.class);
public static final BeanNameGenerator DEFAULT_BEAN_GENERATOR = (definition,
registry) -> AnnotationBeanNameGenerator.INSTANCE.generateBeanName(definition, registry) + ".SpringBootFeignClient";
private ResourceLoader resourceLoader;
private Environment environment;
private ApplicationContext applicationContext;
protected FeignConsumersRegistrar() {
super();
}
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
@Override
public void setEnvironment(Environment environment) {
this.environment = environment;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
/*Check enabled configuration.*/
if (!isEnableConfiguration(environment)) {
LOG.warn("No enabled Spring Boot/Cloud Feign configuration!");
return;
}
AnnotationAttributes attrs = AnnotationAttributes
.fromMap(metadata.getAnnotationAttributes(EnableFeignConsumers.class.getName()));
if (Objects.nonNull(attrs)) {
final Class>[] clients = Objects.isNull(attrs) ? null : attrs.getClassArray("clients");
if (ArrayUtil.emptyArray(clients)) {
/*
* Notes:这里需手动合并,因为当@EnableFeignConsumers引用了@EnableFeignClients时,
* 且没有依赖spring-cloud-openfeign包时,就不能合并属性值?
* 如配置了value就只能获取value的值,而无法获取被@AliasFor的basePackages的值,稳妥起见手动合并。
*/
Set scanBasePackages = getScanBasePackages(metadata, attrs).stream().filter(pkg -> Strings.notBlank(pkg))
.collect(Collectors.toSet());
/*SpringCloud + Feign*/
if (hasSpringCloudFeignClass()) {
LOG.info("The current classpath contains springcloud feign, "
+ "which automatically enables the SpringCloud + Feign architecture. "
+ "SpringBoot + Feign has been ignored");
}
/*SpringBoot + Feign*/
else {
registerSpringBootFeignClients(metadata, registry, attrs, scanBasePackages);
}
} else {
for (Class> clazz : clients) {
AnnotatedGenericBeanDefinition definition = new AnnotatedGenericBeanDefinition(clazz);
configurerFeignClientPropertyValues(definition, attrs.getClassArray(EnableFeignConsumers.DEFAULT_CONFIGURATION));
registry.registerBeanDefinition(DEFAULT_BEAN_GENERATOR.generateBeanName(definition, registry), definition);
}
}
}
}
private void registerSpringBootFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry,
AnnotationAttributes attrs, Set scanBasePackages) {
new SpringBootFeignClientScanner(registry, attrs.getClassArray(EnableFeignConsumers.DEFAULT_CONFIGURATION))
.doScan(StringUtils.toStringArray(scanBasePackages));
}
private Set getScanBasePackages(AnnotationMetadata metadata, AnnotationAttributes attrs) {
Set scanBasePackages = Colls.set();
for (String pkg : (String[]) attrs.get("value")) {
if (Strings.hasText(pkg)) {
scanBasePackages.add(pkg);
}
}
for (String pkg : (String[]) attrs.get(EnableFeignConsumers.BASE_PACKAGES)) {
if (Strings.hasText(pkg)) {
scanBasePackages.add(pkg);
}
}
for (Class> clazz : (Class[]) attrs.get(EnableFeignConsumers.BASE_PACKAGE_CLASSES)) {
scanBasePackages.add(ClassUtils.getPackageName(clazz));
}
if (scanBasePackages.isEmpty()) {
scanBasePackages.add(ClassUtils.getPackageName(metadata.getClassName()));
}
return scanBasePackages;
}
private void configurerFeignClientPropertyValues(BeanDefinition definition, @Nullable Class>[] defaultConfiguration) {
/*First, find SprinCboot Feign client definition.*/
MergedAnnotation> feignClient = ((AnnotatedBeanDefinition) definition).getMetadata().getAnnotations()
.get(FeignConsumer.class);
if (!feignClient.isPresent()) {
/*Fallback, find SpringCloud Feign.*/
feignClient = ((ScannedGenericBeanDefinition) definition).getMetadata().getAnnotations().get(FeignClient.class);
if (!feignClient.isPresent()) {
return;
}
}
String beanClassName = definition.getBeanClassName();
//((GenericBeanDefinition) definition).setBeanClass(FeignConsumerFactoryBean.class);
if (Springs.isServletWeb(applicationContext)) {
((GenericBeanDefinition) definition).setBeanClass(DefaultServletFeignConsumerFactoryBean.class);
} else if (Springs.isReactiveWeb(applicationContext)) {
((GenericBeanDefinition) definition).setBeanClass(DefaultReactiveFeignConsumerFactoryBean.class);
} else {
throw new FatalBeanException("Application environment that does not support Feign.");
}
definition.setPrimary(feignClient.getBoolean("primary"));
MutablePropertyValues propertyValues = definition.getPropertyValues();
propertyValues.add("targetClass", beanClassName);
propertyValues.add("url", environment.resolveRequiredPlaceholders(feignClient.getString("url")));// baseUrl
propertyValues.add("path", getRequestPath(definition, feignClient));
propertyValues.add("decode404", feignClient.getBoolean("decode404"));
propertyValues.add("configuration", feignClient.getClassArray("configuration"));
propertyValues.add("logLevel", feignClient.getValue("logLevel").orElse(null));
propertyValues.add("connectTimeout", resolveNullableLong(feignClient, "connectTimeout"));
propertyValues.add("readTimeout", resolveNullableLong(feignClient, "readTimeout"));
propertyValues.add("writeTimeout", resolveNullableLong(feignClient, "writeTimeout"));
propertyValues.add("followRedirects", resolveNullableBoolean(feignClient, "followRedirects"));
propertyValues.add("defaultConfiguration", defaultConfiguration);
}
private Boolean resolveNullableBoolean(MergedAnnotation> feignClient, String attributeName) {
try {
return Boolean.parseBoolean(environment.resolveRequiredPlaceholders(feignClient.getString(attributeName)));
} catch (NoSuchElementException e) {
LOG.debug(String.format("Cannot resolve %s, using default value", attributeName), e.getMessage());
}
return null;
}
private Long resolveNullableLong(MergedAnnotation> feignClient, String attributeName) {
try {
return Numbers.toLongObjectOrNull(environment.resolveRequiredPlaceholders(feignClient.getString(attributeName)));
} catch (NoSuchElementException e) {
LOG.debug(String.format("Cannot resolve %s, using default value", attributeName), e.getMessage());
}
return null;
}
private String getRequestPath(BeanDefinition definition, MergedAnnotation> feignClient) {
String path = "";
/*Notes: SpringMvcContract It will automatically splice to the URL.*/
MergedAnnotation> requestMapping = ((AnnotatedBeanDefinition) definition).getMetadata().getAnnotations()
.get(RequestMapping.class);
if (!requestMapping.isPresent()) {
/*Fallback, find by @FeignClient/@SpringBootFeignClient*/
path = feignClient.getString("path");
}
return environment.resolveRequiredPlaceholders(path);
}
public static boolean hasSpringCloudFeignClass() {
return Classes.isPresent("org.springframework.cloud.openfeign.FeignClientsRegistrar");
}
/**
* Check enabled Spring Boot/Cloud Feign configuration.
*
* @param environment {@link Environment}
* @return enabled or not
*/
public static boolean isEnableConfiguration(Environment environment) {
/*Default by true, If want to run in standalone mode, should set false.*/
return environment.getProperty(FeignSystemPropertiesConstants.FEIGN_CLIENT_CONSUMER_ENABLED, boolean.class, FeignConsumerConfigInfo.DEFAULT_ENABLED);
}
class SpringBootFeignClientScanner extends ClassPathBeanDefinitionScanner {
@Nullable
private final Class>[] defaultConfiguration;
public SpringBootFeignClientScanner(BeanDefinitionRegistry registry, Class>[] defaultConfiguration) {
super(registry, true);
this.defaultConfiguration = defaultConfiguration;
registerFilters();
setBeanNameGenerator(DEFAULT_BEAN_GENERATOR);
}
@Override
public Set doScan(String... basePackages) {
Set beanDefinitions = super.doScan(basePackages);
if (beanDefinitions.isEmpty()) {
LOG.warn("No spring boot feign client is found in package '{}'.", Arrays.toString(basePackages));
}
for (BeanDefinitionHolder holder : beanDefinitions) {
ScannedGenericBeanDefinition definition = (ScannedGenericBeanDefinition) holder.getBeanDefinition();
configurerFeignClientPropertyValues(definition, defaultConfiguration);
}
return beanDefinitions;
}
@Override
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
return beanDefinition.getMetadata().isInterface();
}
private void registerFilters() {
/*Include service interfaces.*/
addIncludeFilter(new AnnotationTypeFilter(FeignConsumer.class, true, true));
/*For-compatibility*/
addIncludeFilter(new AnnotationTypeFilter(FeignClient.class, true, true));
/*Exclude service interfaces.*/
addExcludeFilter(new TypeFilter() {
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
throws IOException {
String className = metadataReader.getClassMetadata().getClassName();
return Strings.endsWithAny(className, "package-info", "module-info.java");
}
});
}
}
}