SpringBoot - SpringBoot手写模拟SpringBoot启动过程

news2024/12/29 1:46:21

依赖

建一个工程,两个Module:

1. springboot模块,表示springboot框架的源码实现
2. user包,表示用户业务系统,用来写业务代码来测试我们所模拟出来的SpringBoot
首先,SpringBoot是基于的Spring,所以我们要依赖Spring,然后我希望我们模拟出来的
SpringBoot也支持Spring MVC的那一套功能,所以也要依赖Spring MVC,包括Tomcat等,所以在
SpringBoot模块中要添加以下依赖
 <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.18</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>5.3.18</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.3.18</version>
        </dependency>

        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.1</version>
        </dependency>

        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-core</artifactId>
            <version>9.0.60</version>
        </dependency>
    </dependencies>
在User模块下我们进行正常的开发就行了,比如先添加SpringBoot依赖:
<dependencies>
        <dependency>
            <groupId>org.example</groupId>
            <artifactId>springboot</artifactId>
            <version>1.0-SNAPSHOT</version>
            <exclusions>
                <exclusion>
                    <groupId>org.apache.tomcat.embed</groupId>
                    <artifactId>tomcat-embed-core</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>org.eclipse.jetty</groupId>
            <artifactId>jetty-server</artifactId>
            <version>9.4.43.v20210629</version>
        </dependency>
    </dependencies>
然后定义相关的Controller和Service:

@RestController
 public class UserController {

 @Autowired
 private UserService userService;

 @GetMapping("test")
 public String test(){
 return userService.test();
 }
 }
因为我们模拟实现的是SpringBoot,而不是SpringMVC,所以我直接在user包下定义了
UserController和UserService,最终我希望能运行MyApplication中的main方法,就直接启动了项
目,并能在浏览器中正常的访问到UserController中的某个方法。
核心注解和核心类
我们在真正使用SpringBoot时,核心会用到SpringBoot一个类和注解:
        1. @SpringBootApplication,这个注解是加在应用启动类上的,也就是main方法所在的类
        2. SpringApplication,这个类中有个run()方法,用来启动SpringBoot应用的
所以我们也来模拟实现他们
一个@ZhouyuSpringBootApplication注解:
@Target(ElementType.TYPE)
 @Retention(RetentionPolicy.RUNTIME)
 @Configuration
 @ComponentScan
 public @interface ZhouyuSpringBootApplication {
 }
一个用来实现启动逻辑的ZhouyuSpringApplication类。
public class ZhouyuSpringApplication {

 public static void run(Class clazz){

 }

 }
注意run方法需要接收一个Class类型的参数,这个class是用来干嘛的,等会就知道了。
有了以上两者,我们就可以在MyApplication中来使用了,比如:
@ZhouyuSpringBootApplication
 public class MyApplication {

 public static void main(String[] args) {
 ZhouyuSpringApplication.run(MyApplication.class);
 }
 }
现在用来是有模有样了,但中看不中用,所以我们要来好好实现以下run方法中的逻辑了。
run方法
run方法中需要实现什么具体的逻辑呢?
        首先,我们希望run方法一旦执行完,我们就能在浏览器中访问到UserController,那势必在run方法 中要启动Tomcat,通过Tomcat就能接收到请求了。
        大家如果学过Spring MVC的底层原理就会知道,在SpringMVC中有一个Servlet非常核心,那就是 DispatcherServlet,这个DispatcherServlet需要绑定一个Spring容器,因为DispatcherServlet接收 到请求后,就会从所绑定的Spring容器中找到所匹配的Controller,并执行所匹配的方法。
所以,在run方法中,我们要实现的逻辑如下:
1. 创建一个Spring容器
2. 创建Tomcat对象
3. 生成DispatcherServlet对象,并且和前面创建出来的Spring容器进行绑定
4. 将DispatcherServlet添加到Tomcat中
5. 启动Tomcat
创建Spring容器
这个步骤比较简单,代码如下
public class ZhouyuSpringApplication {

 public static void run(Class clazz){
 AnnotationConfigWebApplicationContext applicationContext = new
AnnotationConfigWebApplicationContext();
 applicationContext.register(clazz);
 applicationContext.refresh();
 }
 }
        我们创建的是一个AnnotationConfigWebApplicationContext容器,并且把run方法传入进来的class 作为容器的配置类,比如在MyApplication的run方法中,我们就是把MyApplication.class 传入到了 run方法中,最终MyApplication就是所创建出来的Spring容器的配置类,并且由于MyApplication类 上有@ZhouyuSpringBootApplication注解,而@ZhouyuSpringBootApplication注解的定义上又 存在@ComponentScan注解,所以AnnotationConfigWebApplicationContext容器在执行refresh时,就会解析MyApplication这个配置类,从而发现定义了@ComponentScan注解,也就知道了要 进行扫描 ,只不过扫描路径为空,而AnnotationConfigWebApplicationContext容器会处理这种情 况,如果扫描路径会空,则会将MyApplication所在的包路径做为扫描路径 ,从而就会扫描到UserService和UserController。
所以Spring容器创建完之后,容器内部就拥有了UserService和UserController这两个Bean。
启动Tomcat
我们用的是Embed-Tomcat,也就是内嵌的Tomcat,真正的SpringBoot中也用的是内嵌的Tomcat,
而对于启动内嵌的Tomcat,也并不麻烦,代码如下:
public class TomcatWebServer implements WebServer{
    @Override
    public void start() {
        System.out.println("启动Tomcat");
    }


    public static void startTomcat(WebApplicationContext applicationContext){

        Tomcat tomcat = new Tomcat();

        Server server = tomcat.getServer();
        Service service = server.findService("Tomcat");

        Connector connector = new Connector();
        connector.setPort(8081);

        Engine engine = new StandardEngine();
        engine.setDefaultHost("localhost");

        Host host = new StandardHost();
        host.setName("localhost");

        String contextPath = "";
        Context context = new StandardContext();
        context.setPath(contextPath);
        context.addLifecycleListener(new Tomcat.FixContextListener());

        host.addChild(context);
        engine.addChild(host);

        service.setContainer(engine);
        service.addConnector(connector);

        tomcat.addServlet(contextPath, "dispatcher", new DispatcherServlet(applicationContext));
        context.addServletMappingDecoded("/*", "dispatcher");

        try {
            tomcat.start();
        } catch (LifecycleException e) {
            e.printStackTrace();
        }

    }
}
        代码虽然看上去比较多,但是逻辑并不复杂,比如配置了Tomcat绑定的端口为8081,后面向当前 Tomcat中添加了DispatcherServlet,并设置了一个Mapping关系,最后启动,其他代码则不用太过
关心。
        而且在构造DispatcherServlet对象时,传入了一个ApplicationContext对象,也就是一个Spring容 器,就是我们前文说的,DispatcherServlet对象和一个Spring容器进行绑定。
接下来,我们只需要在run方法中,调用startTomcat即可:
        接下来,我们只需要在run方法中,调用startTomcat即可:
 public static void run(Class clazz){
AnnotationConfigWebApplicationContext applicationContext = new
AnnotationConfigWebApplicationContext();

applicationContext.register(clazz); 
applicationContext.refresh(); 

 startTomcat(applicationContext);

 }
        实际上代码写到这,一个极度精简版的SpringBoot就写出来了,比如现在运行MyApplication,就能正常的启动项目,并能接收请求。
启动能看到Tomcat的启动日志:
然后在浏览器上访问: http://localhost:8081/test
也能正常的看到结果:
        此时,你可以继续去写其他的Controller和Service了,照样能正常访问到,而我们的业务代码中仍然 只用到了ZhouyuSpringApplication类和@ZhouyuSpringBootApplication注解。
实现Tomcat和Jetty的切换
虽然我们前面已经实现了一个比较简单的SpringBoot,不过我们可以继续来扩充它的功能,比如现在 我有这么一个需求,这个需求就是我现在不想使用Tomcat了,而是想要用Jetty,那该怎么办?
我们前面代码中默认启动的是Tomcat,那我现在想改成这样子:
1. 如果项目中有Tomcat的依赖,那就启动Tomcat
2. 如果项目中有Jetty的依赖就启动Jetty
3. 如果两者都没有则报错
4. 如果两者都有也报错
这个逻辑希望SpringBoot自动帮我实现,对于程序员用户而言,只要在Pom文件中添加相关依赖就可以了,想用Tomcat就加Tomcat依赖,想用Jetty就加Jetty依赖。
那SpringBoot该如何实现呢?
         我们知道,不管是Tomcat还是Jetty,它们都是应用服务器,或者是Servlet容器,所以我们可以定义 接口来表示它们,这个接口叫做WebServer(别问我为什么叫这个,因为真正的SpringBoot源码中也 叫这个)。
并且在这个接口中定义一个start方法:

public interface WebServer {

 public void start();

 }
有了WebServer接口之后,就针对Tomcat和Jetty提供两个实现类:
public class TomcatWebServer implements WebServer{

 @Override
 public void start() {
 System.out.println("启动Jetty");
 }
 }
public class JettyWebServer implements WebServer{

 @Override
 public void start() {
 System.out.println("启动Tomcat");

 }
 }

而在ZhouyuSpringApplication中的run方法中,我们就要去获取对应的WebServer,然后启动对应的webServer,代码为:

public static void run(Class clazz){
AnnotationConfigWebApplicationContext applicationContext = new
AnnotationConfigWebApplicationContext();

applicationContext.register(clazz); 
applicationContext.refresh(); 

WebServer webServer = getWebServer(applicationContext);
webServer.start(); 

 }

 public static WebServer getWebServer(ApplicationContext applicationContext){
 return null;
 }
这样,我们就只需要在getWebServer方法中去判断到底该返回TomcatWebServer还是
JettyWebServer。
前面提到过,我们希望根据项目中的依赖情况,来决定到底用哪个WebServer,我就直接用
SpringBoot中的源码实现方式来模拟了。
模拟实现条件注解
首先我们得实现一个条件注解@ZhouyuConditionalOnClass,对应代码如下:
@Target({ ElementType.TYPE, ElementType.METHOD })
 @Retention(RetentionPolicy.RUNTIME)
 @Conditional(ZhouyuOnClassCondition.class)
 public @interface ZhouyuConditionalOnClass {
 String value() default "";
 }

注意核心为@Conditional(ZhouyuOnClassCondition.class)中的ZhouyuOnClassCondition,因为
它才是真正得条件逻辑:

public class ZhouyuOnClassCondition implements Condition {

 @Override
 public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
 Map<String, Object> annotationAttributes =
metadata.getAnnotationAttributes(ZhouyuConditionalOnClass.class.getName());


 String className = (String) annotationAttributes.get("value");

 try {
 context.getClassLoader().loadClass(className);
 return true;
 } catch (ClassNotFoundException e) {
 return false;
 }
 }
 }

        具体逻辑为,拿到@ZhouyuConditionalOnClass中的value属性,然后用类加载器进行加载,如果加载到了所指定的这个类,那就表示符合条件,如果加载不到,则表示不符合条件。

模拟实现自动配置类
有了条件注解,我们就可以来使用它了,那如何实现呢?


这里就要用到自动配置类的概念,我们先看代码:

@Configuration
public class WebServiceAutoConfiguration {

 @Bean
 @ZhouyuConditionalOnClass("org.apache.catalina.startup.Tomcat")
 public TomcatWebServer tomcatWebServer(){
 return new TomcatWebServer();
 }

 @Bean
 @ZhouyuConditionalOnClass("org.eclipse.jetty.server.Server")
 public JettyWebServer jettyWebServer(){
 return new JettyWebServer();
 }
}
这个代码还是比较简单的,通过一个WebServiceAutoConfiguration的Spring配置类,在里面定义了 两个Bean,一个TomcatWebServer,一个JettyWebServer,不过这两个要生效的前提是符合当前所指定的条件,比如:
只有存在"org.apache.catalina.startup.Tomcat"类,那么才有TomcatWebServer这个Bean
只有存在"org.eclipse.jetty.server.Server"类,那么才有TomcatWebServer这个Bean
并且我们只需要在ZhouyuSpringApplication中getWebServer方法,如此实现:
public static WebServer getWebServer(ApplicationContext applicationContext){
 // key为beanName, value为Bean对象
Map<String, WebServer> webServers =
applicationContext.getBeansOfType(WebServer.class);


 if (webServers.isEmpty()) {
throw new NullPointerException(); 
 }
 if (webServers.size() > 1) {
throw new IllegalStateException(); 
 }

 // 返回唯一的一个
 return webServers.values().stream().findFirst().get();
 }
这样整体SpringBoot启动逻辑就是这样的:
        1. 创建一个AnnotationConfigWebApplicationContext容器
        2. 解析MyApplication类,然后进行扫描
        3. 通过getWebServer方法从Spring容器中获取WebServer类型的Bean
        4. 调用WebServer对象的start方法
有了以上步骤,我们还差了一个关键步骤,就是Spring要能解析到WebServiceAutoConfiguration这 个自动配置类,因为不管这个类里写了什么代码,Spring不去解析它,那都是没用的,此时我们需要 SpringBoot在run方法中,能找到WebServiceAutoConfiguration这个配置类并添加到Spring容器 中。
        MyApplication是Spring的一个配置类,但是MyApplication是我们传递给SpringBoot,从而添加到 Spring容器中去的,而WebServiceAutoConfiguration就需要SpringBoot去自动发现,而不需要程 序员做任何配置才能把它添加到Spring容器中去,而且要注意的是,Spring容器扫描也是扫描不到 WebServiceAutoConfiguration这个类的,因为我们的扫描路径是"com.zhouyu.user",而
WebServiceAutoConfiguration所在的包路径为"com.zhouyu.springboot"。
        那SpringBoot中是如何实现的呢?通过SPI,当然SpringBoot中自己实现了一套SPI机制,也就是我们 熟知的spring.factories文件,那么我们模拟就不搞复杂了,就直接用JDK自带的SPI机制。
发现自动配置类
        为了实现这个功能,以及为了最后的效果演示,我们需要把springboot源码和业务代码源码拆分两个 maven模块,也就相当于两个项目,最后的源码结构为

现在我们只需要在springboot项目中的resources目录下添加如下目录(META-INF/services)和文
件:

SPI的配置就完成了,相当于通过com.zhouyu.springboot.AutoConfiguration文件配置了
springboot中所提供的配置类。
并且提供一个接口:
 public interface AutoConfiguration {
 }
并且WebServiceAutoConfiguration实现该接口:
@Configuration
public class WebServerAutoConfiguration implements AutoConfiguration {

    @Bean
    @ConditionalOnClass("org.apache.catalina.startup.Tomcat")
    public TomcatWebServer tomcatWebServer(){
        return new TomcatWebServer();
    }

    @Bean
    @ConditionalOnClass("org.eclipse.jetty.server.Server")
    public JettyWebServer jettyWebServer(){
        return new JettyWebServer();
    }
}
然后我们再利用spring中的@Import技术来导入这些配置类,我们在
@ZhouyuSpringBootApplication的定义上增加如下代码:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Configuration
@ComponentScan
@Import(ZhouyuImportSeclet.class)
public @interface ZhouyuSpringBootApplication {
}
ZhouyuImportSelect类为:
public class ZhouyuImportSeclet implements DeferredImportSelector {

    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {

        // 自动配置

        ServiceLoader<AutoConfiguration> loader = ServiceLoader.load(AutoConfiguration.class);

        List<String> list = new ArrayList<>();

        for (AutoConfiguration configuration : loader) {
            list.add(configuration.getClass().getName());
        }

        return list.toArray(new String[0]);
    }
}
这就完成了从com.zhouyu.springboot.AutoConfiguration文件中获取自动配置类的名字,并导入到
Spring容器中,从而Spring容器就知道了这些配置类的存在,而对于user项目而言,是不需要修改代 码的。
此时运行MyApplication,就能看到启动了Tomcat:
因为SpringBoot默认在依赖中添加了Tomcat依赖,而如果在User模块中再添加jetty的依赖:

<dependencies>
 <dependency>
<groupId>org.example</groupId> 
<artifactId>springboot</artifactId> 
<version>1.0-SNAPSHOT</version> 
 </dependency>

 <dependency>
<groupId>org.eclipse.jetty</groupId> 
<artifactId>jetty-server</artifactId> 
<version>9.4.43.v20210629</version> 1
 </dependency>
 </dependencies>
那么启动MyApplication就会报错:

只有先排除到Tomcat的依赖,再添加Jetty的依赖才能启动Jetty:

注意:由于没有了Tomcat的依赖,记得把最开始写的startTomcat方法给注释掉,并删除掉相关依

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1402388.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

3DMAX初级小白班第一课:菜单栏介绍

基本介绍 这里不可能一个一个选项全部教给大家&#xff08;毕竟之后靠实操慢慢就记住了&#xff09;&#xff0c;只说一些相对需要注意的设置。 自定义-热键编辑器-热键设置 这里有你所需要的全部快捷键 自定义-自定义UI启动布局 将UI布局还原到启动的位置 自定义-通用单…

使用Go发送HTTP GET请求

在Go语言中&#xff0c;我们可以使用net/http包来发送HTTP GET请求。以下是一个简单的示例&#xff0c;展示了如何使用Go发送HTTP GET请求并获取响应。 go复制代码 package main import ( "fmt" "io/ioutil" "net/http" …

HCIP网络的类型

一.网络类型&#xff1a; 点到点 BMA&#xff1a;广播型多路访问 -- 在一个MA网络中同时存在广播&#xff08;泛洪&#xff09;机制 NBMA&#xff1a;非广播型多路访问 -- 在一个MA网络中&#xff0c;没有泛洪机制-----不怎么使用了 MA&#xff1a;多路访问 -- 在一个…

03-常用编程概念

上一篇&#xff1a;02-编程猜谜游戏 本章介绍几乎所有编程语言中都会出现的概念&#xff0c;以及它们在 Rust 中的工作原理。许多编程语言的核心都有许多共同点。本章介绍的概念都不是 Rust 独有的&#xff0c;但我们会在 Rust 的上下文中讨论这些概念&#xff0c;并解释使用这…

课堂教学内容分为哪几类

作为一名老师&#xff0c;每天都要面对各种类型的学生&#xff0c;而学生们最关心的问题之一就是&#xff1a;老师&#xff0c;你到底要教我们什么&#xff1f;这个问题看似简单&#xff0c;实则深奥。那么&#xff0c;课堂教学内容究竟分为哪几类呢&#xff1f;今天&#xff0…

【webrtc】GCC 7: call模块创建的ReceiveSideCongestionController

webrtc 代码学习&#xff08;三十二&#xff09; video RTT 作用笔记 从call模块说起 call模块创建的时候&#xff0c;会创建 src\call\call.h 线程&#xff1a; 统计 const std::unique_ptr<CallStats> call_stats_;SendDelayStats &#xff1a; 发送延迟统计 const…

【书生·浦语大模型实战营05】《(5)LMDeploy 大模型量化部署实践》学习笔记

《(5)LMDeploy 大模型量化部署实践》 课程文档&#xff1a;《LMDeploy 的量化和部署》 1、大模型部署背景 1.1 模型部署 定义 将训练好的模型在特定软硬件环境中启动的过程&#xff0c;使模型能够接收输入并返回预测结果为了满足性能和效率的需求&#xff0c;常常需要对模型…

CLion调试Nodejs源码

【环境】 macOS node-v20.11.0源码 CLion 2023.3.2 【1】下载源码 https://nodejs.org/en/download/ 【2】编译源码 解压后的目录如下 进入解压后的目录进行编译 ./configure --debug make -C out BUILDTYPEDebug -j 4需要好久… 编译成功之后在node-v20.11.0目录下会有一个…

DNS是什么?为什么需要DNS?常用的DNS记录类型有哪些?

在互联网时代&#xff0c;基本上人人都要上网&#xff0c;网络已与人们日常生活息息相关。而DNS就在我们上网的过程中扮演着重要的角色—“将网址/域名解析成 IP 地址”。那么DNS是什么&#xff1f;为什么需要DNS&#xff1f;常用的DNS类型有哪些呢&#xff1f;别着急&#xff…

[数据结构 - C++] 红黑树RBTree

文章目录 1、前言2、红黑树的概念3、红黑树的性质4、红黑树节点的定义5、红黑树的插入Insert6、红黑树的验证7、红黑树与AVL树的比较附录&#xff1a; 1、前言 我们在学习了二叉搜索树后&#xff0c;在它的基础上又学习了AVL树&#xff0c;知道了AVL树是靠平衡因子来调节左右高…

springboot+mysql马拉松赛事志愿者管理系统-计算机毕业设计源码93439

摘 要 信息化社会内需要与之针对性的信息获取途径&#xff0c;但是途径的扩展基本上为人们所努力的方向&#xff0c;由于站在的角度存在偏差&#xff0c;人们经常能够获得不同类型信息&#xff0c;这也是技术最为难以攻克的课题。针对马拉松赛事志愿者管理等问题&#xff0c;对…

司铭宇老师: 房地产中介销售顾问培训:房产中介经纪人提升销售业绩的法宝

房地产中介销售顾问培训&#xff1a;房产中介经纪人提升销售业绩的法宝 一、了解客户需求 成功的沟通始于深入了解客户的需求和期望。房地产经纪人应该通过提问和倾听来收集信息&#xff0c;比如客户对房产的地理位置、价格范围、户型设计等的偏好。此外&#xff0c;了解客户的…

【算法专栏学习】成贤学院,程序员的福利站到了,判断子序列,经典算法实战。

&#x1f3c6;作者简介&#xff0c;普修罗双战士&#xff0c;一直追求不断学习和成长&#xff0c;在技术的道路上持续探索和实践。 &#x1f3c6;多年互联网行业从业经验&#xff0c;历任核心研发工程师&#xff0c;项目技术负责人。 &#x1f389;欢迎 &#x1f44d;点赞✍评论…

优思学院|怎样制作优秀的标准作业流程(SOP)?

我相信你一定去过众所周知的麦当劳。它在世界各地都有许多分店。不知道你是否曾在不同地方的麦当劳尝试过他们的美食&#xff0c;但你可能会好奇&#xff0c;为什么无论在世界哪个角落&#xff0c;麦当劳的食物口味都如此一致&#xff1f;它们是如何确保全球各地分店都能提供同…

中断——外部中断EXIT

终端可以分成外部中断和内部中断吗 文章目录 前言一、pandas是什么&#xff1f;二、使用步骤 1.引入库2.读入数据总结 前言 野火中断章节有这样一句话 【F103在内核水平上搭载了一个异常响应系统&#xff0c; 支持为数众多的系统异常和外部中断。 其中系统异常有8个&#xff…

「 网络安全常用术语解读 」网络攻击者的战术、技术和常识知识库ATTCK详解

引言&#xff1a;随着网络攻击手段的不断升级和多样化&#xff0c;网络安全领域面临着越来越严峻的挑战。为了帮助网络安全专业人员更好地识别和防御网络攻击&#xff0c;MITRE公司创建了ATT&CK框架&#xff0c;以提供一个统一且结构化的方法来描述网络攻击者的行为和技巧。…

Git与GitHub零基础教学

大家好&#xff0c;我是星恒&#xff0c;这个帖子给大家分享的是git和github的全套教程&#xff0c;包含github初始&#xff0c;git常用命令以及基本使用&#xff0c;git的ssh链接github&#xff0c;github使用token登录&#xff0c;github和idea的配合&#xff0c;一些平时常用…

SpringCloudConfig+SpringCloudBus+Actuator+Git实现Eureka关键配置属性热更新(全程不重启服务)

文章目录 前言1.痛点2.解决方案3.具体实现3.1搭建热配置服务3.2编写配置文件3.3搭建版本控制仓库3.4Eureka-Client引入以下依赖3.5Eureka-Client微服务编写以下配置bootstrap.yml提前加载3.6分别编写测试Controller3.7测试效果3.8下线场景压测 4.SpringCloudBus优化 前言 在上…

IDEA(十)2022版本 Services中服务窗口不显示端口号解决

目录 一、问题描述二、问题分析三、解决方案3.1 设置启动参数【生效】3.2 方法二&#xff1a;设置环境变量【不生效】3.3 方法三&#xff1a;删除缓存【不生效】 四、补充&#xff1a;如何手动控制端口显示 一、问题描述 我们在使用 IDEA 的过程中&#xff0c;会发现在 Servic…

Midjourney V6超深度评测;开源人工智能:创新的前沿

&#x1f989; AI新闻 &#x1f680; Midjourney V6超深度评测&#xff1a;超现实主义细节爆表 摘要&#xff1a;国外网友Andre Kovalev对Midjourney V6进行了超深度评测&#xff0c;发现V6相较于V5.2在图片真实感、细节表现方面有较大提升。V6拥有超现实主义的特点&#xff…