Spring Boot 核心编程思想-第一部分-读书笔记「建议收藏」

2022-07-20 09:39:36 浏览数 (1)

大家好,又见面了,我是全栈君。

怕什么真理无穷

进一步有近一步的欢喜

说明

本文是Spring Boot核心编程思想记录的笔记,书籍地址:Spring Boot编程思想(核心篇):

本书已经简单读过一遍,在第一遍读的时候发现里面有些内容没太理解,现在在读下,这次读的过程中对之前的内容又有了许多新的理解和收获,现在整理记录下来,方便后续的回顾。

这篇文档会记录这本我的一些读书的思考,内容可能比较多,我也会根据理解去扩展一些自己的东西,加强自己对技术理解以及应用。

在开头在叨叨一下,技术书籍的评论或评分有时候也就是简单参考下,因为不同的人对书籍内容的理解是不同的。莎士比亚也说过:”一千个观众眼中有一千个哈姆雷特”。我是觉得一本书如果你能从中有些许的收获和思考,那都是有价值的,有时候可以忽略网上的评论或者评价。

PS:本文有大部分内容摘抄自书籍内容,如果内容前后不是很通顺,请阅读书籍原文,谢谢。

Spring Boot 核心编程思想

历史乃论述过去,绝不等同于过去

这本书的议题 以Spring Boot为核心,发散 Spring技术栈、JSR及Java。 ① 以全局视角,了解技术变迁的历程 ② 多方比较,理解特性原理 ③ 整合标准规范,掌握设计的哲学

Spring Boot 易学难精,它的核心是Spring Framework ,要理解Spirng Framework 又取决于对JSR规范及Java的熟悉度。

一:总览Spring Boot

第1章 初览Spring Boot

Spring Framework 时代

初览就是从Spring 开始,因为SpringBoot是在Spring基础上的封装。Spring Framework有两个核心技术:Ioc(Inversion of Control,控制反转)和DI(Dependency Inject ,依赖注入)。

Spring Framework 其实也是一种重复发明的轮子。在Java EE体系 Ioc 实现是JNDI(Java Naming and Directory Interface,Java命名和目录接口),DI是EJB的容器注入。

tips:谈谈对Spring IOC的理解-孤傲苍狼:https://www.cnblogs.com/xdp-gacl/p/4249939.html

Spring Boot简介

目的和意义:Build Anything。 两点: 1、Spring Boot 为快速启动且最小化配置的Spring应用设计

Spring Boot 基本可以不用配置就启动一个Spring应用,我们传统方式要搭建一个SpringMVC项目需要进行大量的xml配置

2、Spring Boot 具有一套固化的视图,该视图用于构建生产级别的应用

我的理解是通过maven 管理 Starter,将Spring Boot平台依赖的第三方类库进行固化,减少管理它们的烦恼。 比如:引用

Spring Boot的特性

六点:

  • 创建独立的Spring应用

独立是怎么理解,独立是相对于不独立,不独立就是需要依赖第三方的容器,Spring Boot 内嵌容器,不需要重新进行部署,所以可以称为独立。

  • 嵌入Web容器,Tomcat 、Jetty 、Undertow等
  • 固化的“Starter”依赖,简化构建
  • 自动装配,条件满足自动装配Spring或第三方类库
  • 提供一些运维的特性-外部化配置,endpoint ,如指标信息,健康检查,也可以自定义
  • 无代码生成,并且不需要xml。也可以引入xml,兼容旧项目

准备环境

JDK8 maven3 Idea Eclipse等

第2章 理解独立的Spring应用

特性中:创建独立的Spring应用 :1、为什么要独立的应用?2、 为什么是Spring应用,而非SpringBoot应用?

:1、独立的应用理解,Spring Boot 通过 Starter 直接或者间接引入依赖,然后使用自动装配,在结合自身的生命周期以及Spring Framework的生命周期,创建并启动嵌入式的Web容器。

多数Spring Boot应用场景中,程序用SpringApplication API引导应用。 应用分为:

  • Web应用
  • 传统Servlet
  • Spring Web MVC
  • Reactive Web (Spring boot 2.x -> Spring 5.0 WebFlux)
  • 非Web应用(服务提供、调度任务、消息处理等场景)

即:Spring Boot无须在像传统的Java EE应用那样,将文件打包成WAR文件或者EAR文件,并部署到JavaEE容器中运行。 也就是Spring Boot应用采用嵌入容器,独立于外部的容器,对应用的生命周期拥有完全自主的控制。

说明:

1、Spring Boot也支持传统的Web部署方式,这种方式属于一种兼容或过渡的手段。 2、误区 :Spring Boot嵌入式Web容器启动时间少于传统的Servlet容器,实际没有证据证明。 正确理解:Spring boot方便快捷的启动方式(启动方式不是启动时间),提升开发和部署效率。

:2、Spring Boot 嵌入式容器启动后,嵌入式容器成为应用的一部分,也属于Spring 应用上下文中的组件Beans,这些组件均由自动装配特性组装成Spring Bean定义(BeanDefinition)。随Spring应用上下文启动而注册并初始化。所以是Spring应用,也称为Spring Boot应用。

Tips:在传统的Spring应用中,外置容器通过启动脚本将其引导,随其生命周期回调执行Spring上下文的初始化。

如 Spring Web中的 ContextLoaderListener , 利用javax.servlet.ServletContext生命周期构建 Web ROOT Spring 应用上下文。简单源码如下:

代码语言:javascript复制
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
    public ContextLoaderListener() {
    }

    public ContextLoaderListener(WebApplicationContext context) {
        super(context);
    }

    public void contextInitialized(ServletContextEvent event) {
        // 事件回调 初始化webApplicationCotext
        this.initWebApplicationContext(event.getServletContext());
    }

    public void contextDestroyed(ServletContextEvent event) {
        this.closeWebApplicationContext(event.getServletContext());
        ContextCleanupListener.cleanupAttributes(event.getServletContext());
    }
}
//  ServletContextListener# javax.servlet.ServletContextEvent 是容器中的事件
public interface ServletContextListener extends EventListener {
    default void contextInitialized(ServletContextEvent sce) {
    }

    default void contextDestroyed(ServletContextEvent sce) {
    }
}

附 : Spring ContextLoaderListener解析

创建Spring 应用

命令行的方式创建-实际不使用,仅供学习
代码语言:javascript复制
mvn archetype:generate -DgroupId=xx-ss-springhboot -DartifactId=xxx-springboot-demo -Dversion=1.0-SNAPSHOT -DinteractiveMode=fase
  • mvn : Maven命令
  • archetype :插件名称
  • archetype:generate 插件目标
  • -D参数

说明:在Java启动命令中,通过-D命令行参数设置java 的系统属性:System.getProperties()。在Maven插件也可以通过此方式获取所需的参数。

  • interactiveMode:交互方式参数(设置为false,静默方式)

此方式创建的需要在Maven中添加 : 1、父pom 和 starter 等 spring-boot-starter-parent pom spring-boot-starter-web jar 2、创建启动类,ApplicationMain

图形化界面创建

三种方式: 1、使用 Spring Boot官方提供的 在线地址:http://start.spring.io/ 2、使用IDEA 创建Spring Boot应用 3、使用 Aliyun Java Initalizr : https://start.aliyun.com/

注:如果要构建Spring Boot应用可执行的JAR,则需要添加 spring-boot-maven-plugin 插件配置到 pom 文件中。图形化创建都会默认添加,使用命令行方式需要手动添加插件配置。

代码语言:javascript复制
<build>
  <plugins>
    <plugin>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-maven-plugin</artifactId>
    </plugin>
  </plugins>
</build>

运行Spring Boot项目

执行方式
可执行JAR的资源结构

主要关注生产的: xxx.jar 和 xxx.jar.original 文件。xxx.jar 文件大小 大于 xxx.jar.original。 结构参看 脑图。

FAT Jar 和 FAT War 执行模块-Spring-boot-loader

:为何 java -jar命令能够执行FAT Jar 文件呢?

答:java -jar 这个命令是Java 官方提供的,改命令引导的是标准可执行的JAR文件,根据Java官方文档规定:

java -jar 命令引导的具体启动类必须配置在MANIFEST.MF 资源的Main-Class属性中。 https://docs.oracle.com/javase/8/docs/technotes/tools/windows/java.html

[By default, the first argument that is not an option of the java command is the fully qualified name of the class to be called. If the -jar option is specified, its argument is the name of the JAR file containing class and resource files for the application. The startup class must be indicated by the Main-Class manifest header in its source code.]()

Spring Boot打包好的文件中,在 META-INF/MANIFEST.MF 文件中如下内容:

代码语言:javascript复制
Manifest-Version: 1.0
Implementation-Title: springboot2-core-ch02
Implementation-Version: 0.0.1-SNAPSHOT
Start-Class: org.learn.springboot2corech02.Springboot2CoreCh02Applicat
 ion
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
Build-Jdk-Spec: 1.8
Spring-Boot-Version: 2.2.1.RELEASE
Created-By: Maven Archiver 3.4.0
Main-Class: org.springframework.boot.loader.JarLauncher

Main-Class: org.springframework.boot.loader.JarLauncher ,发射器,启动类。(WAR的启动类:WarLauncher ) 引导类 Start-Class 也在此文件中配置。

注:org.springframework.boot.loader.JarLauncher 并非项目的文件,由Spring-boot-maven-plugin插件在repackage追加进去的。对应的依赖包

代码语言:javascript复制
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-loader</artifactId>
  <!--provided表明该包只在编译和测试的时候用,不会随项目发布-->
  <scope>provided</scope>
</dependency>

流程: 执行java -jar 后 启动类启动-Main-Class,然后启动类在启动后会读取 Start-Class 属性,并通过反射的方式将引导类中 main方法进行启动,从而启动Spring boot应用。(启动类和引导类处于同一进程中,并在启动前准备好Classpath)

小技巧:通过Class 名称搜索Maven中心仓库,查找对应的GAV信息。

1、 访问 https://search.maven.org/ 2、单击 “Class Search”,https://search.maven.org/classic/ 在跳转后的新的页面 Classname 输入 类的全类名。进行查找即可。

JarLauncher的实现原理

原理org.springframework.boot.loader.JarLauncher 是Spring boot封装的一个类,具体看源码分析。

代码语言:javascript复制
public class JarLauncher extends ExecutableArchiveLauncher {    static final String BOOT_INF_CLASSES = "BOOT-INF/classes/";    static final String BOOT_INF_LIB = "BOOT-INF/lib/";    public JarLauncher() {    }    protected JarLauncher(Archive archive) {        super(archive);    }    protected boolean isNestedArchive(Entry entry) {        return entry.isDirectory() ? entry.getName().equals("BOOT-INF/classes/") : entry.getName().startsWith("BOOT-INF/lib/");    }    // 执行java -jar的时候,将调用main方法,实际是调用JarLauncher#launch(args)    public static void main(String[] args) throws Exception {        (new JarLauncher()).launch(args);    }}

下面执行 launch方法:

代码语言:javascript复制
// 启动应用程序。 此方法是初始入口点应该由一个子类被称为public static void main(String[] args)方法。
protected void launch(String[] args) throws Exception {
    // 1、注册一个'java.protocol.handler.pkgs' 属性,并关联 URLStreamHandler 处理jar URLs 
    JarFile.registerUrlProtocolHandler();
    // 2、为指定的归档文件创建类装入器。
    ClassLoader classLoader = createClassLoader(getClassPathArchives());
    // 3、 启动
    launch(args, getMainClass(), classLoader);
}

此处URL关联的协议内容,可以翻看书籍阅读。下面大致总结: 1、URL关联的协议protocol 对应一种UrlStreamHandler的实现,在JDK中默认实现有 如HTTP、JAR等协议,如果要扩展协议,则必须继承UrlStreamHandler类,通常是配置 Java的系统属性 java.protocol.handler.pkgs ,追加多个package用 “|” 分隔。

代码语言:javascript复制
public static void registerUrlProtocolHandler() {
        String handlers = System.getProperty(PROTOCOL_HANDLER, "");
        System.setProperty(PROTOCOL_HANDLER,
                ("".equals(handlers) ? HANDLERS_PACKAGE : handlers   "|"   HANDLERS_PACKAGE));
        resetCachedUrlHandlers();
    }

Spring boot将 org.springframework.boot.loader 将 package 进行 追加,也就是在此包下有对应的Handler 类,

Spring boot覆盖了JDK内建Jar的协议,如何覆盖在此,URL#getURLStreamHandler 先读取 java.protocol.handler.pkgs 是否存在,不存在则取默认JDK的实现,在Spring boot因为上面追加了org.springframework.boot.loader ,就不取默认的handler了。

代码语言:javascript复制
static URLStreamHandler getURLStreamHandler(String protocol) {

        URLStreamHandler handler = handlers.get(protocol);
        if (handler == null) {

           // 省略....
        }

        return handler;
    }

其实这里代码还不是完全能够理解。

那么Spring Boot为何要覆盖默认JDK 的读取jar 的Handler呢?

Spring boot 的FAT Jar是一个独立的归档文件,除了包含传统的 Java Jar资源外,还有依赖的JAR文件, 被java -jar 引导时,内部依赖的JAR 文件无法被JDK内建的jar handler 实现 当作classPath,故替换。

如果不使用Spring Boot ,要启动传统的 jar文件,如果jar文件依赖第三方的类库的话,启动命令 如下:

代码语言:javascript复制
-- java 命令
java -cp ".:./*:lib/*"  com.test.Main
#-cp 和 -classpath 一样,是指定类运行所依赖其他类的路径,通常是类库,jar包之类,需要全路径到jar包,window上分号“;”
#分隔,linux上是分号“:”分隔。不支持通配符,需要列出所有jar包,用一点“.”代表当前路径。
#-cp 参数后面是类路径,是指定给解释器到哪里找到你的.class文件

-jar参数运行应用时,设置classpath的方法

附:自己的一些理解实践。

示例1:MANIFEST.MF 中没有mian方法

代码语言:javascript复制
 // MANIFEST.MF
Manifest-Version: 1.0
Implementation-Title: demoB
Implementation-Version: 1.0-SNAPSHOT
Build-Jdk-Spec: 1.8
Created-By: Maven Archiver 3.4.0


// 如何jar中没有main类
F:My_WorkSpacespringbootspringboot-core2.0-thinkingdemoBtarget>java -jar demoB-1.0-SNAPSHOT.jar
demoB-1.0-SNAPSHOT.jar中没有主清单属性

示例2:添加一个main方法,编译打包

代码语言:javascript复制
public class DemoBAppMain {

    public static void main(String[] args) {
        System.out.println("hello world");
    }
}
MANIFEST.MF 中依然不会出现Main-Class属性

示例3:使用插件,对示例2的代码进行打包。 Maven 生成打包可执行jar包

配置maven插件,然后用插件打包,执行 命令即可,启动打印 hello world .

2、获取类加载器,getClassPathArchives 根据名称前缀获取,如果匹配到 就是JarFileArchive。

代码语言:javascript复制
static final String BOOT_INF_CLASSES = "BOOT-INF/classes/";
static final String BOOT_INF_LIB = "BOOT-INF/lib/";

3、org.springframework.boot.loader.Launcher#launch(java.lang.String[], java.lang.String, java.lang.ClassLoader)方法

代码语言:javascript复制
// org.springframework.boot.loader.ExecutableArchiveLauncher#getMainClass
protected String getMainClass() throws Exception {
    Manifest manifest = this.archive.getManifest();
    String mainClass = null;
    if (manifest != null) {
        mainClass = manifest.getMainAttributes().getValue("Start-Class");
    }
    if (mainClass == null) {
        throw new IllegalStateException("No 'Start-Class' manifest entry specified in "   this);
    }
    return mainClass;
}
代码语言:javascript复制
// 注意 :mainClass 是 getMainClass()获取的值,即 Manifest#Start-Class
protected void launch(String[] args, String mainClass, ClassLoader classLoader) throws Exception {
    // 设置类加载器到当前线程中
    Thread.currentThread().setContextClassLoader(classLoader);
    // 创建 MainMethodRunner,并执行 run方法
    createMainMethodRunner(mainClass, args, classLoader).run();
}

// mainClassName 其实是 Start-Class ,这里使用反射的方式 执行 Start-Class 属性中类的 main方法
public void run() throws Exception {
    Class<?> mainClass = Thread.currentThread().getContextClassLoader().loadClass(this.mainClassName);
    Method mainMethod = mainClass.getDeclaredMethod("main", String[].class);
    mainMethod.invoke(null, new Object[] { this.args });
    // 如果底层方法是静态的,则指定的obj参数将被忽略。 它可能为null。 具体Invoke方法可看jdk文档。
}

我们平时在IDEA直接执行 Start-Class 引导类,其实是跳过了 JarLauncher的执行过程。

WarLauncher的实现原理

war 和Jar 差异很小,主要区别在于项目文件和JAR Class Path路径不同,具体可以 修改pom进行打包,然后解压进行对应差异。

打包war文件是一种兼容措施,既能 被WarLauncher 启动,又能兼容Servlet容器环境。(WarLauncher 也是通过 java -jar 引导)。

也就是JarLauncher 和 WarLauncher 本质上 无差别,建议 Spring boot应用使用非传统Web部署时,尽可能使用JAR归档的方式。

第3章 理解固化Maven依赖

理解 spring-boot-starter-parent pom 和 spring-boot-dependencies

固化的Maven依赖,实际上是 在Springboot的特性中:固化的“Starter”依赖,简化构建 。 ** Spring boot 采用Maven来进行固化处理,只需理解 spring-boot-starter-parent pom 和 spring-boot-dependencies

代码语言:javascript复制
<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <!--<version>2.2.0.RELEASE</version>-->
        <version>2.2.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->

    </parent>
代码语言:javascript复制
<dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

层级: spring-boot-dependencies 父 :定义了版本号

  • spring-boot-starter-parent 子

可以单独引入spring-boot-dependencies 作为 项目的父 pom。但是有几点是需要注意的(和spring-boot-starter-parent区别,实际上区别是因为spring-boot-starter-parent已经实现了)。

第一:可能存在 maven-war-plugin 插件版本不一致问题。在Spring boot2.0 – 版本前

maven-war-plugin2.2 中,打包规则是必须存在Web应用部署描述文件WEB-INF/web.xml ,而3.1.0版本调整该默认行为

第二:引入 spring-boot-maven-plugin 插件时,需要配置 repackage元素,否则不会添加Spring boot的引导依赖。(repackage在 spring-boot-starter-parent 中默认添加)

第三:根据习惯,通常不会将 spring-boot-dependencies 作为Maven项目的

总结 :Spring boot 利用 Maven的依赖管理特性,进而固化其Maven依赖,固化的“Starter”依赖,简化构建 特性 ,并非Spring Boot专属,但是Spring 技术栈却将其利用的相当充分。 ** **

小技巧:利用 Maven Dependency 插件分析依赖树结构

mvn dependency:tree -Dincludes=*:spring-boot-starter-tomcat

代码语言:javascript复制
F:My_WorkSpacespringbootspringboot-core2.0-thinkingspringboot2-core-ch02>mvn dependency:tree -Dincludes=*:spring-boot-starter-tomcat
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building springboot2-core-ch02 0.0.1-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-dependency-plugin:3.1.1:tree (default-cli) @ springboot2-core-ch02 ---
[INFO] org.learn:springboot2-core-ch02:jar:0.0.1-SNAPSHOT
[INFO] - org.springframework.boot:spring-boot-starter-web:jar:2.2.1.RELEASE:compile
[INFO]    - org.springframework.boot:spring-boot-starter-tomcat:jar:2.2.1.RELEASE:compile
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.784 s
[INFO] Finished at: 2020-04-27T23:50:43 08:00
[INFO] Final Memory: 28M/307M
[INFO] ------------------------------------------------------------------------

❓︎思考:为什么 当前Spring boot仅仅依赖 spring-boot-starter-tomcat 就能引导 Tomcat 容器,并且该 容器嵌入当前应用,不需要预安装?

第4章 理解嵌入式Web容器

首先理解嵌入容器,基本上大一点的Web容器,自身都提供了嵌入式容器的支持。然后大致就能明白SpingBoot的嵌入式容器,Spring Boot对嵌入式容器进行了封装。

Servlet规范和实现三种容器版本的对应关系: Servlet4.0规范 :tomcat 9.x Jetty9.x Undertow2.x Servlet3.1规范:tomcat 8.x Jetty8.x Undertow1.x Servlet3.0规范 :tomcat 7.x Jetty7.x N/A Servlet2.5规范 :tomcat 6.x Jetty6.x N/A

热门的Web容器实现均支持嵌入式容器方式,Spring Boot并非独创,只是技术的整合创新。 **

嵌入式Servlet Web容器

Spring Boot支持三种:tomcat Jetty Undertow。

  • 2.x 对应Servlet3.1规范,JDK 1.8
  • 1.x 对应Servlet 3.0 规范,JDK1.6

这里需要理解 Tomcat 嵌入式容器,比如之前的工程中 1、在maven 加入tomcat的插件,不用外置Tomca就可以在工程中启动项目。 2、但是打的包依然是要放入外在Tomcat容器中,也可以使用Tomcat插件配置打包,打包后用java -jar也可以运行

Tomcat插件演示

官方最高支持 tomcat7 ,tomcat8 社区维护。具体可查询网上博文。 1、导入插件

代码语言:javascript复制
<plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <version>2.1</version>
                <configuration>
                    <port>8088</port>
                    <path>/</path>
                </configuration>
                <executions>
                    <execution>
                        <id>tomcat-run</id>
                        <goals>
                            <goal>exec-war-only</goal>
                        </goals>
                        <phase>package</phase>
                        <configuration>
                            <!-- ServletContext path -->
                            <path>/</path>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

2、在maven 运行:tomcat7:run , tomcat8后运行 tomcat:run 即可。

image.png 3、打包 mvn package ,通过 java -jar 运行,不用外置容器

image.png 通过对应的地址即可访问服务。对比 tomcat7 和 Extract 文件目录。

image.png

Tomcat 插件插件生成jar包 和Spring boot 生成jar的区别

1、Tomcat maven插件,本质上还是传统的Tomcat部署,先将WEB应用打包为ROOT.war ,在启动的时候在解压到webapps目录下面;Spring Boot 2.0 的实现,利用嵌入式Tomcat API构建 为 TomcatWebServer Bean,由Sping应用上下文将其引导,嵌入式tomcat组件(Context、Connector)的运行,以及ClassLoader的装载均由Spring Boot框架代码实现。

2、Tomcat Maven 插件打包的Jar或者War 是非 FAT模式。简单说就是存在压缩的情况。Spring Boot maven 插件 采用零压缩模式。 零压缩相当于 :jar -0 参数。

总结:传统的Servlet容器是将压缩的WAR文件解压到对应的目录,然后在加载该目录的资源。 Spring Boot 可执行的 WAR文件在不解压当前文件的前提下依然可以读取其中的资源。

这也是就Spring boot loader 为何要覆盖 内建JAR 协议的URLStreamhandler 的原因

Spring Boot中使用tomcat

默认加入 spring-boot-starter-web 就会引入 tomcat。 如果要切换 Jetty 或者Undertow ,先tomcat然后在引入对应的容器jar即可。

嵌入式Reactive Web容器

默认如果 同时存在 starter-web 和 starter-webflux ,webflux 会被忽略。

理解WebServerInitializedEvent

Web服务器已初始化事件

通过监听此事件,可以获取WebServer 以及端口。 WebServerInitializedEvent

  • ReactiveWebServerInitializedEvent
  • ServletWebServerInitializedEvent

有两个子类,一个监听ServletWeb ,一个是ReactiveWeb。WebServerInitializedEvent包括这两种。(本质Spring的事件监听) 通过 org.springframework.boot.web.context.WebServerInitializedEvent#getWebServer()获取WebServer。

image.png

代码语言:javascript复制
 @Bean
    public ApplicationRunner runner(WebServerApplicationContext context){
        return args -> {
            System.out.println("当前的WebServer : "   context.getWebServer().getClass().getName());

        };
    }

/**
     * 使用监听器 使代码更健壮,支持web和非web方式
     * */
    @EventListener(WebServerInitializedEvent.class)
    public void onWebServerReady(WebServerInitializedEvent event) {
        System.out.println("当前监听的WebServer :"   event.getWebServer().getClass().getName());
    }

_

第5章 理解自动装配

要理解 自动装配,先理解一句话:没有无缘无故的爱,也没有无缘无故的恨 Spring boot 要不会无缘无故的给我们加载配置类。

在使用Springboot的时候,当我们将 “starter”添加到应用Class path 时,其关联的特性随应用启动而自动装载,这是Spring boot的亮点, 它的原理是什么呢?

原理简述:Spring Boot 有一个Spring boot autoconfigure Jar里面配置了大量自动装配的配置类,如JDBC 、cache、AOP等,这些 配置类均在 spring.factories 中配置,以 org.springframework.boot.autoconfigure.EnableAutoConfiguration 为key 保存,在Spring boot 启动的时候加载,在结合@Conditional 相关注解进行判断,最终将需要的配置类加载,并激活默认配置信息。

自动装配的前提

1、将需要的jar添加到应用中 2、激活自动装配注解 @EnableAutoConfigure/ @SpringBootApplition 标注在 @Configution 的类上

Spring 中 @Configution 类被扫描的三种方式:

  • ClassPathApplicationContext ,配置xml
  • AnnotationConfigurationApplicationContext
  • @Import 注解
  • @ComponentScan注解
理解 @SpringBootApplication 注解语义

@SpringBootApplication 组合注解

@SpringBootConfiguration @EnableAutoConfiguration @ComponentScan

代码语言:javascript复制
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {}
  • @SpringBootConfiguration : Spring boot – **@since **1.4.0 : 标记为配置类
  • @Configuration
    • @Component
  • @EnableAutoConfiguration :Spring boot 注解。激活自动装配

其中核心注解:@AutoConfigurationPackage 和 @Import({AutoConfigurationImportSelector.class}) 后面详细介绍

  • @ComponentScan :spring的注解 。组件扫描,激活@Compnent注解的组件

思考:Spring 的 @ComponentScan注解是如何识别Spring boot 的@SpringBootConfiguration 注解?

这里就是要理解 @Component 的 “派生性”,后续会进行总结,简单的说,就是@ComponentScan 能够扫描到 注解了 @Component的组件,以及其派生(继承)此注解的注解的组件。

@SpringBootApplication 属性别名

属性均标注了 AliasFor 注解

代码语言:javascript复制
public @interface SpringBootApplication {
    @AliasFor(annotation = EnableAutoConfiguration.class)
    Class<?>[] exclude() default {};

    @AliasFor(annotation = EnableAutoConfiguration.class)
    String[] excludeName() default {};

    @AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
    String[] scanBasePackages() default {};

    @AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
    Class<?>[] scanBasePackageClasses() default {};
}

理解 @AliasFor Spring 注解,**@since **4.2

{ **@code *@AliasFor} is an annotation that is used to declare aliases for annotation attributes.

能够将一个或多个注解的属性“别名” 注解到某个注解中。

如:

代码语言:javascript复制
@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
    String[] scanBasePackages() default {};

org.springframework.boot.autoconfigure.SpringBootApplication#scanBasePackages 具有 ComponentScan#basePackages 的能力。

提示: 1、正常情况下,这个注解@SpringBootApplication 会标注在启动引导类上,但是此注解并非限制只能标注在引导类。 2、@SpringBootApplication 和 @EnableAutoConfiguration 都能激活自动装配,但是对于被标注的类的Bean类型存在差异; @SpringBootApplication “继承”@Configuration 与CGLIB 提升特性。 @EnableAutoConfiguration无。

理解自动装配机制

首先理解条件注解@Conditonal** **及其相关注解 如 @ConditionalOnClass @ConditionalOnMissingBean等,结合@Configuration 使用。

创建自动装配类三步走: 第一:创建配置类(WebConfiguration),标注 @Configuration,实现对应装配逻辑 第二:创建自动装配类 XXXAutoConfiguration(WebAutoConfiguration),标注 @Configuration 和 @Import(WebConfiguration.class) 第三:在项目下 新建 src/main/resources/META-INF/spring.factories ,添加自动配置类

代码语言:javascript复制
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=
org.learn.springboot2corech02.config.WebAutoConfiguration

这种方式中Spring boot中不需要配置xml,完全的注解驱动开发,这样解析注解 所带来的时间成本 直接影响了应用的启动速度。Spring Framework 5.0 引入 @Index 注解。增加索引,减少运行时候的性能消耗。自己写的一篇博文:SpringFramework5.0 @Indexed注解 简单解析

第6章 理解Production-Ready特性

1、为生产准备的特性 、立足于DevOps

  • 指标监控
  • 健康检查
  • 外部化配置

2、Spring Boot Actuator ,监视和管理Spring应用 ,通过HTTP或者JMX进行交互,暴露一些endpoints,也可以自定义endpoint。

  • beans : 当前Spring 应用 上下文的SpringBean 完整列表
  • Conditions :显示当前应用 所有的配置类和自动装配类的条件评估结果
  • env :暴露 Spring ConfigurableEnvironment 中的PropertySource 属性
  • health (默认) :应用的健康信息
  • info(默认) :显示任意应用的信息

通过 management.endpoints.web.exposure.include=info,health,shutdown,loggers — 暴露shutdown端点,可以进行优雅关闭服务 management.endpoint.shutdown.enabled=true

代码语言:javascript复制
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

自定义Endpoints方法:Spring boot 2.x

代码语言:javascript复制
@Endpoint(id="dufy")
@Configuration
public class MyEndPointConfig {

    @ReadOperation(produces = MediaType.APPLICATION_JSON_VALUE)
    public Object getMyEndPoint(){
        Map map = new HashMap<>();
        map.put("code", 0);
        map.put("msg", "success");
        return map;
    }
}

// 注意 :management.endpoints.web.exposure.include= 配置
  • @EndPoint中的id不能使用驼峰法,需要以-分割。
  • @Spring Boot会去扫描@EndPoint注解下的@ReadOperation, @WriteOperation, @DeleteOperation注解,分别对应生成Get/Post/Delete的Mapping。注解中有个produces参数,可以指定media type, 如:application/json等。

3、理解“外部化配置” 外部化配置的顺序。 读取外部化配置方式:

  • Bean @Value
  • Spring environment 读取
  • @ConfigurationProperties 绑定到结构化对象

4、理解 “规约大于配置”

5、Spring Boot 做为微服务中间件,Spring Framework 是Spring Boot 的“基础设施”,Spring boot的基本特性均来自Spring Framework。 Spring Boot作为Spring cloud 基础设施。在Spring Cloud中,致力于为开发人员提供快速构建通用的分布式系统,特性:

  • 分布式配置
  • 服务注册和发现
  • 路由
  • 服务调用
  • 负载均衡
  • 熔断机制
  • 分布式消息
  • …. 最后在说一下,这是第一章的内容总结和整理,如果对上述内容感兴趣,可以去看看书籍,谢谢你的阅读。

See you next good day

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/120956.html原文链接:https://javaforall.cn

0 人点赞