2024年十六(4),2024年Java者未来的出路在哪里

《MySql面试专题》

全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好

全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好

《MySql性能优化的21个最佳实践》

全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好

全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好

全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好

全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好

《MySQL高级知识笔记》

全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好

全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好

全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好

全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好

全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好

全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好

全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好

全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好

文中展示的资料包括:**《MySql思维导图》《MySql核心笔记》《MySql调优笔记》《MySql面试专题》《MySql性能优化的21个最佳实践》《MySq高级知识笔记》**如下图

全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好

关注我,点赞本文给更多有需要的人

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

registerDefaultConfiguration(metadata, registry);

//注册Feign的客户端

registerFeignClients(metadata, registry);

}

registerBeanDefinitions调用两个方法,

  • registerDefaultConfiguration:注册默认配置,

  • registerFeignClients注册Feign的客户端

1.Feign的默认配置注册

我们先看registerDefaultConfiguration方法

private void registerDefaultConfiguration(AnnotationMetadata metadata,

BeanDefinitionRegistry registry) {

//得到@EnableFeignClients注解的原始信息

Map<String, Object> defaultAttrs = metadata

.getAnnotationAttributes(EnableFeignClients.class.getName(), true);

//是否配置了@EnableFeignClients(defaultConfiguration=)默认配置

//使用的类型是FeignClientsConfiguration

if (defaultAttrs != null && defaultAttrs.containsKey(“defaultConfiguration”)) {

String name;

//处理类型,以default开头

if (metadata.hasEnclosingClass()) {

name = “default.” + metadata.getEnclosingClassName();

}

else {

name = “default.” + metadata.getClassName();

}

//注册默认配置,name 默认以 default 开头,后续会根据名称选择配置

registerClientConfiguration(registry, name,

defaultAttrs.get(“defaultConfiguration”));

}

}

上面这个方法在为@EnableFeignClients(defaultConfiguration=)默认配置类做注册,一般可以不配置该属性

private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name,

Object configuration) {

//创建一个BeanDefinitionBuilder

BeanDefinitionBuilder builder = BeanDefinitionBuilder

.genericBeanDefinition(FeignClientSpecification.class);

//添加参数,把配置类交给builder

builder.addConstructorArgValue(name);

builder.addConstructorArgValue(configuration);

//注册了Bean到Spring容器

registry.registerBeanDefinition(

name + “.” + FeignClientSpecification.class.getSimpleName(),

builder.getBeanDefinition());

}

这里是把配置封装成FeignClientSpecification类型,然后通过BeanDefinitionRegistry注册到Spring容器中,该配置类包含了FeignClient的重试,超时,日志等配置,如服务定义配置会使用默认的配置。

还记得在FeignAutoConfiguration配置类中的private List<FeignClientSpecification> configurations吗?这是不是前后呼应起来了呢?

2.Feign的客户端注册

我们在看一下 registerFeignClients 方法

public void registerFeignClients(AnnotationMetadata metadata,

BeanDefinitionRegistry registry) {

//ClassPathScanningCandidateComponentProvider是用来扫Feign描客户端接口的

ClassPathScanningCandidateComponentProvider scanner = getScanner();

//resourceLoader是用来加载类资源的

scanner.setResourceLoader(this.resourceLoader);

Set basePackages;

//获取@EnableFeignClients注解元数据

Map<String, Object> attrs = metadata

.getAnnotationAttributes(EnableFeignClients.class.getName());

//类型过滤器,过滤FeignClient

AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(

FeignClient.class);

//检查是否配置clients属性

final Class<?>[] clients = attrs == null ? null
(Class<?>[]) attrs.get(“clients”);

if (clients == null || clients.length == 0) {

//默认走这里

//如果没有配置@EnableFeignClient(clients={})属性,就获取basePackages属性

//给扫描添加includeFilters过滤器,目的是为了扫描@FeignClient注解

scanner.addIncludeFilter(annotationTypeFilter);

basePackages = getBasePackages(metadata);

}

else {

//如果配置了clients就把 clients的包添加到扫描器中

final Set clientClasses = new HashSet<>();

basePackages = new HashSet<>();

for (Class<?> clazz : clients) {

basePackages.add(ClassUtils.getPackageName(clazz));

clientClasses.add(clazz.getCanonicalName());

}

AbstractClassTestingTypeFilter filter = new AbstractClassTestingTypeFilter() {

@Override

protected boolean match(ClassMetadata metadata) {

String cleaned = metadata.getClassName().replaceAll(“\$”, “.”);

return clientClasses.contains(cleaned);

}

};

scanner.addIncludeFilter(

new AllTypeFilter(Arrays.asList(filter, annotationTypeFilter)));

}

for (String basePackage : basePackages) {

//把basePackage下的注解了@FeignClient的接口封装成BeanDefinition

Set candidateComponents = scanner

.findCandidateComponents(basePackage);

for (BeanDefinition candidateComponent : candidateComponents) {

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());

//获取客户端名字

String name = getClientName(attributes);

//获取@FeignClient注解的configuration配置,对配置进行注册

registerClientConfiguration(registry, name,

attributes.get(“configuration”));

//注册客户端接口feignClient

registerFeignClient(registry, annotationMetadata, attributes);

}

}

}

}

上面代码主要扫描了注解了@FeignClient的接口,会扫描到很多的接口,然后做2个事情

  • 将其配置类通过registerClientConfiguration注册到容器中,这个方法在刚才已经分析过了,这里是第二次调用

  • 通过registerFeignClient注册FeignClient接口到容器,内部会生成接口的代理类

继续跟进registerFeignClient

private void registerFeignClient(BeanDefinitionRegistry registry,

AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {

//获取到组合的Feign接口类名

String className = annotationMetadata.getClassName();

//FeignClientFactoryBean是用用来创建FeignClient接口的,

//这里要对Feign接口进行代理,使用的是FeignClientFactoryBean

BeanDefinitionBuilder definition = BeanDefinitionBuilder

.genericBeanDefinition(FeignClientFactoryBean.class);

//校验@FeignClient相关属性

validate(attributes);

//封装相关的属性到definition

definition.addPropertyValue(“url”, getUrl(attributes));

definition.addPropertyValue(“path”, getPath(attributes));

String name = getName(attributes);

definition.addPropertyValue(“name”, name);

definition.addPropertyValue(“type”, className);

definition.addPropertyValue(“decode404”, attributes.get(“decode404”));

definition.addPropertyValue(“fallback”, attributes.get(“fallback”));

definition.addPropertyValue(“fallbackFactory”, attributes.get(“fallbackFactory”));

definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);

String alias = name + “FeignClient”;

//得到BeanDefinition对象,它是对Feign客户端的Bean的定义对象,基于FeignClientFactoryBean

AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();

boolean primary = (Boolean)attributes.get(“primary”); // has a default, won’t be null

beanDefinition.setPrimary(primary);

String qualifier = getQualifier(attributes);

if (StringUtils.hasText(qualifier)) {

alias = qualifier;

}

BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,

new String[] { alias });

//将BeanDefinition加入到spring容器

BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);

}

从上面方法可以看出,Feign的客户端是通过Spring的工厂生成代理类,Feign接口被封装成BeanDefinition,类型是FeignClientFactoryBean,也就是说Feign把所有的接口都包装成FeignClientFactoryBean

思考一个问题,这里虽然对Feign接口进行了描述,封装成FeignClientFactoryBean,那么接口实例到底在什么时候创建呢???

3.FeignClientFactoryBean 工厂

这个工厂是用来生成Feign接口的代理类的,我们看一下他是如何做的

class FeignClientFactoryBean implements FactoryBean, InitializingBean,

ApplicationContextAware {

/***********************************

  • WARNING! Nothing in this class should be @Autowired. It causes NPEs because of some lifecycle race condition.

***********************************/

//Feign接口类型

private Class<?> type;

//Fiegn客户端名字/请求的服务名

private String name;

//请求的url

private String url;

private String path;

private boolean decode404;

private ApplicationContext applicationContext;

private Class<?> fallback = void.class;

//降级工厂

private Class<?> fallbackFactory = void.class;

…省略…

@Override

public Object getObject() throws Exception {

//获取Feign的上下文,在Feign的初始化一节有说道

FeignContext context = applicationContext.getBean(FeignContext.class);

//调用feign方法得到Builder构造器,用来生成feign

//开启Hystrix使用的是HystrixFeign.Builder

//否则使用的是feign.Feign.Builder

Feign.Builder builder = feign(context);

//如果没有url,即没有指定@FeignClient(url=“”),就根据服务名拼接url

if (!StringUtils.hasText(this.url)) {

String url;

//拼接请求的url如:http://user-server

if (!this.name.startsWith(“http”)) {

url = “http://” + this.name;

}

else {

url = this.name;

}

url += cleanPath();

//生成负载均衡代理类

return loadBalance(builder, context, new HardCodedTarget<>(this.type,

this.name, url));

}

//如果配置了@FeignClient(url=“”),生成默认代理

if (StringUtils.hasText(this.url) && !this.url.startsWith(“http”)) {

this.url = “http://” + this.url;

}

String url = this.url + cleanPath();

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 unwrap

client = ((LoadBalancerFeignClient)client).getDelegate();

}

builder.client(client);

}

Targeter targeter = get(context, Targeter.class);

return targeter.target(this, builder, context, new HardCodedTarget<>(

this.type, this.name, url));

}

FeignClientFactoryBean实现了FactoryBean,getObject方法即是用来创建FeignClient实例的

  • 该方法授权通过ApplicationContext得到FeignContext上下文

  • 然后在从FeignContext上下文中得到Feign.Builder,它是用来构建Feign客户端代理类的

  • 然后根据Feign接口 的URL(要么通过@FeignClient(url=“”)指定,如果没指定,就根据@FeignClient(value=“服务名”)拼接) 生成负载均衡代理类

为什么这里要判断URL呢?如果我们不指定URL会默认走Ribbon的负载均衡

@FeignClient(value=“user-server”)

如果指定了URL会走默认的负载均衡,相当于不走负载均衡

@FeignClient(value=“user-server”,url=“localhost:8989”)

Feign.Builder

这里的Feign.Builder 是从FeignContext中得到的,我们看一下它的源码

public static class Builder {

//请求拦截器

private final List requestInterceptors = new ArrayList();

…省略…

public Builder() {

//Feign的日志打印级别,默认不打印

this.logLevel = Level.NONE;

this.contract = new Default();

this.client = new feign.Client.Default((SSLSocketFactory)null, (HostnameVerifier)null);

//重试策略

this.retryer = new feign.Retryer.Default();

this.logger = new NoOpLogger();

//编码器

this.encoder = new feign.codec.Encoder.Default();

//解码器

this.decoder = new feign.codec.Decoder.Default();

//错误解码器

this.errorDecoder = new feign.codec.ErrorDecoder.Default();

this.options = new Options();

//方法调用工厂

this.invocationHandlerFactory = new feign.InvocationHandlerFactory.Default();

}

//设置日志级别

public Feign.Builder logLevel(Level logLevel) {

this.logLevel = logLevel;

return this;

}

public Feign.Builder contract(Contract contract) {

this.contract = contract;

return this;

}

public Feign.Builder client(Client client) {

this.client = client;

return this;

}

public Feign.Builder retryer(Retryer retryer) {

this.retryer = retryer;

return this;

}

public Feign.Builder logger(Logger logger) {

this.logger = logger;

return this;

}

public Feign.Builder encoder(Encoder encoder) {

this.encoder = encoder;

return this;

}

public Feign.Builder decoder(Decoder decoder) {

this.decoder = decoder;

return this;

}

public Feign.Builder mapAndDecode(ResponseMapper mapper, Decoder decoder) {

this.decoder = new Feign.ResponseMappingDecoder(mapper, decoder);

return this;

}

public Feign.Builder decode404() {

this.decode404 = true;

return this;

}

public Feign.Builder errorDecoder(ErrorDecoder errorDecoder) {

this.errorDecoder = errorDecoder;

return this;

}

public Feign.Builder options(Options options) {

this.options = options;

return this;

}

public Feign.Builder requestInterceptor(RequestInterceptor requestInterceptor) {

this.requestInterceptors.add(requestInterceptor);

return this;

}

//添加Feign的拦截器

public Feign.Builder requestInterceptors(Iterable requestInterceptors) {

this.requestInterceptors.clear();

Iterator var2 = requestInterceptors.iterator();

while(var2.hasNext()) {

RequestInterceptor requestInterceptor = (RequestInterceptor)var2.next();

this.requestInterceptors.add(requestInterceptor);

}

return this;

}

public Feign.Builder invocationHandlerFactory(InvocationHandlerFactory invocationHandlerFactory) {

this.invocationHandlerFactory = invocationHandlerFactory;

return this;

}

public T target(Class apiType, String url) {

return this.target(new HardCodedTarget(apiType, url));

}

//这个方法挺重要的,它是用来构建FeignClient的代理类的

public T target(Target target) {

return this.build().newInstance(target);

}

//注意:这里使用的是ReflectiveFeign作为Feign的默认实现

//创建Feign接口的代理就是ReflectiveFeign搞定的

public Feign build() {

Factory synchronousMethodHandlerFactory = new Factory(this.client, this.retryer, this.requestInterceptors, this.logger, this.logLevel, this.decode404);

ParseHandlersByName handlersByName = new ParseHandlersByName(this.contract, this.options, this.encoder, this.decoder, this.errorDecoder, synchronousMethodHandlerFactory);

return new ReflectiveFeign(handlersByName, this.invocationHandlerFactory);

}

}

Feign.Builder中封装了Feign的日志注解,编码器,解码器,拦截器等等,这些东西是在FeignClientsConfiguration中被创建的

FeignClientsConfiguration

@Configuration

public class FeignClientsConfiguration {

//Feign的消息转换器

@Autowired

private ObjectFactory messageConverters;

@Autowired(required = false)

private List parameterProcessors = new ArrayList<>();

@Autowired(required = false)

private List feignFormatterRegistrars = new ArrayList<>();

@Autowired(required = false)

private Logger logger;

//解码器

@Bean

@ConditionalOnMissingBean

public Decoder feignDecoder() {

return new OptionalDecoder(new ResponseEntityDecoder(new SpringDecoder(this.messageConverters)));

}

//编码器

@Bean

@ConditionalOnMissingBean

public Encoder feignEncoder() {

return new SpringEncoder(this.messageConverters);

}

@Bean

@ConditionalOnMissingBean

public Contract feignContract(ConversionService feignConversionService) {

return new SpringMvcContract(this.parameterProcessors, feignConversionService);

}

@Bean

public FormattingConversionService feignConversionService() {

FormattingConversionService conversionService = new DefaultFormattingConversionService();

for (FeignFormatterRegistrar feignFormatterRegistrar : feignFormatterRegistrars) {

feignFormatterRegistrar.registerFormatters(conversionService);

}

return conversionService;

}

//如果开启了Hystrix,那么使用的是HystrixFeign.builder();

//而不是Feign.Builder

@Configuration

@ConditionalOnClass({ HystrixCommand.class, HystrixFeign.class })

protected static class HystrixFeignConfiguration {

@Bean

@Scope(“prototype”)

@ConditionalOnMissingBean

@ConditionalOnProperty(name = “feign.hystrix.enabled”)

public Feign.Builder feignHystrixBuilder() {

return HystrixFeign.builder();

}

}

//重试策略

@Bean

@ConditionalOnMissingBean

public Retryer feignRetryer() {

return Retryer.NEVER_RETRY;

}

//注册Feign.Builder

@Bean

@Scope(“prototype”)

@ConditionalOnMissingBean

public Feign.Builder feignBuilder(Retryer retryer) {

return Feign.builder().retryer(retryer);

}

//日志工厂

@Bean

@ConditionalOnMissingBean(FeignLoggerFactory.class)

public FeignLoggerFactory feignLoggerFactory() {

return new DefaultFeignLoggerFactory(logger);

}

}

loadBalance方法

回到getObject方法看一下loadBalance方法是如何生成负载均衡代理类的

最后

分享一些资料给大家,我觉得这些都是很有用的东西,大家也可以跟着来学习,查漏补缺。

《Java高级面试》

《Java高级架构知识》

《算法知识》

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

eturn HystrixFeign.builder();

}

}

//重试策略

@Bean

@ConditionalOnMissingBean

public Retryer feignRetryer() {

return Retryer.NEVER_RETRY;

}

//注册Feign.Builder

@Bean

@Scope(“prototype”)

@ConditionalOnMissingBean

public Feign.Builder feignBuilder(Retryer retryer) {

return Feign.builder().retryer(retryer);

}

//日志工厂

@Bean

@ConditionalOnMissingBean(FeignLoggerFactory.class)

public FeignLoggerFactory feignLoggerFactory() {

return new DefaultFeignLoggerFactory(logger);

}

}

loadBalance方法

回到getObject方法看一下loadBalance方法是如何生成负载均衡代理类的

最后

分享一些资料给大家,我觉得这些都是很有用的东西,大家也可以跟着来学习,查漏补缺。

《Java高级面试》

[外链图片转存中…(img-r4PAP4ok-1715057156001)]

《Java高级架构知识》

[外链图片转存中…(img-fXv05qtX-1715057156001)]

《算法知识》

[外链图片转存中…(img-csu77up6-1715057156002)]

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

  • 25
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值