String managementContextPath = env.getProperty(“management.server.servlet.context-path”);// nullable. should be wrapped into optional
Integer jmxPort = env.getProperty(“com.sun.management.jmxremote.port”, Integer.class);//nullable
EurekaInstanceConfigBean instance = new EurekaInstanceConfigBean(inetUtils);
instance.setNonSecurePort(serverPort);
instance.setInstanceId(getDefaultInstanceId(env));
instance.setPreferIpAddress(preferIpAddress);
instance.setSecurePortEnabled(isSecurePortEnabled);
if (StringUtils.hasText(ipAddress)) {
instance.setIpAddress(ipAddress);
}
if(isSecurePortEnabled) {
instance.setSecurePort(serverPort);
}
if (StringUtils.hasText(hostname)) {
instance.setHostname(hostname);
}
String statusPageUrlPath = getProperty(“eureka.instance.status-page-url-path”);
String healthCheckUrlPath = getProperty(“eureka.instance.health-check-url-path”);
if (StringUtils.hasText(statusPageUrlPath)) {
instance.setStatusPageUrlPath(statusPageUrlPath);
}
if (StringUtils.hasText(healthCheckUrlPath)) {
instance.setHealthCheckUrlPath(healthCheckUrlPath);
}
ManagementMetadata metadata = managementMetadataProvider.get(instance, serverPort,
serverContextPath, managementContextPath, managementPort);
if(metadata != null) {
instance.setStatusPageUrl(metadata.getStatusPageUrl());
instance.setHealthCheckUrl(metadata.getHealthCheckUrl());
if(instance.isSecurePortEnabled()) {
instance.setSecureHealthCheckUrl(metadata.getSecureHealthCheckUrl());
}
Map<String, String> metadataMap = instance.getMetadataMap();
if (metadataMap.get(“management.port”) == null) {
metadataMap.put(“management.port”, String.valueOf(metadata.getManagementPort()));
}
} else {
//without the metadata the status and health check URLs will not be set
//and the status page and health check url paths will not include the
//context path so set them here
if(StringUtils.hasText(managementContextPath)) {
instance.setHealthCheckUrlPath(managementContextPath + instance.getHealthCheckUrlPath());
instance.setStatusPageUrlPath(managementContextPath + instance.getStatusPageUrlPath());
}
}
setupJmxPort(instance, jmxPort);
return instance;
}
//注册DiscoveryClient服务发现客户端,它是spring-cloud-commons:2.0.1.RELEASE SpringCloud公共抽象包中的(org.springframework.cloud.netflix.eureka.EurekaDiscoveryClient),
//是Spring Cloudd的服务发现抽象接口 EurekaDiscoveryClient是它的实现
@Bean
public DiscoveryClient discoveryClient(EurekaInstanceConfig config, EurekaClient client) {
return new EurekaDiscoveryClient(config, client);
}
//Eureak服务注册器,他是SpringCloud 在Netflix标准上拓展出来的,提供了服务注册,取消注册,设置服务状态,获取服务状态方法,ServiceRegistry负责服务注册,DiscoveryClient负责服务发现
@Bean
public EurekaServiceRegistry eurekaServiceRegistry() {
return new EurekaServiceRegistry();
}
//EurekaAutoServiceRegistration :eureka服务自动注册器
@Bean
@ConditionalOnBean(AutoServiceRegistrationProperties.class)
@ConditionalOnProperty(value = “spring.cloud.service-registry.auto-registration.enabled”, matchIfMissing = true)
public EurekaAutoServiceRegistration eurekaAutoServiceRegistration(ApplicationContext context, EurekaServiceRegistry registry,
EurekaRegistration registration) {
return new EurekaAutoServiceRegistration(context, registry, registration);
}
//取消注册
@Override
public void stop() {
this.serviceRegistry.deregister(this.registration);
this.running.set(false);
}
//EureakClient配置,该配置没有刷新功能
@Configuration
@ConditionalOnMissingRefreshScope //不可刷新
protected static class EurekaClientConfiguration {
@Autowired
private ApplicationContext context;
@Autowired
private AbstractDiscoveryClientOptionalArgs<?> optionalArgs;
//注册EurekaClient 客户端对象 , 它有一个实现 DiscoveryClient ,
//这个实现 DiscoveryClient 是eureka-client:1.9.3包里面的,是Euraek的服务发现的核心实现
@Bean(destroyMethod = “shutdown”)
@ConditionalOnMissingBean(value = EurekaClient.class, search = SearchStrategy.CURRENT)
public EurekaClient eurekaClient(ApplicationInfoManager manager, EurekaClientConfig config) {
return new CloudEurekaClient(manager, config, this.optionalArgs,
this.context);
}
//注册ApplicationInfoManager ,该对象用来管理 InstanceInfo
@Bean
@ConditionalOnMissingBean(value = ApplicationInfoManager.class, search = SearchStrategy.CURRENT)
public ApplicationInfoManager eurekaApplicationInfoManager(
EurekaInstanceConfig config) {
InstanceInfo instanceInfo = new InstanceInfoFactory().create(config);
return new ApplicationInfoManager(config, instanceInfo);
}
//注册EurekaRegistration,它是Eureka实例的服务注册信息,开启自动注册时,该对象才会被注册
@Bean
@ConditionalOnBean(AutoServiceRegistrationProperties.class)
@ConditionalOnProperty(value = “spring.cloud.service-registry.auto-registration.enabled”, matchIfMissing = true)
public EurekaRegistration eurekaRegistration(EurekaClient eurekaClient,
CloudEurekaInstanceConfig instanceConfig,
ApplicationInfoManager applicationInfoManager,
@Autowired(required = false) ObjectProvider healthCheckHandler) {
return EurekaRegistration.builder(instanceConfig)
.with(applicationInfoManager)
.with(eurekaClient)
.with(healthCheckHandler)
.build();
}
}
//这里也是EurekaClient的配置,只是可刷新的
@Configuration
@ConditionalOnRefreshScope
protected static class RefreshableEurekaClientConfiguration {
@Autowired
private ApplicationContext context;
@Autowired
private AbstractDiscoveryClientOptionalArgs<?> optionalArgs;
//注册Eureak客户端EurekaClient, @Lazy 懒初始化,该bean被用到的时候会被创建
//EurekaClient有一个实现 DiscoveryClient这个实现 是eureka-client:1.9.3包里面的,是Euraek的服务发现的核心实现
@Bean(destroyMethod = “shutdown”)
@ConditionalOnMissingBean(value = EurekaClient.class, search = SearchStrategy.CURRENT)
@org.springframework.cloud.context.config.annotation.RefreshScope
@Lazy
public EurekaClient eurekaClient(ApplicationInfoManager manager, EurekaClientConfig config, EurekaInstanceConfig instance) {
manager.getInfo(); // force initialization
return new CloudEurekaClient(manager, config, this.optionalArgs,
this.context);
}
//注册ApplicationInfoManager,它是InstanceInfo的管理器
@Bean
@ConditionalOnMissingBean(value = ApplicationInfoManager.class, search = SearchStrategy.CURRENT)
@org.springframework.cloud.context.config.annotation.RefreshScope
@Lazy
public ApplicationInfoManager eurekaApplicationInfoManager(EurekaInstanceConfig config) {
//注意:这里的InstanceInfo被创建的状态是InstanceStatus.STARTING;后面在初始化的过程中会修改为 InstanceStatus.UP;
InstanceInfo instanceInfo = new InstanceInfoFactory().create(config);
return new ApplicationInfoManager(config, instanceInfo);
}
@Bean
@org.springframework.cloud.context.config.annotation.RefreshScope
@ConditionalOnBean(AutoServiceRegistrationProperties.class)
@ConditionalOnProperty(value = “spring.cloud.service-registry.auto-registration.enabled”, matchIfMissing = true)
public EurekaRegistration eurekaRegistration(EurekaClient eurekaClient,
CloudEurekaInstanceConfig instanceConfig,
ApplicationInfoManager applicationInfoManager,
@Autowired(required = false) ObjectProvider healthCheckHandler) {
return EurekaRegistration.builder(instanceConfig)
.with(applicationInfoManager)
.with(eurekaClient)
.with(healthCheckHandler)
.build();
}
}
…省略…
//健康指示器,用来管理服务的健康状况
@Configuration
@ConditionalOnClass(Health.class)
protected static class EurekaHealthIndicatorConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnEnabledHealthIndicator(“eureka”)
public EurekaHealthIndicator eurekaHealthIndicator(EurekaClient eurekaClient,
EurekaInstanceConfig instanceConfig, EurekaClientConfig clientConfig) {
return new EurekaHealthIndicator(eurekaClient, instanceConfig, clientConfig);
}
}
总结一下:这里注册了很多对象比较重要的有:
1.EurekaClientConfigBean
:这个类实现了netflix 的 EurekaClientConfig客户端配置接口,是对Eureka客户端的配置对象,即我们在配置文件中配置的 eureka.client
节点下的配置都会被封装到该对象中。
2.EurekaInstanceConfigBean
: 该类实现了netflix的EurekaInstanceConfig接口,是的服务实例信息配置,ApplicationManager通过该接口用来构建InstanceConfig,比如我们在配置文件中配置的eureka.instance
开头的配置就会配置到该对象中。
3.DiscoveryClient
:注册了DiscoveryClient服务发现客户端接口,默认实现是EurekaDiscoveryClient。这个类来源于spring-cloud-common.2.0.1.jar 这个包,这个包是springcloud的抽象包的公共包里面制定了一系列公共的API,而DiscoveryClient中包含了服务发现的基本公共方法
4.配置了一些客户端自动注册组件
-
EurekaServiceRegistry
: Eureak服务注册器,他是SpringCloud 在Netflix标准上拓展出来的,提供了服务注册,取消注册, 关闭服务,设置服务状态,获取服务状态方法 -
EurekaRegistration
: 它是Eureka实例的服务注册信息对象,开启自动注册时,该对象才会被注册,EurekaServiceRegistry负责服务注册,EurekaRegistration存放服务注册信息 -
EurekaAutoServiceRegistration
: Eureka服务自动注册器,用来注册EurekaRegistration信息
5.EurekaClient
:这里通过CloudEurekaClient来实例化,他是用来和EureakServer进行交互,是netflix提供的接口类,它还有个比较重要的实现 DiscoveryClient
主要负责服务发现,这个是与注册中心EureakServer交互的最核心的实现,包括服务注册,注册表拉取,服务续约等等功能都在里面提现。需要注意的是该Bean在注册的时候使用了@Lazy进行延迟创建
6.ApplicationInfoManager
:这个类是用来管理实例注册信息InstanceInfo的,其中还包括了对实例状的态监听机制
7.EurekaHealthIndicator
:这个是用作服务的健康检查的,以及提供了获取服务状态,以及获取Applications服务注册表等方法
2.EurekaAutoServiceRegistration:Eureaka自动注册
在spring-cloud-commons-2.0.1.RELEASE.jar
SpringCloud公共包中/META-INF/spring.factories配置文件中有AutoServiceRegistrationAutoConfiguration
自动配置类,当应用启动SpringBoot会扫描到该自动配置类,然后注册到Spring容器
AutoConfiguration
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.client.serviceregistry.ServiceRegistryAutoConfiguration,\
org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationAutoConfiguration
ServiceRegistryAutoConfiguration
是配置注册的服务的端点信息,可通过端点 “/service-registry”查看到服务注册情况
@Configuration
public class ServiceRegistryAutoConfiguration {
@ConditionalOnBean(ServiceRegistry.class)
@ConditionalOnClass(Endpoint.class)
protected class ServiceRegistryEndpointConfiguration {
@Autowired(required = false)
private Registration registration;
@Bean
@ConditionalOnEnabledEndpoint
public ServiceRegistryEndpoint serviceRegistryEndpoint(ServiceRegistry serviceRegistry) {
ServiceRegistryEndpoint endpoint = new ServiceRegistryEndpoint(serviceRegistry);
endpoint.setRegistration(registration);
return endpoint;
}
}
}
//查看注册服务端点
@Endpoint(id = “service-registry”)
public class ServiceRegistryEndpoint {
private final ServiceRegistry serviceRegistry;
private Registration registration;
public ServiceRegistryEndpoint(ServiceRegistry<?> serviceRegistry) {
this.serviceRegistry = serviceRegistry;
}
public void setRegistration(Registration registration) {
this.registration = registration;
}
@WriteOperation
public ResponseEntity<?> setStatus(String status) {
Assert.notNull(status, “status may not by null”);
if (this.registration == null) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(“no registration found”);
}
this.serviceRegistry.setStatus(this.registration, status);
return ResponseEntity.ok().build();
}
//获取服务状态
@ReadOperation
public ResponseEntity getStatus() {
if (this.registration == null) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(“no registration found”);
}
return ResponseEntity.ok().body(this.serviceRegistry.getStatus(this.registration));
}
}
AutoServiceRegistrationAutoConfiguration
是开启服务自动注册其中注入了AutoServiceRegistration
确保该类会被注册,源码如下
@Configuration
@Import(AutoServiceRegistrationConfiguration.class)
@ConditionalOnProperty(value = “spring.cloud.service-registry.auto-registration.enabled”, matchIfMissing = true)
public class AutoServiceRegistrationAutoConfiguration {
@Autowired(required = false)
private AutoServiceRegistration autoServiceRegistration;
@Autowired
private AutoServiceRegistrationProperties properties;
@PostConstruct
protected void init() {
if (autoServiceRegistration == null && this.properties.isFailFast()) {
throw new IllegalStateException(“Auto Service Registration has been requested, but there is no AutoServiceRegistration bean”);
}
}
}
EurekaAutoServiceRegistration
是Eureka的自动注册器,在EurekaClientAutoConfiguration
中对EurekaAutoServiceRegistration做了Bean的定义,当开启spring.cloud.service-registry.auto-registration.enabled=true
(默认)该Bean注册起效 , 它是 AutoServiceRegistration
的实现
EurekaAutoServiceRegistration 源码如下:
//自动注册器
public class EurekaAutoServiceRegistration implements AutoServiceRegistration, SmartLifecycle, Ordered {
private static final Log log = LogFactory.getLog(EurekaAutoServiceRegistration.class);
private AtomicBoolean running = new AtomicBoolean(false);
private int order = 0;
private AtomicInteger port = new AtomicInteger(0);
private ApplicationContext context;
//服务注册器
private EurekaServiceRegistry serviceRegistry;
//服务注册信息登记
private EurekaRegistration registration;
public EurekaAutoServiceRegistration(ApplicationContext context, EurekaServiceRegistry serviceRegistry, EurekaRegistration registration) {
this.context = context;
this.serviceRegistry = serviceRegistry;
this.registration = registration;
}
//开启服务注册
@Override
public void start() {
// only set the port if the nonSecurePort or securePort is 0 and this.port != 0
if (this.port.get() != 0) {
if (this.registration.getNonSecurePort() == 0) {
this.registration.setNonSecurePort(this.port.get());
}
if (this.registration.getSecurePort() == 0 && this.registration.isSecure()) {
this.registration.setSecurePort(this.port.get());
}
}
// only initialize if nonSecurePort is greater than 0 and it isn’t already running
// because of containerPortInitializer below
if (!this.running.get() && this.registration.getNonSecurePort() > 0) {
//调用服务注册器进行服务注册
this.serviceRegistry.register(this.registration);
//发布一个服务注册事件
this.context.publishEvent(
new InstanceRegisteredEvent<>(this, this.registration.getInstanceConfig()));
this.running.set(true);
}
}
@Override
public void stop() {
//服务停止,取消服务注册
this.serviceRegistry.deregister(this.registration);
this.running.set(false);
}
@Override
public boolean isRunning() {
//获取服务状态是否运行
return this.running.get();
}
@Override
public int getPhase() {
return 0;
}
@Override
public boolean isAutoStartup() {
return true;
}
//停止服务
@Override
public void stop(Runnable callback) {
stop();
callback.run();
}
@Override
public int getOrder() {
return this.order;
}
//监听web启动完成事件,执行start()方法开启服务注册
@EventListener(WebServerInitializedEvent.class)
public void onApplicationEvent(WebServerInitializedEvent event) {
// TODO: take SSL into account
int localPort = event.getWebServer().getPort();
if (this.port.get() == 0) {
log.info("Updating port to " + localPort);
this.port.compareAndSet(0, localPort);
start();
}
}
//监听应用关闭事件,执行stop()方法
@EventListener(ContextClosedEvent.class)
public void onApplicationEvent(ContextClosedEvent event) {
if( event.getApplicationContext() == context ) {
stop();
}
}
}
EurekaAutoServiceRegistration
监听了应用启动事件,调用EurekaServiceRegistry.register
进行服务注册,同时监听应用关闭事件,调用EurekaServiceRegistry.deregister
取消服务注册
3.EurekaServiceRegistry:服务注册器
EurekaServiceRegistry
是Eureak服务注册器,他是SpringCloud 在Netflix标准ServiceRegistry
上拓展出来的,提供了服务注册,取消注册, 关闭服务,设置服务状态,获取服务状态方法
ServiceRegistry
是EureakaServiceRegistry
的接口,他是Netflix的服务发现抽象,ServiceRegistry
需要依赖EurekaRegistration
,EurekaRegistration是ServiceInstance
的实现它约定了服务发现的实例应用的通用的信息
在应用启动时,EurekaAutoServiceRegistration
会监听应用启动完成事件,在start()方法中调用,EureakaServiceRegistry.register
方法开始服务注册,源码如下
//服务注册
public class EurekaServiceRegistry implements ServiceRegistry {
private static final Log log = LogFactory.getLog(EurekaServiceRegistry.class);
//注册方法,注册EurekaRegistration
@Override
public void register(EurekaRegistration reg) {
//初始化客户端,该方法会触发 EurekaClient 的创建
maybeInitializeClient(reg);
if (log.isInfoEnabled()) {
log.info("Registering application " + reg.getInstanceConfig().getAppname()
-
" with eureka with status "
-
reg.getInstanceConfig().getInitialStatus());
}
//初始化客户端状态,把InstanceStatus设置为 InstanceStatus.UP;
reg.getApplicationInfoManager()
.setInstanceStatus(reg.getInstanceConfig().getInitialStatus());
//注册健康检查处理器
reg.getHealthCheckHandler().ifAvailable(healthCheckHandler ->
reg.getEurekaClient().registerHealthCheck(healthCheckHandler));
}
//初始化客户端,reg.getEurekaClient()会触发EureakClient(DiscoveryClient)的初始化
private void maybeInitializeClient(EurekaRegistration reg) {
// force initialization of possibly scoped proxies
reg.getApplicationInfoManager().getInfo();
//初始化Eurak客户端
reg.getEurekaClient().getApplications();
}
//取消注册,设置服务状态为down
@Override
public void deregister(EurekaRegistration reg) {
if (reg.getApplicationInfoManager().getInfo() != null) {
if (log.isInfoEnabled()) {
log.info("Unregistering application " + reg.getInstanceConfig().getAppname()
- " with eureka with status DOWN");
}
reg.getApplicationInfoManager().setInstanceStatus(InstanceInfo.InstanceStatus.DOWN);
//shutdown of eureka client should happen with EurekaRegistration.close()
//auto registration will create a bean which will be properly disposed
//manual registrations will need to call close()
}
}
//设置服务注册状态
@Override
public void setStatus(EurekaRegistration registration, String status) {
InstanceInfo info = registration.getApplicationInfoManager().getInfo();
//TODO: howto deal with delete properly?
if (“CANCEL_OVERRIDE”.equalsIgnoreCase(status)) {
registration.getEurekaClient().cancelOverrideStatus(info);
return;
}
//TODO: howto deal with status types across discovery systems?
InstanceInfo.InstanceStatus newStatus = InstanceInfo.InstanceStatus.toEnum(status);
registration.getEurekaClient().setStatus(newStatus, info);
}
//获取服务注册状态
@Override
public Object getStatus(EurekaRegistration registration) {
String appname = registration.getInstanceConfig().getAppname();
String instanceId = registration.getInstanceConfig().getInstanceId();
InstanceInfo info = registration.getEurekaClient().getInstanceInfo(appname, instanceId);
HashMap<String, Object> status = new HashMap<>();
if (info != null) {
status.put(“status”, info.getStatus().toString());
status.put(“overriddenStatus”, info.getOverriddenStatus().toString());
} else {
status.put(“status”, UNKNOWN.toString());
}
return status;
}
public void close() {
}
}
EurekaServiceRegistry中register方法初始化了Eureka客户端,以及注册了健康检查处理器,在private void maybeInitializeClient(EurekaRegistration reg)
方法中reg.getEurekaClient().getApplications();
会触发在EurekaClientAutoConfiguration
自动配置中对EureakClient的延迟创建,使用的EurekaClient的实现com.netflix.discovery.DiscoveryClient
4.DiscoveryClient:服务发现
这个类来源于 eureka-client-19.3.jar这个包 ,DiscoveryClient
这个类是用来和Eureak Server服务端做交互,通过程序的主入口类上的@EnableDiscoveryClient
标签开启,该类在EurekaClientAutoConfiguration
中被注册,该类的继承关系如下:
CloudEurekaClient
实现了 com.netflix.discovery.DiscoveryClient
接口,DiscoveryClient实现了com.netflix.discovery.EurekaClient
接口,
EurekaClient是Netflix提供的服务发现抽象的接口,DiscoveryClient
是其默认实现,CloudEurekaClient
是spring cloud的提供的,其中重写了onCacheRefreshed()方法,目的是在刷新本地注册表缓存之后用于使用ApplicationEventPublisher发布HeartbeatEvent事件。
com.netflix.discovery.shared.LookupService
:
服务发现顶级接口,里面包含了服务查找方法
/**
-
Lookup service for finding active instances.
-
用来查找活动的服务实例
*/
public interface LookupService {
/**
根据应用名获取该名字对应的应用,Application中描述了该appName对应的多个应用实例
*/
Application getApplication(String appName);
/**
获取所有的应用注册列表,Applications是eureka server返回的所有注册信息
*/
Applications getApplications();
/**
根据ID获取服务实例信息信息对象列表
*/
List getInstancesById(String id);
InstanceInfo getNextServerFromEureka(String virtualHostname, boolean secure);
}
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)
最后
学习视频:
大厂面试真题:
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
*/
Applications getApplications();
/**
根据ID获取服务实例信息信息对象列表
*/
List getInstancesById(String id);
InstanceInfo getNextServerFromEureka(String virtualHostname, boolean secure);
}
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。[外链图片转存中…(img-QppBruQX-1713310183708)]
[外链图片转存中…(img-lXgH3mEr-1713310183708)]
[外链图片转存中…(img-QleRcvXq-1713310183709)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)
最后
学习视频:
[外链图片转存中…(img-TveKHAaN-1713310183709)]
大厂面试真题:
[外链图片转存中…(img-VPgZzAHm-1713310183709)]
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!