微服务的访问可使用restTemplate,也可以使用feign远程调用,feign默认使用ribbon实现负载均衡
声明式的伪Http客户端,整合了feign和Hystrix,big和eureka结合,可实现负载均衡和断路器等功能
自动装配FeignAutoConfiguration的时候,会创建Targeter 配置Bean,默认使用 HystrixTargeter实现断路器
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
//启动类注解引入了一个注册类
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients
//注册类
class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
//feign启动时会将需要扫描的配置
//我们定义的feign接口动态代理对象的创建
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
//解析全局配置,如@EnableFeignClients(defaultConfiguration = DefaultFeignConfiguration.class)
//feign针对不同的服务调用可设置自定义的配置,如在启动类上设置,即为全局配置,即所有的feign调用都会引入此配置
registerDefaultConfiguration(metadata, registry);
//解析我们定义的feign接口,解析局部配置,创建代理对象
registerFeignClients(metadata, registry);
}
//================================先说第一个方法===============================
/**
* @Description: 解析全局配置并注册到beanDefinitionMap中
* @Author: PABLO
* @Date: 2022/5/20 12:44
* @Params: [metadata 启动类, registry注册器]
* @Return: void
**/
private void registerDefaultConfiguration(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
//通过启动类注解获取该注解中定义的信息,如下
/*
String[] value() default {};
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
Class<?>[] defaultConfiguration() default {};
Class<?>[] clients() default {};
*/
Map<String, Object> defaultAttrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName(), true);
//如果配置了defaultConfiguration 即全局的feign配置
if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) {
String name;
//前缀
if (metadata.hasEnclosingClass()) {
name = "default." + metadata.getEnclosingClassName();
} else {
name = "default." + metadata.getClassName();
}
//注册
registerClientConfiguration(registry, name, defaultAttrs.get("defaultConfiguration"));
}
}
/**
* @Description:注册feign的全局配置信息到beanDefinitionMap中
* @Author: PABLO
* @Date: 2022/5/20 12:49
* @Params: [registry 注册器, name 加了前缀的名称,如default.com.gator.feign.Application, configuration配置信息]
* @Return: void
**/
private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name, Object configuration) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(FeignClientSpecification.class);
builder.addConstructorArgValue(name);
builder.addConstructorArgValue(configuration);
registry.registerBeanDefinition(name + "." + FeignClientSpecification.class.getSimpleName(),
builder.getBeanDefinition());
}
}
//================================第二个方法(重点)===============================
/**
* @Description: 将feign接口的自定义配置添加到beanDefinitionMap&创建该接口的代理对象(特殊bean-->FactoryBean)
* @Author: PABLO
* @Date: 2022/5/20 12:58
* @Params: [metadata 注解修饰的元数据 , registry 注册器]
* @Return: void
**/
public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
LinkedHashSet<BeanDefinition> candidateComponents = new LinkedHashSet<>();
//获取启动类注解中所有定义的信息
Map<String, Object> attrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName());
//获取启动类上的client列表中是否配置了对应feign接口即@FeignClient
//注意:如果client[]不为null,禁用类路径扫描
final Class<?>[] clients = attrs == null ? null : (Class<?>[]) attrs.get("clients");
//扫描basePackage下的所有@FeignClient修饰的class
if (clients == null || clients.length == 0) {
ClassPathScanningCandidateComponentProvider scanner = getScanner();
//资源加载器
scanner.setResourceLoader(this.resourceLoader);
//筛选出符合要求的class
scanner.addIncludeFilter(new AnnotationTypeFilter(FeignClient.class));
//获取basePackage,启动类所在的包
Set<String> basePackages = getBasePackages(metadata);
//扫描类路径寻找符合要求的组件,即class
for (String basePackage : basePackages) {
candidateComponents.addAll(scanner.findCandidateComponents(basePackage));
}
} else {
//启动类上指定了feign接口的class,注解添加
for (Class<?> clazz : clients) {
candidateComponents.add(new AnnotatedGenericBeanDefinition(clazz));
}
}
for (BeanDefinition candidateComponent : candidateComponents) {
if (candidateComponent instanceof AnnotatedBeanDefinition) {
// 判断这些@FeignClient修饰的class是否为接口(后续需要借口代理,必须是接口)
AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
Assert.isTrue(annotationMetadata.isInterface(), "@FeignClient can only be specified on an interface");
Map<String, Object> attributes = annotationMetadata
.getAnnotationAttributes(FeignClient.class.getCanonicalName());
//获取注解中配置的远程节点的信息如 eureka-client
String name = getClientName(attributes);
//获取注解中配置的自定义配置类信息并注册到beanDefinitionMap中
registerClientConfiguration(registry, name, attributes.get("configuration"));
//针对@FeignClient修饰的接口的操作
registerFeignClient(registry, annotationMetadata, attributes);
}
}
}
/**
* @Description:
* @Author: PABLO
* @Date: 2022/5/20 13:11
* @Params: [registry 注册器, annotationMetadata @FeignClient修饰的接口, attributes @FeignClient中的定义信息]
* @Return: void
**/
private void registerFeignClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata,
Map<String, Object> attributes) {
//类名
String className = annotationMetadata.getClassName();
Class clazz = ClassUtils.resolveClassName(className, null);
ConfigurableBeanFactory beanFactory = registry instanceof ConfigurableBeanFactory
? (ConfigurableBeanFactory) registry : null;
//get
String contextId = getContextId(beanFactory, attributes);
String name = getName(attributes);
//这是特殊处理的bean,和mybatis中的MapperFactoryBean一样
FeignClientFactoryBean factoryBean = new FeignClientFactoryBean();
factoryBean.setBeanFactory(beanFactory);
factoryBean.setName(name);
factoryBean.setContextId(contextId);
factoryBean.setType(clazz);
factoryBean.setRefreshableClient(isClientRefreshEnabled());
BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(clazz, () -> {
//完善属性
factoryBean.setUrl(getUrl(beanFactory, attributes));
factoryBean.setPath(getPath(beanFactory, attributes));
factoryBean.setDecode404(Boolean.parseBoolean(String.valueOf(attributes.get("decode404"))));
Object fallback = attributes.get("fallback");
if (fallback != null) {
factoryBean.setFallback(fallback instanceof Class ? (Class<?>) fallback
: ClassUtils.resolveClassName(fallback.toString(), null));
}
Object fallbackFactory = attributes.get("fallbackFactory");
if (fallbackFactory != null) {
factoryBean.setFallbackFactory(fallbackFactory instanceof Class ? (Class<?>) fallbackFactory
: ClassUtils.resolveClassName(fallbackFactory.toString(), null));
}
//这里是factoryBean的核心方法,后面我列出了调用链路
return factoryBean.getObject();
});
//set
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
definition.setLazyInit(true);
validate(attributes);
//构建beanDefinition
AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
beanDefinition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, className);
beanDefinition.setAttribute("feignClientsRegistrarFactoryBean", factoryBean);
//标记feign接口对应的代理对象是否为主要bean
boolean primary = (Boolean) attributes.get("primary");
beanDefinition.setPrimary(primary);
//修饰词 如 eureka-clientFeignClient
String[] qualifiers = getQualifiers(attributes);
if (ObjectUtils.isEmpty(qualifiers)) {
qualifiers = new String[]{
contextId + "FeignClient"};
}
//构建描述持有者对象
BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, qualifiers);
//注册
BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
//这个是针对动态刷新的,感兴趣的宝子们可以自己瞅瞅
registerOptionsBeanDefinition(registry, contextId);
}
//-----------------------------------------创建代理对象链路-----------------------------------------
public Object getObject() {
return getTarget();
}
<T> T getTarget() {
//......
return (T) loadBalance(builder, context, new Target.HardCodedTarget<>(type, name, url));
//.......
//.......
}
protected <T> T loadBalance(Feign.Builder builder, FeignContext context, Target.HardCodedTarget<T> target) {
//......
return targeter.target(this, builder, context, target);
}
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,
Target.HardCodedTarget<T> target) {
//.....断路器.....
return feign.target(target);
}
public <T> T target(Target<T> target) {
return build().newInstance(target);
}
public <T> T newInstance(Target<T> target) {
//.......
T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
new Class<?>[]{
target.type()}, handler);
//.......
return proxy;
}
//生成的代理类
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com.sun.proxy;
import com.gator.feign.client.EurekaClientFeign;
import com.gator.feign.domain.Parent;
import com.gator.feign.domain.Student;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy76 extends Proxy implements RemoteFeign {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m4;
private static Method m0;
private static Method m5;
public $Proxy76(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{
var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
//我的方法
public final Student postGetInfo(Parent var1) throws {
try {
return (Student)super.h.invoke(this, m3, new Object[]{
var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
//我的方法
public final Student getInfoByFeign(String var1, String var2) throws {
try {
//这里调用父类的h,即Proxy中的protected InvocationHandler h;
//最终会调用invoke
return (Student)super.h.invoke(this, m4, new Object[]{
var1, var2});
} catch (RuntimeException | Error var4) {
throw var4;
} catch (Throwable var5) {
throw new UndeclaredThrowableException(var5);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
//我的方法
public final String getInfo() throws {
try {
return (String)super.h.invoke(this, m5, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("com.gator.feign.client.EurekaClientFeign").getMethod("postStudentByFeign", Class.forName("com.gator.feign.domain.Parent"));
m4 = Class.forName("com.gator.feign.client.EurekaClientFeign").getMethod("getStudentByFeign", Class.forName("java.lang.String"), Class.forName("java.lang.String"));
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
m5 = Class.forName("com.gator.feign.client.EurekaClientFeign").getMethod("infoByFeign");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}