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

怕什么真理无穷进一步有近一步的欢喜说明本文是Spring Boot核心编程思想记录的笔记,书籍地址:Spring Boot编程思想(核心篇):本书已经简单读过一遍,在第一遍读的时候发现里面…

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

怕什么真理无穷

进一步有近一步的欢喜

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

说明

本文是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 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 应用上下文。简单源码如下:

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 应用

命令行的方式创建-实际不使用,仅供学习

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 文件中。图形化创建都会默认添加,使用命令行方式需要手动添加插件配置。

<build>
  <plugins>
    <plugin>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-maven-plugin</artifactId>
    </plugin>
  </plugins>
</build>

运行Spring Boot项目

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



执行方式

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   文件中如下内容:

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追加进去的。对应的依赖包

<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 输入 类的全类名。进行查找即可。

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



JarLauncher的实现原理

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

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



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方法:

// 启动应用程序。 此方法是初始入口点应该由一个子类被称为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用 “|” 分隔。

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 核心编程思想-第一部分-读书笔记「建议收藏」



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

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文件依赖第三方的类库的话,启动命令 如下:

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

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

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

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

 // 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_WorkSpace\springboot\springboot-core2.0-thinking\demoB\target>java -jar demoB-1.0-SNAPSHOT.jar
demoB-1.0-SNAPSHOT.jar中没有主清单属性

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

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。

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)方法

// 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;
}
// 注意 :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

<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>
<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 核心编程思想-第一部分-读书笔记「建议收藏」



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

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

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

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

F:\My_WorkSpace\springboot\springboot-core2.0-thinking\springboot2-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、导入插件

<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 即可。

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

image.png
3、打包 mvn package ,通过 java -jar 运行,不用外置容器
Spring Boot 核心编程思想-第一部分-读书笔记「建议收藏」
image.png
通过对应的地址即可访问服务。对比 tomcat7 和 Extract 文件目录。


Spring Boot 核心编程思想-第一部分-读书笔记「建议收藏」
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。

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

image.png

 @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

@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 注解

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.

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

如:

@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 ,添加自动配置类

# 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

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

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

@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~

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

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

不定期分享干货技术/

秘籍
,每天进步一点点
小的积累,能带来大的改变

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

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

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

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 二级反渗透1T/H二级反渗透纯水机 纯净水反渗透设备 反渗透设备

    反渗透技术原理反渗透技术是美国六十年代后期为解决宇航员在太空的饮水问题而研制的高新技术,也是目前的膜分离技术。简单地说,反渗透装置是利用半透膜在压力差的作用下使含盐水脱盐纯化的设备,它能有效地去除水中的无机盐、细菌、病毒、色素、热源、重金属离子及农药、化肥、清洁剂、胶体物质等污染物。反渗透膜孔径非常小,一般在2-10埃左右,而水中的各种离子杂质的直径约为几十埃,病毒、细菌的直径为几百至几十万埃,因此这些物质都是无法透过反渗透膜的,被截止在膜的浓水端,随浓水排出,透过反渗透膜的即是无菌,无毒害且富氧的纯净

    2022年4月17日
    48
  • python读取pkl_Python读取文件的一段内容

    python读取pkl_Python读取文件的一段内容以mnist.pkl为例方法一:然而我的还是会出现EOFError,无解,郁闷方法二:dataset=’mnist.pkl’datasets=load_data(dataset)train_set_x,train_set_y=datasets[0]valid_set_x,valid_set_y=datasets[1]test_set_x,test_s

    2025年10月10日
    3
  • SIGPIPE信号

    SIGPIPE信号当一个进程向某个已收到RST的套接字执行写操作时,内核向该进程发送一个SIGPIPE信号。该信号的默认行为是终止进程,因此进程必须捕获它以免不情愿地被终止。不论该进程是捕获了该信号并从其信号处理函数返回,还是简单地忽略该信号,写操作都将返回EPIPE错误。

    2022年5月7日
    50
  • Java 持久层概述

    Java 持久层概述JDBCJavaData 是一系列接口规范 Java 程序都是通过 JDBC 连接数据库的 然后通过其执行 SQL 对数据库进行操作 DBC 只是 Sun 公司定义的接口规范 具体实现是交由各个数据库厂商去实现的 因为每个数据库都有其特殊性 这些是 Java 规范没办法确定的 importjava sql importjava util logging Level importjava util logging Logger publicclas

    2025年11月17日
    3
  • EOS智能合约授权限制和数据存储

    EOS智能合约授权限制和数据存储

    2021年6月6日
    95
  • 下载jieba 库[通俗易懂]

    下载jieba 库[通俗易懂]步骤:1.打开命令行2.联网3.在C:\Users\User&gt;后面加语句 pipinstalljieba形成 C:\Users\User&gt;pipinstalljieba然后按回车,然后就开始安装在最后一行出现Successfullyinstalledjieba-0.39 证明安装成功  PS由于我们用pip来安装的jieba库嘛…

    2022年9月21日
    3

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号