触屏版网站设计,快速建设网站视频教程,seo优化上首页,vfp wordpress什么是Open Feign?
OpenFeign 是 Spring Cloud 全家桶的组件之一#xff0c; 其核心的作用是为 Rest API 提供高效简洁的 RPC 调用方式
搭建测试项目
服务接口和实体
项目名称
cloud-feign-api
实体类
public class Order implements Serializable {private Long id;p…什么是Open Feign?
OpenFeign 是 Spring Cloud 全家桶的组件之一 其核心的作用是为 Rest API 提供高效简洁的 RPC 调用方式
搭建测试项目
服务接口和实体
项目名称
cloud-feign-api
实体类
public class Order implements Serializable {private Long id;private String name;public Order() {}public Order(Long id, String name) {this.id id;this.name name;}
}public class User implements Serializable {private Long id;private String name;public User() {}public User(Long id, String name) {this.id id;this.name name;}
}public class Result T implements Serializable
{private Integer code;private String message;private T data;public Result(Integer code, String message, T data) {this.code code;this.message message;this.data data;}public Result(T data) {this(200, 操作成功, data);}
}服务提供方
项目名称
cloud-feign-server
依赖 (pom.xml)
dependencies!--实体类--dependencygroupIdorg.example/groupIdartifactIdcloud-feign-api/artifactIdversion1.0-SNAPSHOT/version/dependency!-- 注册中心 nacos --dependencygroupIdcom.alibaba.cloud/groupIdartifactIdspring-cloud-starter-alibaba-nacos-discovery/artifactId/dependency!-- web --dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependency!-- test --dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-test/artifactIdscopetest/scope/dependency
/dependencies配置文件(application.yml)
server:port: 9001spring:application:name: cloud-feign-servercloud:nacos:discovery:server-addr: localhost:8848 #配置Nacos地址
配置类
无
启动类
SpringBootApplication
EnableDiscoveryClient
public class FeignServerMain {public static void main(String[] args){SpringApplication.run(FeignServerMain.class,args);}
}控制器
RestController
public class OrderServerController {GetMapping(value /order/get/{id})public Order getPaymentById(PathVariable(id) Long id){return new Order(id, order);}
}RestController
public class UserServerController {GetMapping(value /user/get/{id})public User getUserById(PathVariable(id) Long id){return new User(id, user);}
}服务消费方
项目名称
cloud-feign-client
依赖 (pom.xml)
dependencies!--openfeign--dependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-openfeign/artifactId/dependency!--实体类--dependencygroupIdorg.example/groupIdartifactIdcloud-feign-api/artifactIdversion1.0-SNAPSHOT/version/dependency!-- 注册中心 nacos --dependencygroupIdcom.alibaba.cloud/groupIdartifactIdspring-cloud-starter-alibaba-nacos-discovery/artifactId/dependency!--web--dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependency!--test--dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-test/artifactIdscopetest/scope/dependency
/dependencies配置文件(application.yml)
server:port: 9000spring:application:name: feign-order-clientcloud:nacos:discovery:server-addr: localhost:8848 #配置Nacos地址配置类
Configuration
public class DefaultConfiguration {}Configuration
public class OrderConfiguration {}Configuration
public class UserConfiguration {}启动类
SpringBootApplication
EnableFeignClients(defaultConfiguration {DefaultConfiguration.class}) // 开启feign
EnableDiscoveryClient
public class FeignClientMain {public static void main(String[] args){SpringApplication.run(FeignClientMain.class,args);}
}控制器
RestController
public class OrderClientController {Resourceprivate OrderService orderService;GetMapping(value /consumer/feign/order/get/{id})public ResultOrder getOrderById(PathVariable(id) Long id){Order order orderService.getOrderById(id);return new Result(order);}
}RestController
public class UserClientController {Resourceprivate UserService userService;GetMapping(value /consumer/feign/user/get/{id})public ResultUser getUserById(PathVariable(id) Long id){User user userService.getUserById(id);return new Result(user);}
}服务接口
// http://localhost:9000/consumer/feign/order/get/1
FeignClient(value cloud-feign-server, contextId order, configuration OrderConfiguration.class)
public interface OrderService {GetMapping(value /order/get/{id})Order getOrderById(PathVariable(id) Long id);
}// http://localhost:9000/consumer/feign/user/get/1
FeignClient(value cloud-feign-server, contextId user, configuration UserConfiguration.class)
public interface UserService {GetMapping(value /user/get/{id})User getUserById(PathVariable(id) Long id);
}问题为何只定义接口而没有实现类
思路分析
问题一如何动态生成实现类做到
动态代理 cglib, jdk)
问题二代理对象如何交给spring容器
把Bean交给spring容器的方法
1.xml 声明bean bean id“”, class“”
2.ComponentScan Sevice/Controller/Repository/Componet
3.Import(XXX.class)
4.ImportSelector 接口 - 返回类名数组
5.ImportBeanDefinitionRegistrar 接口 - registerBeanDefinitions
6.Bean 注解
7.FactoryBean 接口 - getObject()
8.SingletonBeanRegistry.registerSingleton(); API
前五种方法bean的创建过程是交给spring负责的流程如下
class - bean definition - bean - put in cache 如何把一个第三方的对象完全由程序员控制对象创建过程交给Spring管理
1.factoryBean
2.SingletonBeanRegistry.registerSingleton();
3.Bean
问题三多个接口需要写多个对应的factoryBean类吗
不需要
1只要定义一个factoryBean类把接口的Class作为变量传给factoryBean
2 针对不同的接口需要创建不同的factoryBean对象每个factoryBean对象所持有的接口类型是不同的。
class FeignClientFactoryBean implements FactoryBeanObject {private Class? type; // 接口类型Overridepublic Object getObject() throws Exception {// 返回代理对象return Proxy.newProxyInstance(this.getClassLoader(),new Class?[] {type}, new InvocationHandler());}
}问题四一个factoryBean类如何创建多个持有不同的接口类型的对象?
1创建多个Bean Definition
BeanDefinitionBuilder.build()
2每个Bean Definition 指定不同的接口类型
BeanDefinitionBuilder.addPropertyValue(String name, Nullable Object value)
BeanDefinitionBuilder.addConstructorArgValue(Nullable Object value)
问题五如何优雅地把自定义的Bean Definition交给Spring?
ImportBeanDefinitionRegistrar 接口
- registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry)
Import、ImportSelector、ImportBeanDefinitionRegistrar的使用和区别
1Import(XXX.class)一般配合ImportSelector或者ImportBeanDefinitionRegistrar使用
2ImportSelector返回的是全类名数组用于选择需要的配置类
3ImportBeanDefinitionRegistrar提供BeanDefinitionRegistry用于注册自定义的Bean Definition
问题六如何获取带有FeignClient注解的接口以及注解信息
包扫描
Spring 提供ClassPathScanningCandidateComponentProvider类做包扫描功能
public class ClassPathScanningCandidateComponentProvider implements EnvironmentCapable, ResourceLoaderAware {private final ListTypeFilter includeFilters new LinkedList();private final ListTypeFilter excludeFilters new LinkedList();public SetBeanDefinition findCandidateComponents(String basePackage) {if (this.componentsIndex ! null indexSupportsIncludeFilters()) {return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);}else {return scanCandidateComponents(basePackage);}}private SetBeanDefinition scanCandidateComponents(String basePackage) {SetBeanDefinition candidates new LinkedHashSet();try {String packageSearchPath ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX resolveBasePackage(basePackage) / this.resourcePattern;Resource[] resources getResourcePatternResolver().getResources(packageSearchPath);for (Resource resource : resources) {if (resource.isReadable()) {try {MetadataReader metadataReader getMetadataReaderFactory().getMetadataReader(resource);// 第一次判断是否是候选组件if (isCandidateComponent(metadataReader)) {ScannedGenericBeanDefinition sbd new ScannedGenericBeanDefinition(metadataReader);sbd.setResource(resource);sbd.setSource(resource);// 第二次判断是否是候选组件if (isCandidateComponent(sbd)) {candidates.add(sbd);}} }catch (Throwable ex) {throw new BeanDefinitionStoreException(Failed to read candidate component class: resource, ex);}}}}catch (IOException ex) {throw new BeanDefinitionStoreException(I/O failure during classpath scanning, ex);}return candidates;}/** 用类型过滤器来判断是否是候选的组件 */protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {for (TypeFilter tf : this.excludeFilters) {if (tf.match(metadataReader, getMetadataReaderFactory())) {return false;}}for (TypeFilter tf : this.includeFilters) {if (tf.match(metadataReader, getMetadataReaderFactory())) {return isConditionMatch(metadataReader);}}return false;}/** 判断bean定义是否符合候选的组件:独立的并且是具体的(不是接口或抽象类) 可以重写 */protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {AnnotationMetadata metadata beanDefinition.getMetadata();return (metadata.isIndependent() (metadata.isConcrete() ||(metadata.isAbstract() metadata.hasAnnotatedMethods(Lookup.class.getName()))));}
}源码解读
EnableFeignClients
Import(FeignClientsRegistrar.class)
public interface EnableFeignClients {// basePackages的别名String[] value() default {};// 扫描的包String[] basePackages() default {};// 扫描的包的classClass?[] basePackageClasses() default {};// 默认的配置类Class?[] defaultConfiguration() default {};// 手动传入的feign client对应的ClassClass?[] clients() default {};}FeignClientsRegistrar
class FeignClientsRegistrarimplements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {Overridepublic void registerBeanDefinitions(AnnotationMetadata metadata,BeanDefinitionRegistry registry) {// 注册默认配置 (启动类上面EnableFeignClients里面的defaultConfiguration属性值)registerDefaultConfiguration(metadata, registry);// 注册feign clientsregisterFeignClients(metadata, registry);}/** 注册默认配置的bean定义(FeignClientSpecification) */private void registerDefaultConfiguration(AnnotationMetadata metadata,BeanDefinitionRegistry registry) {// 从EnableFeignClients注解取出所有的属性值MapString, Object defaultAttrs metadata.getAnnotationAttributes(EnableFeignClients.class.getName(), true);// 如果有配置defaultConfigurationif (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));}} /** 注册所有的feign client的bean定义(FeignClientFactoryBean) */public void registerFeignClients(AnnotationMetadata metadata,BeanDefinitionRegistry registry) {// 获取扫描器ClassPathScanningCandidateComponentProvider scanner getScanner();scanner.setResourceLoader(this.resourceLoader);SetString basePackages;// 获取EnableFeignClients注解中的属性 和 属性值MapString, Object attrs metadata.getAnnotationAttributes(EnableFeignClients.class.getName());// 创建注解类型的过滤器用于过滤出带有FeignClient注解的类或接口AnnotationTypeFilter annotationTypeFilter new AnnotationTypeFilter(FeignClient.class);// 是否写了clients EnableFeignClients(clients {ConsumerFeignClient.class}) 写了就会新增ClassFilter 把过滤器添加到扫描器中 然后通过扫描器器扫描制定的class所在的包final Class?[] clients attrs null ? null: (Class?[]) attrs.get(clients);if (clients null || clients.length 0) {// 扫描器添加注解过滤器scanner.addIncludeFilter(annotationTypeFilter);// 获取扫描包路径basePackages getBasePackages(metadata);}else {final SetString clientClasses new HashSet();basePackages new HashSet();for (Class? clazz : clients) {// 指定class的包路径basePackages.add(ClassUtils.getPackageName(clazz));// 把指定的class的权限定类名放在clientClasses中clientClasses.add(clazz.getCanonicalName());}AbstractClassTestingTypeFilter filter new AbstractClassTestingTypeFilter() {Overrideprotected boolean match(ClassMetadata metadata) {String cleaned metadata.getClassName().replaceAll(\\$, .);return clientClasses.contains(cleaned);}};// 把添加class的过滤器scanner.addIncludeFilter(new AllTypeFilter(Arrays.asList(filter, annotationTypeFilter)));}/** 进行包扫描 */for (String basePackage : basePackages) {// 根据每一个包找出候选的bean定义SetBeanDefinition candidateComponents scanner.findCandidateComponents(basePackage);for (BeanDefinition candidateComponent : candidateComponents) {if (candidateComponent instanceof AnnotatedBeanDefinition) {AnnotatedBeanDefinition beanDefinition (AnnotatedBeanDefinition) candidateComponent;// 获取BeanDefinition的注解元信息AnnotationMetadata annotationMetadata beanDefinition.getMetadata();// 判断这个Bean定义是否是接口Assert.isTrue(annotationMetadata.isInterface(),FeignClient can only be specified on an interface);// 获取FeignClient注解的属性值MapString, Object attributes annotationMetadata.getAnnotationAttributes(FeignClient.class.getCanonicalName());// 获取FeignClient的名字String name getClientName(attributes);// 注册每个feign client注册对应的配置(FeignClientSpecification)registerClientConfiguration(registry, name,attributes.get(configuration));// 注册feign client的bean定义(FeignClientFactoryBean)registerFeignClient(registry, annotationMetadata, attributes);}}}}/** 获取扫描器 重写第二个isCandidateComponent */protected ClassPathScanningCandidateComponentProvider getScanner() {return new ClassPathScanningCandidateComponentProvider(false, this.environment) {Overrideprotected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {boolean isCandidate false;// beanDefinition.getMetadata().isIndependent() 当前类是否是独立的(普通我们那样写的就是独立的, 而内部类(静态内部类除外)就算加了Component, 它因为不是独立的所以返回false)if (beanDefinition.getMetadata().isIndependent()) {// bean定义对应的class不能是注解if (!beanDefinition.getMetadata().isAnnotation()) {isCandidate true;}}return isCandidate;}};}/** 根据配置类生成并注册FeignClientSpecification的bean定义*/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());}/** 生成并注册FeignClientFactoryBean的bean定义 */private void registerFeignClient(BeanDefinitionRegistry registry,AnnotationMetadata annotationMetadata, MapString, Object attributes) {// 获取要注入feign的class名称String className annotationMetadata.getClassName();// FeignClientFactoryBean这个类就是它怎么通过FactorBean, 把所有的接口转换成对应类型的BeanDefinition的// BeanClass就是通过genericBeanDefinition方法设置进去的BeanDefinitionBuilder definition BeanDefinitionBuilder.genericBeanDefinition(FeignClientFactoryBean.class);// 校验FeignClient中的属性, fallback, fallbackFactory, 熔断降级的validate(attributes);// feignclient中的urldefinition.addPropertyValue(url, getUrl(attributes));// feignclient中的pathdefinition.addPropertyValue(path, getPath(attributes));// feignclient中的nameString name getName(attributes);definition.addPropertyValue(name, name);// feignclient中的contextIdString contextId getContextId(attributes);definition.addPropertyValue(contextId, contextId);// 要注册的feign的class名称 (把FeignClientFactoryBean属性Type给赋值了, 到时候获取bean的时候就是这个类型的Bean)definition.addPropertyValue(type, className);// feignclient中的decode404definition.addPropertyValue(decode404, attributes.get(decode404));// feignclient中的fallbackdefinition.addPropertyValue(fallback, attributes.get(fallback));// feignclient中的fallbackFactorydefinition.addPropertyValue(fallbackFactory, attributes.get(fallbackFactory));// 自动注入类型是通过byTypedefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);String alias contextId FeignClient;AbstractBeanDefinition beanDefinition definition.getBeanDefinition();// feignclient中的primaryboolean primary (Boolean) attributes.get(primary); // has a default, wont be// nullbeanDefinition.setPrimary(primary);// feignclient中的qualifierString qualifier getQualifier(attributes);if (StringUtils.hasText(qualifier)) {alias qualifier;}// 构建BeanDefinitionBeanDefinitionHolder holder new BeanDefinitionHolder(beanDefinition, className,new String[] { alias });// 注册beanDefinitionBeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);}
}FeignClientFactoryBean
class FeignClientFactoryBeanimplements FactoryBeanObject, InitializingBean, ApplicationContextAware {private Class? type;Overridepublic Object getObject() throws Exception {return getTarget();}/*** param T the target type of the Feign client* return a {link Feign} client created with the specified data and the context* information*/T T getTarget() {FeignContext context this.applicationContext.getBean(FeignContext.class);Feign.Builder builder feign(context);if (!StringUtils.hasText(this.url)) {if (!this.name.startsWith(http)) {this.url http:// this.name;}else {this.url this.name;}this.url cleanPath();return (T) loadBalance(builder, context,new HardCodedTarget(this.type, this.name, this.url));}if (StringUtils.hasText(this.url) !this.url.startsWith(http)) {this.url http:// this.url;}String url this.url cleanPath();// 获取client, 重点, 它到时候要给到SynchronousMethodHandler中Client client getOptional(context, Client.class);if (client ! null) {if (client instanceof LoadBalancerFeignClient) {// not load balancing because we have a url,// but ribbon is on the classpath, so unwrapclient ((LoadBalancerFeignClient) client).getDelegate();}if (client instanceof FeignBlockingLoadBalancerClient) {// not load balancing because we have a url,// but Spring Cloud LoadBalancer is on the classpath, so unwrapclient ((FeignBlockingLoadBalancerClient) client).getDelegate();}builder.client(client);}Targeter targeter get(context, Targeter.class);return (T) targeter.target(this, builder, context,new HardCodedTarget(this.type, this.name, url));}
}DefaultTargeter
class DefaultTargeter implements Targeter {Overridepublic T T target(FeignClientFactoryBean factory, Feign.Builder feign,FeignContext context, Target.HardCodedTargetT target) {return feign.target(target);}}Feign
public abstract class Feign {public static Builder builder() {return new Builder();}public T T target(TargetT target) {return build().newInstance(target);}public Feign build() {SynchronousMethodHandler.Factory synchronousMethodHandlerFactory new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,logLevel, decode404, closeAfterDecode, propagationPolicy);ParseHandlersByName handlersByName new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,errorDecoder, synchronousMethodHandlerFactory);return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);}
}ReflectiveFeign
public class ReflectiveFeign extends Feign {private final InvocationHandlerFactory factory;Overridepublic T T newInstance(TargetT target) {MapString, MethodHandler nameToHandler targetToHandlersByName.apply(target);MapMethod, MethodHandler methodToHandler new LinkedHashMapMethod, MethodHandler();ListDefaultMethodHandler defaultMethodHandlers new LinkedListDefaultMethodHandler();for (Method method : target.type().getMethods()) {if (method.getDeclaringClass() Object.class) {continue;} else if (Util.isDefault(method)) {DefaultMethodHandler handler new DefaultMethodHandler(method);defaultMethodHandlers.add(handler);methodToHandler.put(method, handler);} else {methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));}}InvocationHandler handler factory.create(target, methodToHandler);T proxy (T) Proxy.newProxyInstance(target.type().getClassLoader(),new Class?[] {target.type()}, handler);for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {defaultMethodHandler.bindTo(proxy);}return proxy;}
}总结
设计只需要定义接口 注解 没有具体的实现类
解决方案根据接口动态生成代理对象把增强功能封装在里面并把此对象交给spring管理
技术点动态代理factoryBean接口包扫描如何把自定义的Bean 定义交给springImportBeanDefinitionRegistrar
备份
dependencies!--openfeign--dependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-openfeign/artifactId/dependency!--spring retry framework--dependencygroupIdorg.springframework.retry/groupIdartifactIdspring-retry/artifactId/dependency!-- http 客户端--
!-- dependency--
!-- groupIdorg.apache.httpcomponents/groupId--
!-- artifactIdhttpclient/artifactId--
!-- /dependency--!-- dependency--
!-- groupIdcom.squareup.okhttp3/groupId--
!-- artifactIdokhttp/artifactId--
!-- /dependency--dependencygroupIdio.github.openfeign/groupIdartifactIdfeign-httpclient/artifactId/dependencydependencygroupIdio.github.openfeign/groupIdartifactIdfeign-okhttp/artifactId/dependency!--实体类--dependencygroupIdorg.example/groupIdartifactIdcloud-feign-api/artifactIdversion1.0-SNAPSHOT/version/dependency!-- 注册中心 nacos --dependencygroupIdcom.alibaba.cloud/groupIdartifactIdspring-cloud-starter-alibaba-nacos-discovery/artifactId/dependency!--web--dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-test/artifactIdscopetest/scope/dependency/dependencies