入口:
FeignClientsRegistrar其实现了一个很重要的接口ImportBeanDefinitionRegistrar:
这个接口专门配合@Import注解、Configuration类使用的
可以获取到@Import注解所在的配置类的类元数据信息,然后根据导入的类元数据,按需注册Bean Definition:
我们看FeignClientsRegistrar对registerBeanDefinitions方法的实现:
//FeignClientsRegistrar.java public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { //注册默认的Configuration(其实就是在解析@EnableFeignClients注解) registerDefaultConfiguration(metadata, registry); //注册FeignClients(扫描所有@FeignClient注解并解析) registerFeignClients(metadata, registry); }DEBUG,看下一导入的类元数据,就是启动类,通过这个类的元数据可以获取到它上面所有的注解信息:
看registerDefaultConfiguration:
//FeignClientsRegistrar.java private void registerDefaultConfiguration(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { //从类元数据中获取@EnableFeignClients注解 //getAnnotationAttributes:获取类上指定注解的属性 //该方法第二个参数true,表示将注解中class类型的属性转换为字符串类名暴露到返回到map中 Map<String, Object> defaultAttrs = metadata .getAnnotationAttributes(EnableFeignClients.class.getName(), true); //处理defaultConfiguration属性 if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) { String name; //返回当前类是否在封闭类中声明(例如,当前类是一个内部/嵌套类,还是一个方法中的本地类)。 //false代表当前类就是顶级类,此时是启动类,肯定是顶级类 if (metadata.hasEnclosingClass()) { //如果当前是内部、嵌套、方法中的类,获取我的封闭类的类名 name = "default." + metadata.getEnclosingClassName(); } else { //返回false代表当前是就顶级类,直接获取类名 name = "default." + metadata.getClassName(); } registerClientConfiguration(registry, name, defaultAttrs.get("defaultConfiguration")); } }DEBUG看一下name值,可以看到刚好是default + 当前启动类的全限定性类名
继续看registerClientConfiguration:
//FeignClientsRegistrar.java private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name, Object configuration) { //获取一个BeanDefinition的构建者 专门构建FeignClientSpecification的BeanDefinition BeanDefinitionBuilder builder = BeanDefinitionBuilder .genericBeanDefinition(FeignClientSpecification.class); //为FeignClientSpecification的构造器设置参数 builder.addConstructorArgValue(name); builder.addConstructorArgValue(configuration); //builder.getBeanDefinition()会构建对应的BeanDefinition实例 //然后将其注册到Spring的注册表中 registry.registerBeanDefinition( name + "." + FeignClientSpecification.class.getSimpleName(), builder.getBeanDefinition()); } //BeanDefinitionBuilder.java /** * Add an indexed constructor arg value. The current index is tracked internally * and all additions are at the present point. * 添加一个索引构造函数arg值。 内部跟踪当前索引,所有添加都在当前位置。 */ public BeanDefinitionBuilder addConstructorArgValue(@Nullable Object value) { this.beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue( this.constructorArgIndex++, value); return this; }将BeanDefinition注册到Spring注册表:
//DefaultListableBeanFactory.java public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException { //此时beanName为 default.启动类类名.FeignClientSpecification Assert.hasText(beanName, "Bean name must not be empty"); Assert.notNull(beanDefinition, "BeanDefinition must not be null"); if (beanDefinition instanceof AbstractBeanDefinition) { try { //验证这个bean定义。 ((AbstractBeanDefinition) beanDefinition).validate(); } catch (BeanDefinitionValidationException ex) { throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Validation of bean definition failed", ex); } } //先尝试从注册表中获取 BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName); if (existingDefinition != null) { //如果已经存在,判断是否允许覆盖,不允许就抛异常 if (!isAllowBeanDefinitionOverriding()) { throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition); } else if (existingDefinition.getRole() < beanDefinition.getRole()) { // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE if (logger.isInfoEnabled()) { logger.info("Overriding user-defined bean definition for bean '" + beanName + "' with a framework-generated bean definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]"); } } else if (!beanDefinition.equals(existingDefinition)) { if (logger.isDebugEnabled()) { logger.debug("Overriding bean definition for bean '" + beanName + "' with a different definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]"); } } else { if (logger.isTraceEnabled()) { logger.trace("Overriding bean definition for bean '" + beanName + "' with an equivalent definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]"); } } //覆盖,放入beanDefinitionMap this.beanDefinitionMap.put(beanName, beanDefinition); } else { //第一次注册 //检查该工厂bean创建阶段是否已经开始,通过在此期间是否有任何bean被标记为已创建来判断 if (hasBeanCreationStarted()) { // Cannot modify startup-time collection elements anymore (for stable iteration) // 不能修改启动中的集合元素(用于稳定的迭代) synchronized (this.beanDefinitionMap) { //先放入注册表 this.beanDefinitionMap.put(beanName, beanDefinition); //beanDefinitionNames是一个可供遍历的beanName集合,bean创建阶段就是 //先遍历该集合通过beanName再从beanDefinitionMap中获取BeanDefinition的 //所以在创建阶段为了保证集合迭代稳定性,需要创建新的集合在新的集合上进行修改 //创建一个新list,将beanDefinitionNames内容添加进去 List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1); updatedDefinitions.addAll(this.beanDefinitionNames); //把新的beanName放到新的list中 updatedDefinitions.add(beanName); //将新的集合替代旧的集合 this.beanDefinitionNames = updatedDefinitions; //从工厂内部的手动单例名称集中删除指定名称,避免重复注册 removeManualSingletonName(beanName); } } else { // Still in startup registration phase //仍处于启动注册阶段,不用考虑集合迭代稳定性问题 //直接放入注册表、添加到可遍历集合中 this.beanDefinitionMap.put(beanName, beanDefinition); this.beanDefinitionNames.add(beanName); //从工厂内部的手动单例名称集中删除指定名称,避免重复注册 removeManualSingletonName(beanName); } this.frozenBeanDefinitionNames = null; } if (existingDefinition != null || containsSingleton(beanName)) { resetBeanDefinition(beanName); } }集合迭代稳定性: 为什么多线程的情况,需要复制一个新的集合,在新的集合中添加元素后,替换原来旧集合?为了迭代稳定性。
首先为了保证多线程情况下共享变量的可见性(主内存和工作内存之间的可见性),添加了volatile修饰: 其次为了保证有序性、原子性(多线程并发修改造成问题),所以添加了synchronized关键字 迭代稳定性: 一个线程正在修改集合中的数据,另一个线程正在迭代读取集合中的数据,由于加了volatile,导致读线程迭代的过程中,写线程对集合中的修改读线程是立即可见的,读线程读取的数据正好是写线程修改的数据,或者读线程一开始获取的个数是10个,遍历过程中,数量变多了变少了,发生这些变化都代表不稳定,并有可能引发错误。 解决方案:修改的线程在原来集合基础上复制一个新的集合进行修改,等所有修改完成后,将整个新的集合替换掉原来旧的集合,而在修改过程中,其他线程访问的集合的地址还是指向旧的(类似写时复制的感觉) 如果使用JUC并发包的集合,严重影响性能。看下从工厂内部的手动单例名称集中删除指定名称,避免重复注册:
//DefaultListableBeanFactory.java private void removeManualSingletonName(String beanName) { //Consumer:对给定的参数执行此操作。 //set -> set.remove(beanName):对给定的set集合删除key为beanName的元素 //Predicate:对给定参数计算此谓词。 //set -> set.contains(beanName):对给定的set集合进行判断,包含key为beanName的元素就返回true updateManualSingletonNames(set -> set.remove(beanName), set -> set.contains(beanName)); } //DefaultListableBeanFactory.java //更新工厂内部的手动单例名称集。 private void updateManualSingletonNames(Consumer<Set<String>> action, Predicate<Set<String>> condition) { //检查这个工厂的bean创建阶段是否已经开始 if (hasBeanCreationStarted()) {//一样存在迭代稳定性问题 // Cannot modify startup-time collection elements anymore (for stable iteration) // 不能修改启动中的集合元素(用于稳定的迭代) synchronized (this.beanDefinitionMap) { if (condition.test(this.manualSingletonNames)) { //复制了一个集合,对复制的集合进行操作 Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames); action.accept(updatedSingletons); this.manualSingletonNames = updatedSingletons; } } } else { // Still in startup registration phase // 判断this.manualSingletonNames这个集合是否包含key为beanName的元素 if (condition.test(this.manualSingletonNames)) { //包含了就从manualSingletonNames集合删除这个key为beanName的元素 action.accept(this.manualSingletonNames); } } }Spring注册表中专门有一个集合,在使用org.springframework.beans.factory.support.DefaultListableBeanFactory#registerSingleton方法手动注册单例BeanDefinition时,会将其放到manualSingletonNames这个集合,beanDefinitionNames和manualSingletonNames中的元素不能重复,所以这里做了处理。
回到FeignClientsRegistrar的registerBeanDefinitions,现在看registerFeignClients方法:
//FeignClientsRegistrar.java public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { //注册默认的Configuration(其实就是在解析@EnableFeignClients注解) registerDefaultConfiguration(metadata, registry); //注册FeignClients(扫描所有@FeignClient注解并解析) registerFeignClients(metadata, registry); } //FeignClientsRegistrar.java public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { //获取扫描器 ClassPathScanningCandidateComponentProvider scanner = getScanner(); //设置资源加载器 scanner.setResourceLoader(this.resourceLoader); Set<String> basePackages; //获取@EnableFeignClients注解的属性 Map<String, Object> attrs = metadata .getAnnotationAttributes(EnableFeignClients.class.getName()); //定义扫描过滤器,专门指定扫描被@FeignClient注解的类 AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter( FeignClient.class); //获取@EnableFeignClients注解的clients属性 //该属性直接指定要加载哪些@FeignClient类,配置了这个属性就只会加载指定的 final Class<?>[] clients = attrs == null ? null : (Class<?>[]) attrs.get("clients"); if (clients == null || clients.length == 0) { //clients属性为空,则为扫描器指定条件,只扫描被@FeignClient注解的类 scanner.addIncludeFilter(annotationTypeFilter); //获取扫描路径 basePackages = getBasePackages(metadata); } else { //clients不空的情况 final Set<String> clientClasses = new HashSet<>(); basePackages = new HashSet<>(); for (Class<?> clazz : clients) {//直接遍历指定的@FeignClient类 //获取类所在的包路径 basePackages.add(ClassUtils.getPackageName(clazz)); //获取类的规范类名 clientClasses.add(clazz.getCanonicalName()); } //定义扫描过滤器,只扫描clientClasses中包含的类 AbstractClassTestingTypeFilter filter = new AbstractClassTestingTypeFilter() { @Override protected boolean match(ClassMetadata metadata) { //对扫描到的类进行匹配: //获取当前扫描到的类的类名,转换成规范类名(处理内部类的情况) String cleaned = metadata.getClassName().replaceAll("\\$", "."); //判断clientClasses中是否包含这个类 return clientClasses.contains(cleaned); } }; //即要同时满足被@FeignClient注解,同时该类在@FeignClientd的clients属性中被指定 scanner.addIncludeFilter( new AllTypeFilter(Arrays.asList(filter, annotationTypeFilter))); } for (String basePackage : basePackages) { //扫描包,获取候选组件的BeanDefinition Set<BeanDefinition> candidateComponents = scanner .findCandidateComponents(basePackage); for (BeanDefinition candidateComponent : candidateComponents) { //判断是否是具有注解元数据的BeanDefinition if (candidateComponent instanceof AnnotatedBeanDefinition) { // verify annotated class is an interface AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent; //获取注解的元数据 AnnotationMetadata annotationMetadata = beanDefinition.getMetadata(); Assert.isTrue(annotationMetadata.isInterface(), "@FeignClient can only be specified on an interface"); //获取@FeignClient注解的属性 Map<String, Object> attributes = annotationMetadata .getAnnotationAttributes( FeignClient.class.getCanonicalName()); //获取FeignClient的名称(即@FeignClient注解的四个属性) //优先级contextId > value > name > serviceId //就是服务id、服务名称 String name = getClientName(attributes); //注册ClientConfiguration,之前跟过 //就是注册FeignClientSpecification,FeignClient规范 registerClientConfiguration(registry, name, attributes.get("configuration")); //注册FeignClient的FactoryBean的BeanDefinition registerFeignClient(registry, annotationMetadata, attributes); } } } }大致流程看了,现在分别看一下几个分支:
可以看到这些属性指定的扫描包路径,是一个并集的关系。
入口,从依赖开始:
找到这个自动配置类: 可以看到这里将Spring容器中所有的FeignClient规范类实例都放入了FeignContext。
断点看一下:
现在看FeignClientFactoryBean,它是Spring的FactoryBean,我们看其getObject方法:
//FeignClientFactoryBean.java public Object getObject() throws Exception { return getTarget(); } //FeignClientFactoryBean.java <T> T getTarget() { //根据spring容器,获取FeignContext,Feign的上下文,也是FeignClient的工厂类 FeignContext context = this.applicationContext.getBean(FeignContext.class); //根据FeignContext,获取一个Feign的构建器 Feign.Builder builder = feign(context); ... return (T) targeter.target(this, builder, context, new HardCodedTarget<>(this.type, this.name, url)); }看feign方法:
//FeignClientFactoryBean.java protected Feign.Builder feign(FeignContext context) { //get方法:从FeignContext中获取对应类型的实例,底层会从当前FeignClient对应的子容器中获取 //这里获取Feign的日志工厂 FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class); Logger logger = loggerFactory.create(this.type); // @formatter:off //这里获取Feign的构建器 //构建器的意义我们不需要关注复杂的构建流程,只需要给构建器传递一些需要的组件即可 //这里主要往构建器放入一些FeignClient依赖的一些组件 Feign.Builder builder = get(context, Feign.Builder.class) // required values .logger(logger) .encoder(get(context, Encoder.class)) .decoder(get(context, Decoder.class)) .contract(get(context, Contract.class)); // @formatter:on //获取FeignClientProperties进行一些属性的配置 configureFeign(context, builder); return builder; } //看其中一个get方法: //FeignClientFactoryBean.java protected <T> T get(FeignContext context, Class<T> type) { //注意,当前类是FeignClientFactoryBean //所以这个this.contextId实际上是当前FeignClient的服务id、微服务名称 T instance = context.getInstance(this.contextId, type); if (instance == null) { throw new IllegalStateException( "No bean found of type " + type + " for " + this.contextId); } return instance; }继续看context.getInstance:
//NamedContextFactory.java,就是FeignContext.java public <T> T getInstance(String name, Class<T> type) { //根据name先获取对应的子容器 //name就是微服务名称,FeignClient的名称 AnnotationConfigApplicationContext context = getContext(name); //根据类型从当前子容器,和子容器所有的祖先容器中查找bean的名称,判断是否存在 if (BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context, type).length > 0) { //存在就返回对应类型的实例 return context.getBean(type); } return null; }看获取子容器getContext方法:
//NamedContextFactory.java //FeignContext.java继承自NamedContextFactory.java protected AnnotationConfigApplicationContext getContext(String name) { if (!this.contexts.containsKey(name)) { synchronized (this.contexts) {//双重检查锁,线程安全问题 if (!this.contexts.containsKey(name)) { //子容器还不存在则进行创建 this.contexts.put(name, createContext(name)); } } } return this.contexts.get(name); } //创建子容器 //NamedContextFactory.java protected AnnotationConfigApplicationContext createContext(String name) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); //这里configurations存的就是各个feign client的规范类 if (this.configurations.containsKey(name)) { //获取规范类中的配置类 for (Class<?> configuration : this.configurations.get(name) .getConfiguration()) { //将对应服务名称的配置类注册到该容器 context.register(configuration); } } for (Map.Entry<String, C> entry : this.configurations.entrySet()) { //default开头的是全局的规范类,存的是@EnableFeignClients的defaultConfiguration属性配置的配置类 if (entry.getKey().startsWith("default.")) { for (Class<?> configuration : entry.getValue().getConfiguration()) { //将全局的配置类注册到该容器 context.register(configuration); } } } //注册占位符配置解析器,可以解析bean定义属性值和{@code @Value}注解中的占位符。 //注册默认配置类,defaultConfigType就是FeignClientsConfiguration.class context.register(PropertyPlaceholderAutoConfiguration.class, this.defaultConfigType); //添加具有最高优先级的给定属性源对象。 context.getEnvironment().getPropertySources().addFirst(new MapPropertySource( this.propertySourceName, Collections.<String, Object>singletonMap(this.propertyName, name))); if (this.parent != null) { // 关键!为当前容器设置父容器 // Uses Environment from parent as well as beans context.setParent(this.parent); // jdk11 issue // https://github.com/spring-cloud/spring-cloud-netflix/issues/3101 context.setClassLoader(this.parent.getClassLoader()); } context.setDisplayName(generateDisplayName(name)); //刷新容器 context.refresh(); return context; }回到FeignClientFactoryBean,继续看getObject方法:
//FeignClientFactoryBean.java public Object getObject() throws Exception { return getTarget(); } //FeignClientFactoryBean.java <T> T getTarget() { //根据spring容器,获取FeignContext,Feign的上下文,也是FeignClient的工厂类 FeignContext context = this.applicationContext.getBean(FeignContext.class); //根据FeignContext,获取一个Feign的构建器 //底层就是从当前feignClient名称对应的子容器中获取一些 // 创建FeignClient所依赖的组件实例 Feign.Builder builder = feign(context); //判断是否指定url属性,没有指定了就会负载均衡的方式进行远程调用 if (!StringUtils.hasText(this.url)) { //为服务名补全协议 if (!this.name.startsWith("http")) { this.url = "http://" + this.name; } else { this.url = this.name; } //拼接前缀,就是path属性,cleanPath会先格式化一下 this.url += cleanPath(); //没有指定url,使用具有负载均衡的远程调用客户端 构建feignClient return (T) loadBalance(builder, context, new HardCodedTarget<>(this.type, this.name, this.url)); } //指定了url,则是直连方式 if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) { //补全协议 this.url = "http://" + this.url; } //拼接前缀,就是path属性,cleanPath会先格式化一下 String url = this.url + cleanPath(); //getOptional:也是从context中对应的feignClient名称的子容器中获取Client类型的实例 //这个Client就是发起远程调用的客户端 Client client = getOptional(context, Client.class); if (client != null) { //判断client是否是具有负载均衡的功能client,如果是的话取消包装 //确保直连 if (client instanceof LoadBalancerFeignClient) { // ribbon的负载均衡客户端 // not load balancing because we have a url, // but ribbon is on the classpath, so unwrap // 没有负载平衡,因为我们有一个URL,但是ribbon在类路径中,所以请取消包装 client = ((LoadBalancerFeignClient) client).getDelegate(); } if (client instanceof FeignBlockingLoadBalancerClient) { // openFeign的负载均衡客户端 // not load balancing because we have a url, // but Spring Cloud LoadBalancer is on the classpath, so unwrap // 因为我们有一个URL,所以没有负载均衡 // 但是Spring Cloud LoadBalancer在类路径上,因此请取消包装 client = ((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)); } cleanPath//FeignClientFactoryBean.java private String cleanPath() { //对前缀,path属性进行格式化 String path = this.path.trim(); if (StringUtils.hasLength(path)) { if (!path.startsWith("/")) { path = "/" + path; } if (path.endsWith("/")) { path = path.substring(0, path.length() - 1); } } return path; } getOptional//FeignClientFactoryBean.java protected <T> T getOptional(FeignContext context, Class<T> type) { //这个this.contextId是当前FeignClient的服务id、微服务名称 //之前跟过了不看了,从对应子容器中获取指定类型实例 return context.getInstance(this.contextId, type); }直连方式、负载均衡方式创建其实是一样的,继续跟:
//HystrixTargeter.java public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context, Target.HardCodedTarget<T> target) { if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) { //没有开启熔断功能话就不是熔断的Builder走这 return feign.target(target); } //如果开启了熔断,就会处理一些服务降级的配置: feign.hystrix.HystrixFeign.Builder builder = (feign.hystrix.HystrixFeign.Builder) feign; String name = StringUtils.isEmpty(factory.getContextId()) ? factory.getName() : factory.getContextId(); SetterFactory setterFactory = getOptional(name, context, SetterFactory.class); if (setterFactory != null) { builder.setterFactory(setterFactory); } Class<?> fallback = factory.getFallback(); if (fallback != void.class) { return targetWithFallback(name, context, target, builder, fallback); } Class<?> fallbackFactory = factory.getFallbackFactory(); if (fallbackFactory != void.class) { return targetWithFallbackFactory(name, context, target, builder, fallbackFactory); } //也是调feign.target return feign.target(target); } //Feign.java public <T> T target(Target<T> target) { return build().newInstance(target); }继续跟newInstance:
//ReflectiveFeign.java public <T> T newInstance(Target<T> target) { //targetToHandlersByName.apply:生成方法处理器 //返回值nameToHandler: // key:当前feignClient的方法名 // value:方法处理器 Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target); //methodToHandler:key是方法对象,value是方法处理器 Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>(); //默认方法处理器列表 List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>(); //遍历当前feignClient的接口的所有的方法 for (Method method : target.type().getMethods()) { if (method.getDeclaringClass() == Object.class) { //Object的方法不处理 continue; } else if (Util.isDefault(method)) {//是否是接口中的默认方法 //默认方法创建一个默认方法处理器 DefaultMethodHandler handler = new DefaultMethodHandler(method); //添加到默认方法处理器集合 defaultMethodHandlers.add(handler); //保存方法和处理器映射关系 methodToHandler.put(method, handler); } else { //不是默认方法,就是抽象方法 //从nameToHandler获取已经生成好的对应的方法处理器 methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method))); } } //jdk动态代理,创建InvocationHandler,再创建代理对象 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; }判断是否是接口中的默认方法Util.isDefault(method):
//feign.Util.java public static boolean isDefault(Method method) { // Default methods are public non-abstract, non-synthetic, and non-static instance methods // declared in an interface. // method.isDefault() is not sufficient for our usage as it does not check // for synthetic methods. As a result, it picks up overridden methods as well as actual default // methods. // 默认方法是在接口中声明的公共非抽象,非合成和非静态实例方法。 // method.isDefault()不足以用于我们的用法,因为它不检查合成方法。 // 结果,它选择了覆盖的方法以及实际的默认方法。 //SYNTHETIC也是修饰符,但是是编译器自动生成的,修饰方法或类 //编译器通过生成一些在源代码中不存在的synthetic方法和类的方式, //实现了对private级别的字段和类的访问,从而绕开了语言限制,这可以算是一种trick。 //一般用在内部类,例如外部类要访问内部类成员时,内部类成员都是私有的, //编译以后实际上会为内部类成员生成SYNTHETIC的get方法给外部类进行访问 //参考:https://www.cnblogs.com/bethunebtj/p/7761596.html final int SYNTHETIC = 0x00001000; //获取当前方法修饰符,做与运算,如果只有public修饰,说明是实现方法(没有被abstract、static修饰) //又因为在接口中申明的,所以是接口的默认方法。 return ((method.getModifiers() & (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC | SYNTHETIC)) == Modifier.PUBLIC) && method.getDeclaringClass().isInterface(); }此时有public和abstract修饰,说明是抽象方法。
此时只有public修饰符,说明是一个实现方法,又在接口中,所以是默认方法。
方法处理器生成 看targetToHandlersByName.apply方法:
//feign.ReflectiveFeign.ParseHandlersByName.java public Map<String, MethodHandler> apply(Target key) { //parseAndValidatateMetadata:调用它来解析类中链接到HTTP请求的方法。 //方法元数据,这里只会获取接口中的抽象方法的元数据 List<MethodMetadata> metadata = contract.parseAndValidatateMetadata(key.type()); Map<String, MethodHandler> result = new LinkedHashMap<String, MethodHandler>(); //遍历每一个方法元数据 for (MethodMetadata md : metadata) { BuildTemplateByResolvingArgs buildTemplate; //buildTemplate主要构建http请求的 if (!md.formParams().isEmpty() && md.template().bodyTemplate() == null) { buildTemplate = new BuildFormEncodedTemplateFromArgs(md, encoder, queryMapEncoder); } else if (md.bodyIndex() != null) { buildTemplate = new BuildEncodedTemplateFromArgs(md, encoder, queryMapEncoder); } else { buildTemplate = new BuildTemplateByResolvingArgs(md, queryMapEncoder); } //初始化一个buildTemplate后,根据buildTemplate创建方法处理器 //md.configKey():就是方法名,例如xxxService#saveDepart(Depart),没有包路径 result.put(md.configKey(), //factory = new InvocationHandlerFactory.Default() factory.create(key, md, buildTemplate, options, decoder, errorDecoder)); } return result; }看一下创建方法处理器:
//feign.SynchronousMethodHandler.Factory.java public MethodHandler create(Target<?> target, MethodMetadata md, RequestTemplate.Factory buildTemplateFromArgs, Options options, Decoder decoder, ErrorDecoder errorDecoder) { //看到new 了一个SynchronousMethodHandler,同步方法处理器 return new SynchronousMethodHandler(target, client, retryer, requestInterceptors, logger, logLevel, md, buildTemplateFromArgs, options, decoder, errorDecoder, decode404, closeAfterDecode, propagationPolicy); }