cn.herodotus.engine.facility.alibaba.autoconfigure.sentinel.enhance.HerodotusSentinelFeign Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of facility-alibaba-spring-boot-starter Show documentation
Show all versions of facility-alibaba-spring-boot-starter Show documentation
基于 Spring Authorization Server 的 Dante Cloud 基础核心组件模块
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2020-2030 郑庚伟 ZHENGGENGWEI (码匠君), Licensed under the AGPL License
*
* This file is part of Herodotus Engine.
*
* Herodotus Engine is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Herodotus Engine is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package cn.herodotus.engine.facility.alibaba.autoconfigure.sentinel.enhance;
import com.alibaba.cloud.sentinel.feign.SentinelContractHolder;
import com.alibaba.cloud.sentinel.feign.SentinelFeign;
import feign.Contract;
import feign.Feign;
import feign.InvocationHandlerFactory;
import feign.Target;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.cloud.openfeign.FallbackFactory;
import org.springframework.cloud.openfeign.FeignClientFactory;
import org.springframework.cloud.openfeign.FeignClientFactoryBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Map;
/**
* {@link Feign.Builder}.
*
* @author Jim
* @author 黄学敏(huangxuemin)
*/
public final class HerodotusSentinelFeign {
private static final String FEIGN_LAZY_ATTR_RESOLUTION = "spring.cloud.openfeign.lazy-attributes-resolution";
private HerodotusSentinelFeign() {
}
public static SentinelFeign.Builder builder() {
return new SentinelFeign.Builder();
}
public static final class Builder extends Feign.Builder implements ApplicationContextAware {
private Contract contract = new Contract.Default();
private ApplicationContext applicationContext;
private FeignClientFactory feignClientFactory;
@Override
public Feign.Builder invocationHandlerFactory(InvocationHandlerFactory invocationHandlerFactory) {
throw new UnsupportedOperationException();
}
@Override
public Builder contract(Contract contract) {
this.contract = contract;
return this;
}
@Override
public Feign internalBuild() {
super.invocationHandlerFactory(new InvocationHandlerFactory() {
@Override
public InvocationHandler create(Target target, Map dispatch) {
GenericApplicationContext gctx = (GenericApplicationContext) Builder.this.applicationContext;
BeanDefinition def = gctx.getBeanDefinition(target.type().getName());
FeignClientFactoryBean feignClientFactoryBean;
// If you need the attributes to be resolved lazily, set the property value to true.
Boolean isLazyInit = applicationContext.getEnvironment().getProperty(FEIGN_LAZY_ATTR_RESOLUTION, Boolean.class, false);
if (isLazyInit) {
/*
* Due to the change of the initialization sequence,
* BeanFactory.getBean will cause a circular dependency. So
* FeignClientFactoryBean can only be obtained from BeanDefinition
*/
feignClientFactoryBean = (FeignClientFactoryBean) def.getAttribute("feignClientsRegistrarFactoryBean");
} else {
feignClientFactoryBean = (FeignClientFactoryBean) applicationContext.getBean("&" + target.type().getName());
}
Class fallback = feignClientFactoryBean.getFallback();
Class fallbackFactory = feignClientFactoryBean.getFallbackFactory();
String beanName = feignClientFactoryBean.getContextId();
if (!StringUtils.hasText(beanName)) {
beanName = (String) getFieldValue(feignClientFactoryBean, "name");
}
Object fallbackInstance;
FallbackFactory fallbackFactoryInstance;
// check fallback and fallbackFactory properties
if (void.class != fallback) {
fallbackInstance = getFromContext(beanName, "fallback", fallback, target.type());
return new HerodotusSentinelInvocationHandler(target, dispatch, new FallbackFactory.Default(fallbackInstance));
}
if (void.class != fallbackFactory) {
fallbackFactoryInstance = (FallbackFactory) getFromContext(beanName, "fallbackFactory", fallbackFactory, FallbackFactory.class);
return new HerodotusSentinelInvocationHandler(target, dispatch, fallbackFactoryInstance);
}
// 默认的 fallbackFactory
HerodotusFallbackFactory herodotusFallbackFactory = new HerodotusFallbackFactory(target);
return new HerodotusSentinelInvocationHandler(target, dispatch, herodotusFallbackFactory);
}
private Object getFromContext(String name, String type, Class fallbackType, Class targetType) {
Object fallbackInstance = feignClientFactory.getInstance(name, fallbackType);
if (fallbackInstance == null) {
throw new IllegalStateException(String.format(
"No %s instance of type %s found for feign client %s",
type, fallbackType, name));
}
// when fallback is a FactoryBean, should determine the type of instance
if (fallbackInstance instanceof FactoryBean> factoryBean) {
try {
fallbackInstance = factoryBean.getObject();
} catch (Exception e) {
throw new IllegalStateException(type + " create fail", e);
}
fallbackType = fallbackInstance.getClass();
}
if (!targetType.isAssignableFrom(fallbackType)) {
throw new IllegalStateException(String.format(
"Incompatible %s instance. Fallback/fallbackFactory of type %s is not assignable to %s for feign client %s",
type, fallbackType, targetType, name));
}
return fallbackInstance;
}
});
super.contract(new SentinelContractHolder(contract));
return super.internalBuild();
}
private Object getFieldValue(Object instance, String fieldName) {
Field field = ReflectionUtils.findField(instance.getClass(), fieldName);
field.setAccessible(true);
try {
return field.get(instance);
} catch (IllegalAccessException e) {
// ignore
}
return null;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.applicationContext = applicationContext;
feignClientFactory = this.applicationContext.getBean(FeignClientFactory.class);
}
}
}