狂神说——SpringBoot学习

spring官网
SpringBoot官网
spring-security版本下载
狂神官网学习 也可以搜索B站 (狂神说)
学习网站:https://www.bilibili.com/video/BV1PE411i7CV?p=1

狂神说——SpringBoot学习笔记

  • SpringBoot
    • Springboot简介
    • 什么是微服务?
    • 注解
    • SpringApplication
    • 第一个SpringBoot程序
    • 更改banner图标
  • 02 运行原理探究
    • 父依赖
    • 启动器(pring-boot-starter)
    • 主程序
    • 结论:
    • 启动 SpringApplication.run(xxx.class, args);
  • 03 yaml语法学习
    • 配置文件
    • YAML
    • 注入配置文件
    • yaml注入配置文件
    • 加载指定的配置文件
    • 配置文件占位符
    • 回顾properties配置
    • 对比小结
  • 04 JSR303数据校验
    • 多环境切换
    • 多配置文件
    • yaml的多文档块
    • 配置文件加载位置
    • 拓展运维小技巧指定位置加载配置文件
  • 05 自动配置原理
    • 分析自动配置原理
    • 自动配置——精髓
    • @Conditional
    • 自动配置类是否生效
  • 06 Web 开发
    • Thymeleaf
  • SpringBoot 整合
  • 07 整合JDBC
    • SpringData简介
    • 整合JDBC
  • 08 整合Druid
    • 集成Druid
      • Druid简介
      • 配置数据源
      • 配置Druid数据源监控
  • 09 整合mybatis
  • 10 整合 SpringSecurity安全框架
    • 登录和拦截
    • 补充:前端权限验证
  • 11 整合Shiro
    • Shiro简介
    • Hello World
    • SpringBoot 集成 Shiro
    • Shiro实现登录拦截
    • Shiro实现用户认证
    • Shiro整合Mybatis
    • Shiro 实现用户授权
    • Shiro整合Thymeleaf
    • Shiro案例的所有代码
  • 12 整合 swagger
  • 13 异步任务
  • 14 邮件任务
  • 15 定时任务
  • 16 SpringBoot整合Redis
  • 17 整合Dubbo+Zookeeper(分布式 dubbo+ zookeeper+ springboot)
    • 分布式
    • Dubbo
    • RPC(两大核心:通讯,序列化)
    • Zookeeper
  • 总结

SpringBoot

Springboot简介

回顾什么是Spring

  • Spring是一个开源框架,2003 年兴起的一个轻量级的Java 开发框架,作者:Rod Johnson 。

  • Spring是为了解决企业级应用开发的复杂性而创建的,简化开发。

Spring是如何简化Java开发的

为了降低Java开发的复杂性,Spring采用了以下4种关键策略:

  1. 基于POJO的轻量级和最小侵入性编程,所有东西都是bean;

  2. 通过IOC,依赖注入(DI)和面向接口实现松耦合;

  3. 基于切面(AOP)和惯例进行声明式编程;

  4. 通过切面和模版减少样式代码,RedisTemplatexxxTemplate

什么是SpringBoot

学过 javaweb 的同学就知道,开发一个web应用,从最初开始接触Servlet结合Tomcat, 跑出一个Hello Wolrld程序,是要经历特别多的步骤;后来就用了框架Struts,再后来是SpringMVC,到了现在的SpringBoot,过一两年又会有其他web框架出现;你们有经历过框架不断的演进,然后自己开发项目所有的技术也在不断的变化、改造吗?建议都可以去经历一遍;

言归正传,什么是SpringBoot呢,就是一个javaweb的开发框架,和SpringMVC类似,对比其他javaweb框架的好处,官方说是简化开发,约定大于配置, you can “just run”,能迅速的开发web应用,几行代码开发一个http接口。

所有的技术框架的发展似乎都遵循了一条主线规律:从一个复杂应用场景 衍生 一种规范框架,人们只需要进行各种配置而不需要自己去实现它,这时候强大的配置功能成了优点;发展到一定程度之后,人们根据实际生产应用情况,选取其中实用功能和设计精华,重构出一些轻量级的框架;之后为了提高开发效率,嫌弃原先的各类配置过于麻烦,于是开始提倡“约定大于配置”,进而衍生出一些一站式的解决方案。

是的这就是Java企业级应用->J2EE->spring->springboot的过程。

随着 Spring 不断的发展,涉及的领域越来越多,项目整合开发需要配合各种各样的文件,慢慢变得不那么易用简单,违背了最初的理念,甚至人称配置地狱。Spring Boot 正是在这样的一个背景下被抽象出来的开发框架,目的为了让大家更容易的使用 Spring 、更容易的集成各种常用的中间件、开源软件;

Spring Boot 基于 Spring 开发,Spirng Boot 本身并不提供 Spring 框架的核心特性以及扩展功能,只是用于快速、敏捷地开发新一代基于 Spring 框架的应用程序。也就是说,它并不是用来替代 Spring 的解决方案,而是和 Spring 框架紧密结合用于提升 Spring 开发者体验的工具。Spring Boot 以约定大于配置的核心思想,默认帮我们进行了很多设置,多数 Spring Boot 应用只需要很少的 Spring 配置。同时它集成了大量常用的第三方库配置(例如 Redis、MongoDB、Jpa、RabbitMQ、Quartz 等等),Spring Boot 应用中这些第三方库几乎可以零配置的开箱即用。

简单来说就是SpringBoot其实不是什么新的框架,它默认配置了很多框架的使用方式,就像maven整合了所有的jar包,spring boot整合了所有的框架 。

Spring Boot 出生名门,从一开始就站在一个比较高的起点,又经过这几年的发展,生态足够完善,Spring Boot 已经当之无愧成为 Java 领域最热门的技术。

springboot的主要优点

  • 为spring开发者更快入门
  • 开箱即用,提供各种默认配置来简化项目配置
  • 内嵌式容器,简化web项目
  • 没有冗余代码生成和XML配置的要求

微服务阶段:

  • JavaSE:OOP
  • mysql:持久化
  • HTML+CSS+JS+jQuery+框架:视图框架不熟练,css不好
  • javaweb:独立开发MVC三层架构的网站:原始
  • SSM:框架:简化我们的开发流程,配置也开始较为复杂
  • war:Tomcat运行
  • spring再简化:springboot-jar:内嵌Tomcat;微服务架构
  • 服务越来越多:springcloud

约定:不需要配置或者配置很少的东西,默认存在可用的东西叫好的约定

程序=数据结构+算法(集合框架)【程序猿】
程序=面向对象+框架【码农】

前端为主的MV*时代:

  • MVC(同步通信为主【易阻塞】)

      MVC 三层架构: (Model-View-Controller)  
      	 Model:数据和业务
      	 View:HTML
      	 Controller:交接
    
  • MVP(异步通信为主)

  • MVVM (异步通信为主)

      MVVM 微服务架构:Model-View-ViewModel ,ViewModel 核心)MVVM来源于MVC
      Model : 模型层,在这里表示JavaScript对象
      View : 视图层,在这里表示DOM (HTML操作的元素)
      ViewModel : 连接视图和数据的中间件,Vue.js就是MVVM中的ViewModel层的实现者在MVVM架构中,	
      			是不允许数据和视图直接通信的,只能通过ViewModel来通信,而ViewModel就是定义了一个Observer观察者;
      			ViewModel 能够观察到数据的变化,并对视图对应的内容进行更新;
      			ViewModel 能够监听到视图的变化,并能够通知数据发生改变
    
      MVVM优点:低耦合、可复用、独立开发、可测试
      
      优点展开详细说明:
      MVVM模式和MVC模式一样,主要目的是分离视图(View)和模型(Model),有几大好处:
      1. 低耦合:视图(View)可以独立于Model变化和修改,一个ViewModel可以绑定到不同的View上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变。
      2. 可复用:你可以把一些视图逻辑放在一个ViewModel里面,让很多View重用这段视图逻辑。
      3. 独立开发:开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于页面设计。
      4. 可测试:界面素来是比较难于测试的,而现在测试可以针对ViewModel来写。
    

至此,我们就明白了,Vue.js 就是一个MVVM的实现者,他的核心就是实现了DOM监听与数据绑定

springboot常用的starter有哪些

spring-boot-starter-web   嵌入tomcat和web开发需要servlet与jsp支持
spring-boot-starter-data-jpa  数据库支持
spring-boot-starter-data-redis   redis数据库支持
spring-boot-starter-data-solr   solr支持
mybatis-spring-boot-starter   第三方的mybatis集成starter

什么是微服务?

微服务论文(英文版)
微服务论文(中文版)
微服务架构的系统是一个分布式的系统,按业务进行划分为独立的服务单元,解决单体系统的不足,同时也满足越来越复杂的业务需求。

微服务架构就是将单一程序开发成一个微服务,每个微服务运行在自己的进程中,并使用轻量级的机制通信,通常是HTTP RESTFUL API。这些服务围绕业务能力来划分,并通过自动化部署机制来独立部署。这些服务可以使用不同的编程语言,不同数据库,以保证最低限度的集中式管理。

总结起来微服务就是将一个单体架构的应用按业务划分为一个个的独立运行的程序即服务,它们之间通过HTTP协议进行通信(也可以采用消息队列来通信,如RoocketMQ,Kafaka等),可以采用不同的编程语言,使用不同的存储技术,自动化部署(如Jenkins)减少人为控制,降低出错概率。服务数量越多,管理起来越复杂,因此采用集中化管理。例如Eureka,Zookeeper等都是比较常见的服务集中化管理框架。

springboot可以自定义启动时的图标,默认为spring字样,如果想拥有自己的图标也可以在这个网站获取 springboot自定义banner图标

注解

@SpringBootApplication【springboot的配置】
	@Configuration【spring配置类】
	@Component【说明这也是spring的一个组件】

@EnableAutoConfiguration 【自动配置】
	@AutoConfigurationPackage【自动配置包】
		@Import(AutoConfigurationPackages.Registrar.class)【自动配置‘包注册’】
	@Import(AutoConfigurationImportSelector.class)  【自动导入包的核心(自动配置导入选择)】        
		List<String> configurations=getCandidateConfigurations(annotationMetadata,attributes);

SpringApplication

主入口里面的SpringApplication 类,这个类主要做了4个事情:

  1. 推断应用的类型是普通的项目还是Web项目
  2. 查找并加载所有可用初始化器。设置initializers 属性
  3. 找出所有的应用程序监听器,设置到listeners 属性中
  4. 推断并设置main方法的定义类,找到运行的主类
    狂神说——SpringBoot学习_第1张图片
  • application.properties:只能以键值对格式存在,书写的中文编译可能会乱码
  • application.yml:可(赋值)表示对象、数组、纯量 ,也支持函数、正则表达式等。也可以使用${xxx}表达获取前面已经定义的变量(属性)
    例如 :
    spring:
      application:
        name: springWeb
        instance-id: ${spring.application.name}:${server.port} #取自前面已定义的属性
    

yml格式的特点:

  1. 大小写敏感
  2. 使用缩进表示层级关系
  3. 缩进时不允许使用Tab键,只允许使用空格
  4. 缩进的空格数目不重要,只要相同层级的元素左侧对齐即可. (通常采用两个空格表示一个缩进)

YAML 语言教程——阮一峰

YAML 支持的数据结构有三种。

  • 对象:键值对的集合,又称为映射(mapping)/ 哈希(hashes) / 字典(dictionary)
  • 数组:一组按次序排列的值,又称为序列(sequence) / 列表(list)
  • 纯量(scalars):单个的、不可再分的值
@ConfigurationProperties @Value
功能 批量注入配置文件中的属性 一个个指定
松散绑定 支持 不支持
SpEL 不支持 支持
JSR303数据校验 支持 不支持
复杂类型封装 支持 不支持

接口调用优先级:

1. file:./config/
2. file:./
3. classpath:/config/
4. classpath:/

狂神说——SpringBoot学习_第2张图片
狂神说——SpringBoot学习_第3张图片

静态资源导入探究:
总结:

  1. 在springboot,我们可以使用以下方式处理静态资源

     (1)webjars: localhost:8080/webjars/
     (2)public,static,/**,resources : localhost:8080/xxx.jar
    
  2. 优先级:resource>static(默认)>public

第一个SpringBoot程序

准备工作

我们将学习如何快速的创建一个Spring Boot应用,并且实现一个简单的Http请求处理。通过这个例子对Spring Boot有一个初步的了解,并体验其结构简单、开发快速的特性。

我的环境准备跟开发工具:jdk1.8、Maven-3.6.1、SpringBoot 最新版、IDEA

创建基础项目说明

Spring官方提供了非常方便的工具让我们快速构建应用
Spring Initializr:https://start.spring.io/

项目创建方式一:在官网使用Spring Initializr 的 Web页面创建项目

1、打开 https://start.spring.io/
2、填写项目信息
3、点击”Generate Project“按钮生成项目;下载此项目
4、解压项目包,并用IDEA以Maven项目导入,一路下一步即可,直到项目导入完毕。
5、如果是第一次使用,可能速度会比较慢,包比较多、需要耐心等待一切就绪。

项目创建方式二:使用 IDEA 直接创建项目

1、创建一个新项目
2、选择spring initalizr , 可以看到默认就是去官网的快速构建工具那里实现
3、填写项目信息
4、选择初始化的组件(初学勾选 Web 即可)
5、填写项目路径
6、等待项目构建成功

项目结构分析

通过上面步骤完成了基础项目的创建。就会自动生成以下文件。

1、程序的主启动类
2、一个 application.properties 配置文件
3、一个 测试类
4、一个 pom.xml

pom.xml 分析

打开pom.xml,看看Spring Boot项目的依赖:

<!-- 父工程依赖 -->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.5.RELEASE</version>
    <relativePath/>
</parent>

<dependencies>
    <!-- web场景启动器 -->
    <!--web依赖:tomcat,dispatcherServlet,xml...-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!--spring-boot-starter:所有的springboot依赖都是使用这个开头的-->
    <!-- springboot单元测试 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
        <!-- 剔除依赖 -->
        <exclusions>
            <exclusion>
                <groupId>org.junit.vintage</groupId>
                <artifactId>junit-vintage-engine</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
</dependencies>

<build>
    <plugins>
        <!-- 打包插件 -->
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>

<!--在工作中,很多情况下我们打包是不想执行测试用例的,可能是测试用例不完善,或是测试用例会影响数据库数据,跳过测试用例执 -->
        <!--<plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <configuration>
               跳过项目运行测试用例
                <skipTests>true</skipTests>
            </configuration>
        </plugin>-->
        
    </plugins>
</build>

如上所示,主要有四个部分:

  • 项目元数据:创建时候输入的Project Metadata部分,也就是Maven项目的基本元素,包括:groupId、artifactId、version、name、description等

  • parent:继承spring-boot-starter-parent的依赖管理,控制版本与打包等内容

  • dependencies:项目具体依赖,这里包含了spring-boot-starter-web用于实现HTTP接口(该依赖中包含了Spring MVC),官网对它的描述是:使用Spring MVC构建Web(包括RESTful)应用程序的入门者,使用Tomcat作为默认嵌入式容器。spring-boot-starter-test用于编写单元测试的依赖包。更多功能模块的使用将在后面逐步展开。

  • build:构建配置部分。默认使用了spring-boot-maven-plugin,配合spring-boot-starter-parent就可以把Spring Boot应用打包成JAR来直接运行。

编写一个http接口

  1. 在主程序的同级目录下,新建一个controller包,一定要在同级目录下,否则识别不到
    狂神说——SpringBoot学习_第4张图片
  2. 在包中新建一个HelloController类
@RestController
public class HelloController {

    @RequestMapping("/hello")
    public String hello() {
        return "Hello World";
    }
    
}
  1. 编写完毕后,从主程序启动项目,浏览器发起请求,看页面返回;控制台输出了 Tomcat 访问的端口号!
    狂神说——SpringBoot学习_第5张图片简单几步,就完成了一个web接口的开发,SpringBoot就是这么简单。所以我们常用它来建立我们的微服务项目!

将项目打成jar包,点击 maven的 package

如果打包成功,则会在target目录下生成一个 jar 包
狂神说——SpringBoot学习_第6张图片
如果遇到以上②的错误,可以配置打包时跳过项目运行测试用例(上面有讲过的,注释掉的)

<!--在工作中,很多情况下我们打包是不想执行测试用例的可能是测试用例不完善,或是测试用例会影响数据库数据 跳过测试用例执-->
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <configuration>
        <!--跳过项目运行测试用例-->
        <skipTests>true</skipTests>
    </configuration>
</plugin>

打成了jar包后,就可以在任何地方运行了!OK
狂神说——SpringBoot学习_第7张图片
浏览器运行结果(上图第⑤步):
狂神说——SpringBoot学习_第8张图片

更改banner图标

如何更改启动时显示的字符拼成的字母,SpringBoot呢?
只需一步:到项目下的 resources 目录下新建一个banner.txt 即可。

图案可以到:https://www.bootschool.net/ascii 这个网站生成,然后拷贝到文件中即可

02 运行原理探究

我们之前写的HelloSpringBoot,到底是怎么运行的呢,Maven项目,我们一般从pom.xml文件探究起;

父依赖

pom.xml

  • spring-boot-dependencies:核心依赖在父工程中!
  • 我们在写或者引入一些Springboot依赖的时候,不需要指定版本,就因为有这些版本仓库
  1. 其中它主要是依赖一个父项目,主要是管理项目的资源过滤及插件!
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.3.1.RELEASE</version>
    <relativePath/> 
    <!-- lookup parent from repository -->
</parent>
  1. 点进去,发现还有一个父依赖
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-dependencies</artifactId>
    <version>2.3.1.RELEASE</version>
</parent>
  1. 这里才是真正管理SpringBoot应用里面所有依赖版本的地方,SpringBoot的版本控制中心;

  2. 以后我们导入依赖默认是不需要写版本;但是如果导入的包没有在依赖中管理着就需要手动配置版本了;

启动器(pring-boot-starter)

  • 依赖
<dependency>        								 	
	<groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
</dependency>
  • springboot-boot-starter-xxx,说白了就是Springboot的启动场景

  • 比如spring-boot-starter-web,他就会帮我们自动导入web的所有依赖

  • springboot会将所有的功能场景,都变成一个个的启动器

  • 我们要使用什么功能,就只需要找到对应的启动器就好了start

主程序

默认的主启动类

//@SpringBootApplication 来标注一个主程序类
//说明这是一个Spring Boot应用
@SpringBootApplication
public class SpringbootApplication {
   public static void main(String[] args) {
     //以为是启动了一个方法,没想到启动了一个服务
      SpringApplication.run(SpringbootApplication.class, args);
   }
}

但是**一个简单的启动类并不简单!**我们来分析一下这些注解都干了什么

注解(@SpringBootApplication)

  • 作用:标注在某个类上说明这个类是SpringBoot的主配置
  • SpringBoot就应该运行这个类的main方法来启动SpringBoot应用;
  • 进入这个注解:可以看到上面还有很多其他注解!
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
    // ......
}

@ComponentScan

  • 这个注解在Spring中很重要 ,它对应XML配置中的元素。
  • 作用:自动扫描并加载符合条件的组件或者bean , 将这个bean定义加载到IOC容器中

@SpringBootConfiguration

  • 作用:SpringBoot的配置类 ,标注在某个类上 , 表示这是一个SpringBoot的配置类;
  • 我们继续进去这个注解查看
    // 点进去得到下面的 @Component
    @Configuration
    public @interface SpringBootConfiguration {}
    
    @Component
    public @interface Configuration {}
    
  • 这里的 @Configuration,说明这是一个spring的配置类 ,配置类就是对应Spring的xml 配置文件;
  • @Component 这就说明,启动类本身也是Spring中的一个组件而已,负责启动应用!
  • 我们回到 SpringBootApplication 注解中继续看。

@EnableAutoConfiguration

  • 开启自动配置功能

      以前我们需要自己配置的东西,而现在SpringBoot可以自动帮我们配置 ;
      @EnableAutoConfiguration告诉SpringBoot开启自动配置功能,这样自动配置才能生效;
    

    点进注解接续查看:

  • @AutoConfigurationPackage :自动配置包

    @Import({Registrar.class})
    public @interface AutoConfigurationPackage {
    }
    
    • @import :Spring底层注解@import , 给容器中导入一个组件

    • Registrar.class 作用:自动配置包注册,将主启动类的所在包及包下面所有子包里面的所有组件扫描到Spring容器 ;

    • 这个分析完了,退到上一步,继续看

  • @Import({AutoConfigurationImportSelector.class}) :给容器导入组件 ;

    • AutoConfigurationImportSelector :自动配置导入选择器,那么它会导入哪些组件的选择器呢?我们点击去这个类看源码:
    @Import({AutoConfigurationImportSelector.class}) :给容器导入组件 ;
    
    AutoConfigurationImportSelector :自动配置导入选择器,那么它会导入哪些组件的选择器呢?我们点击去这个类看源码:
    
    // 获取所有的配置 List configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
    

    // 获取所有的配置 List configurations = this.getCandidateConfigurations(annotationMetadata, attributes);

    - 获得候选的配置
    ```java
    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        // 和下面的方法对应
        //这里的getSpringFactoriesLoaderFactoryClass()方法
        //返回的就是我们最开始看的启动自动导入配置文件的注解类;EnableAutoConfiguration
      List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
        
      
        Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
        return configurations;
    }
    
    //和上面的类的方法loadFactoryNames里面的第一个参数对应
    //这里的getSpringFactoriesLoaderFactoryClass()方法
    //返回的就是我们最开始看的启动自动导入配置文件的注解类;EnableAutoConfiguration
    protected Class<?> getSpringFactoriesLoaderFactoryClass() {
         return EnableAutoConfiguration.class;
    }
    
    • 这个方法getCandidateConfigurations()又调用了 SpringFactoriesLoader 类的静态方法!我们进入SpringFactoriesLoader类loadFactoryNames() 方法,获取所有的加载配置
    public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
        String factoryClassName = factoryClass.getName();
        //这里它又调用了 loadSpringFactories 方法
        return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
    }
    
    • 我们继续点击查看 loadSpringFactories 方法

      • 项目资源:META-INF/spring.factories
      • 系统资源:META-INF/spring.factories
      • 从这些资源中配置了所有的nextElement(自动配置),分装成properties
      //将所有的资源加载到配置类中(将下面的抽离出来分析,第15行)
      Properties properties = PropertiesLoaderUtils.loadProperties(resource);
      
      private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
          //获得classLoader , 我们返回可以看到这里得到的就是EnableAutoConfiguration标注的类本身
          MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
          if (result != null) {
              return result;
          } else {
              try {
                  //去获取一个资源 "META-INF/spring.factories"
                  Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
                  LinkedMultiValueMap result = new LinkedMultiValueMap();
                  //判断有没有更多的元素,将读取到的资源循环遍历,封装成为一个Properties
                  while(urls.hasMoreElements()) {
                    URL url = (URL)urls.nextElement();
                      UrlResource resource = new UrlResource(url);
                      Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                      Iterator var6 = properties.entrySet().iterator();
                      while(var6.hasNext()) {
                          Entry<?, ?> entry = (Entry)var6.next();
                          String factoryClassName = ((String)entry.getKey()).trim();
                          String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                          int var10 = var9.length;
                          for(int var11 = 0; var11 < var10; ++var11) {
                              String factoryName = var9[var11];
                              result.add(factoryClassName, factoryName.trim());
                          }
                      }
                  }
                  cache.put(classLoader, result);
                  return result;
              } catch (IOException var13) {
                  throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
              }
          }
      }
      
      • 发现一个多次出现的文件:spring.factories,全局搜索它

spring.factories

我们根据源头打开spring.factories , 看到了很多自动配置的文件;这就是自动配置根源所在!
狂神说——SpringBoot学习_第9张图片
WebMvcAutoConfiguration

我们在上面的自动配置类随便找一个打开看看,比如 :WebMvcAutoConfiguration
狂神说——SpringBoot学习_第10张图片
可以看到这些一个个的都是JavaConfig配置类,而且都注入了一些Bean,可以找一些自己认识的类,看着熟悉一下!

所以,自动配置真正实现是从classpath中搜寻所有的META-INF/spring.factories配置文件 ,并将其中对应的 org.springframework.boot.autoconfigure. 包下的配置项,通过反射实例化为对应标注了 @ConfigurationJavaConfig形式的IOC容器配置类 , 然后将这些都汇总成为一个实例并加载到IOC容器中。

工作原理总结

  1. 读取spring.properties文件
    (1)SpringBoot在启动的时候从spring-boot-autoConfigure.jar包下的的META-INF/spring.factories中获取EnableAutoConfiguration属性的值加载 自动配置类

    (2)将这些值作为自动配置类导入容器 , 自动配置类就生效 , 帮我们进行自动配置工作;

  2. 加载XXXProperties
    根据自动配置类中指定的xxxxProperties类设置自动配置的属性值,开发者可以根据该类在yml配置文件中修改自动配置

  3. 根据@ConditionalXXX注解决定加载哪些组件
    Springboot通过该注解指定组件加入IOC容器时锁需要具备的特定条件。这个组件会在满足条件时候加入到IOC容器内
    狂神说——SpringBoot学习_第11张图片

结论:

  1. SpringBoot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值
  2. 将这些值作为自动配置类导入容器 , 自动配置类就生效 , 帮我们进行自动配置工作;
  3. 以前我们需要自动配置的东西,现在springboot帮我们做了
  4. 整合JavaEE,整体解决方案和自动配置的东西都在springboot-autoconfigure的jar包中;
  5. 它会把所有需要导入的组件,以类名的方式返回,这些组件就会被添加到容器中
  6. 它会给容器中导入非常多的自动配置类 (xxxAutoConfiguration), 就是给容器中导入这个场景需要的所有组件 , 并自动配置,@Configuration(javaConfig) ;
  7. 有了自动配置类 , 免去了我们手动编写配置注入功能组件等的工作;

现在大家应该大概的了解了SpringBoot的运行原理,后面我们还会深化一次!

启动 SpringApplication.run(xxx.class, args);

1、不简单的方法

我最初以为就是运行了一个main方法,没想到却开启了一个服务;

@SpringBootApplication
public class Springboot01HellowordApplication {

    public static void main(String[] args) {
       	//该方法返回一个ConfigurableApplicationContext对象
 		//参数一:应用入口的类; 参数二:命令行参数  
        SpringApplication.run(Springboot01HellowordApplication.class, args);
    }

}

SpringApplication.run分析

分析该方法主要分两部分:一是SpringApplication的实例化, 二是run方法的执行;

2、SpringApplication

这个类主要做了以下四件事情:

  1. 推断应用的类型是普通的项目还是Web项目
  2. 查找并加载所有可用初始化器 , 设置到initializers属性中
  3. 找出所有的应用程序监听器,设置到listeners属性中
  4. 推断并设置main方法的定义类,找到运行的主类

查看构造器:

public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {
    // ......
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    this.setInitializers(this.getSpringFactoriesInstances();
    this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
    this.mainApplicationClass = this.deduceMainApplicationClass();
}

3、run方法流程分析(跟着源码和这幅图就可以一探究竟了!)

狂神说——SpringBoot学习_第12张图片

4、关于SpringBoot,谈谈关于你的理解;

  • 自动装配
  • run()
  • 全面接管SpringMVC的配置

03 yaml语法学习

配置文件

SpringBoot使用一个全局的配置文件 , 配置文件名称是固定的

  • application.properties
  • 语法结构 :key=value
  • application.yaml
  • 语法结构 :key:空格 value

配置文件的作用 :修改SpringBoot自动配置的默认值,因为SpringBoot在底层都给我们自动配置好了;
比如我们可以在配置文件中修改Tomcat 默认启动的端口号!测试一下!

server:
  port: 8081

YAML

yaml概述

  • YAML是 “YAML Ain’t a Markup Language” (YAML不是一种标记语言)的递归缩写。在开发的这种语言时,YAML 的意思其实是:“Yet Another Markup Language”(仍是一种标记语言)

  • 这种语言以数据作为中心,而不是以标记语言为重点!

  • 以前的配置文件,大多数都是使用xml来配置;比如一个简单的端口配置,我们来对比下yaml和xml

    • 传统xml配置(以标记语言为中心):
    <server>
        <port>8081<port>
    </server>
    
    • yaml配置(以数据为中心):
    server:
      prot: 8080
     #  servlet:
    	# 原先的Tomcat工程路径在这里修改
    	# context-path: /laosong    
    

yaml基础语法

说明:语法要求严格!

  1. 空格不能省略

  2. 以缩进来控制层级关系,只要是左边对齐的一列数据都是同一个层级的。

  3. 属性和值的大小写都是十分敏感的。

字面量:普通的值 [ 数字,布尔值,字符串 ]

  • 字面量直接写在后面就可以 , 字符串默认不用加上双引号或者单引号;k: v

    注意:

    • “ ” 双引号,不会转义字符串里面的特殊字符 , 特殊字符会作为本身想表示的意思;

      比如 :name: "kuang \n shen"
      输出 :kuang 换行 shen

    • '' 单引号,会转义特殊字符 , 特殊字符最终会变成和普通字符一样输出

      比如 :name: ‘kuang \n shen’
      输出 :kuang \n shen

对象、Map(键值对)

#对象、Map格式
k: 
    v1:
    v2:

在下一行来写对象的属性和值得关系,注意缩进;比如:

student:
    name: qinjiang
    age: 3

行内写法

student: {name: qinjiang,age: 3}

数组( List、set )

用 - 值表示数组中的一个元素,比如:

pets:
 - cat
 - dog
 - pig

行内写法

pets: [cat,dog,pig]

修改SpringBoot的默认端口号

配置文件中添加,端口号的参数,就可以切换端口;

server:
  port: 8082

注入配置文件

yaml文件更强大的地方在于,他可以给我们的实体类直接注入匹配值!

yaml注入配置文件

  1. 在springboot项目中的resources目录下新建一个文件 application.yaml

  2. 编写一个实体类 Dog;

    package nuc.ss.pojo;
    
    @Component  //注册bean到容器中
    public class Dog {
        private String name;
        private Integer age;
        
        //有参无参构造、get、set方法、toString()方法  
    }
    
  3. 思考,我们原来是如何给bean注入属性值的!@Value,给狗狗类测试一下:

    @Component //注册bean
    public class Dog {
        @Value("阿黄")
        private String name;
        @Value("18")
        private Integer age;
    }
    
  4. 在SpringBoot的测试类下注入狗狗输出一下;

    @SpringBootTest
    class Springboot02ConfigApplicationTests {
    
        @Autowired
        private Dog dog;
    
        @Test
        void contextLoads() {
            System.out.println(dog);
        }
    
    }
    

    结果成功输出,@Value注入成功,这是我们原来的办法对吧。
    狂神说——SpringBoot学习_第13张图片

  5. 我们在编写一个复杂一点的实体类:Person 类

    @Component //注册bean到容器中
    public class Person {
        private String name;
        private Integer age;
        private Boolean happy;
        private Date birth;
        private Map<String,Object> maps;
        private List<Object> lists;
        private Dog dog;
        
        //有参无参构造、get、set方法、toString()方法  
    }
    
  6. 我们来使用yaml配置的方式进行注入,大家写的时候注意区别和优势,我们编写一个yaml配置!

    person:
      name: qinjiang
      age: 3
      happy: false
      birth: 2000/01/01
      maps: {k1: v1,k2: v2}
      lists:
       - code
       - girl
       - music
      dog:
        name: 旺财
        age: 1
    
  7. 我们刚才已经把person这个对象的所有值都写好了,我们现在来注入到我们的类中!

    /*
    @ConfigurationProperties作用:
    将配置文件中配置的每一个属性的值,映射到这个组件中;
    告诉SpringBoot将本类中的所有属性和配置文件中相关的配置进行绑定
    参数 prefix = “person” : 将配置文件中的person下面的所有属性一一对应
    */
    @Component
    @ConfigurationProperties(prefix = "person")
    public class Person {
        private String name;
        private Integer age;
        private Boolean happy;
        private Date birth;
        private Map<String,Object> maps;
        private List<Object> lists;
        private Dog dog;
    
        //有参无参构造、get、set方法、toString()方法
    }
    
  8. IDEA 提示,springboot配置注解处理器没有找到,让我们看文档,我们可以查看文档,找到一个依赖!
    8.1、注解@ConfigurationProperties(prefix = "person")
    在这里插入图片描述点击 open Decumentation进入官网
    狂神说——SpringBoot学习_第14张图片
    8.2、在pom中导入依赖

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-configuration-processor</artifactId>
        <optional>true</optional>
    </dependency>
    
  9. 确认以上配置都OK之后,我们去测试类中测试一下:

    @SpringBootTest
    class Springboot02ConfigApplicationTests {
        @Autowired
        private Person person;
        @Test
        void contextLoads() {
            System.out.println(person);
        }
    
    }
    

结果:所有值全部注入成功!
在这里插入图片描述
yaml配置注入到实体类完全OK!

加载指定的配置文件

  • @PropertySource :加载指定的配置文件;
  • @configurationProperties:默认从全局配置文件中获取值;
  1. 我们去在resources目录下新建一个person.properties文件

    name=kuangshen
    
  2. 然后在我们的代码中指定加载person.properties文件

    @PropertySource(value = "classpath:person.properties")
    @Component //注册bean
    public class Person {
    
        @Value("${name}")
        private String name;
    
        ......  
    }
    
  3. 再次输出测试一下:指定配置文件绑定成功!
    狂神说——SpringBoot学习_第15张图片

配置文件占位符

配置文件还可以编写占位符生成随机数

person:
  name: qinjiang${random.uuid}
  age: ${random.int}
  happy: false
  birth: 2020/07/13
  maps: {k1: v1,k2: v2}
  lists:
    - code
    - music
    - girl
  dog:
    name: ${person.hell:hello}_旺财
    age: 3

回顾properties配置

  • 我们上面采用的yaml方法都是最简单的方式,开发中最常用的;
  • 也是springboot所推荐的!
  • 那我们来唠唠其他的实现方式,道理都是相同的;写还是那样写;配置文件除了yml还有我们之前常用的properties , 我们没有讲,我们来唠唠!

【注意】properties配置文件在写中文的时候,会有乱码 , 我们需要去IDEA中设置编码格式为UTF-8;settings–>FileEncodings 中配置;
狂神说——SpringBoot学习_第16张图片

对比小结

@Value这个使用起来并不友好!我们需要为每个属性单独注解赋值,比较麻烦;我们来看个功能对比图

@ConfigurationProperties @Value
功能 批量注入配置文件中的属性 一个个指定
松散绑定 支持 不支持
SpEL 不支持 支持
JSR303数据校验 支持 不支持
复杂类型封装 支持 不支持
  1. @ConfigurationProperties只需要写一次即可 , @Value则需要每个字段都添加

  2. 松散绑定:这个什么意思呢? 比如我的yml中写的last-name,这个和lastName是一样的, - 后面跟着的字母默认是大写的。这就是松散绑定。可以测试一下

  3. JSR303数据校验 , 这个就是我们可以在字段是增加一层过滤器验证 , 可以保证数据的合法性

  4. 复杂类型封装,yml中可以封装对象 , 使用value就不支持

结论

配置yml和配置properties都可以获取到值 , 强烈推荐 yml;
如果我们在某个业务中,只需要获取配置文件中的某个值,可以使用一下 @value;
如果说,我们专门编写了一个JavaBean来和配置文件进行一一映射,就直接@configurationProperties,不要犹豫!

04 JSR303数据校验

如何使用

Springboot中可以用@validated来校验数据,如果数据异常则会统一抛出异常,方便异常中心统一处理。我们这里来写个注解让我们的name只能支持Email格式;

1、添加validation启动器

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

2、@Email添加

@Component //注册bean
@ConfigurationProperties(prefix = "person")
@Validated  //数据校验
public class Person {
    @Email(message="邮箱格式错误") //name必须是邮箱格式
    private String name;
}

运行结果 :default message [不是一个合法的电子邮件地址];

在这里插入图片描述
使用数据校验,可以保证数据的正确性;

常见参数:

@NotNull(message="名字不能为空")
private String userName;
@Max(value=120,message="年龄最大不能查过120")
private int age;
@Email(message="邮箱格式错误")
private String email;

空检查
@Null       验证对象是否为null
@NotNull    验证对象是否不为null, 无法查检长度为0的字符串
@NotBlank   检查约束字符串是不是Null还有被Trim的长度是否大于0,只对字符串,且会去掉前后空格.
@NotEmpty   检查约束元素是否为NULL或者是EMPTY.
    
Booelan检查
@AssertTrue     验证 Boolean 对象是否为 true  
@AssertFalse    验证 Boolean 对象是否为 false  
    
长度检查
@Size(min=, max=) 验证对象(Array,Collection,Map,String)长度是否在给定的范围之内  
@Length(min=, max=) string is between min and max included.

日期检查
@Past       验证 Date 和 Calendar 对象是否在当前时间之前  
@Future     验证 Date 和 Calendar 对象是否在当前时间之后  
@Pattern    验证 String 对象是否符合正则表达式的规则

.......等等
除此以外,我们还可以自定义一些数据校验规则

狂神说——SpringBoot学习_第17张图片
狂神说——SpringBoot学习_第18张图片
狂神说——SpringBoot学习_第19张图片

多环境切换

profile是Spring对不同环境提供不同配置功能的支持,可以通过激活不同的环境版本,实现快速切换环境;

多配置文件

我们在主配置文件编写的时候,文件名可以是 application-{profile}.properties/yml , 用来指定多个环境版本;
例如:

application-test.properties 代表测试环境配置
application-dev.properties 代表开发环境配置

但是Springboot并不会直接启动这些配置文件,它默认使用application.properties主配置文件;
狂神说——SpringBoot学习_第20张图片
我们需要通过一个配置来选择需要激活的环境:

#比如在配置文件中指定使用dev环境,我们可以通过设置不同的端口号进行测试;
#我们启动SpringBoot,就可以看到已经切换到dev下的配置了;
spring.profiles.active=dev

yaml的多文档块

和properties配置文件中一样,但是使用yml去实现不需要创建多个配置文件,更加方便了 !

server:
  port: 8081
#选择要激活那个环境块
spring:
  profiles:
    active: test

---
server:
  port: 8083
spring:
  profiles: dev #配置环境的名称


---

server:
  port: 8084
spring:
  profiles: test  #配置环境的名称

注意:如果yml和properties同时都配置了端口,并且没有激活其他环境 , 默认会使用properties配置文件的!

配置文件加载位置

外部加载配置文件的方式十分多,我们选择最常用的即可,在开发的资源文件中进行配置!

官方外部配置文件说明参考文档https://docs.spring.io/spring-boot/docs/2.3.1.RELEASE/reference/html/spring-boot-features.html#boot-features-logging-color-coded-output
狂神说——SpringBoot学习_第21张图片
springboot 启动会扫描以下位置的application.properties或者application.yml文件作为Spring boot的默认配置文件:
狂神说——SpringBoot学习_第22张图片

优先级1:项目路径下的config文件夹配置文件
优先级2:项目路径下配置文件
优先级3:资源路径下的config文件夹配置文件
优先级4:资源路径下配置文件

优先级由高到底,高优先级的配置会覆盖低优先级的配置;

SpringBoot会从这四个位置全部加载主配置文件;互补配置;

我们在最低级的配置文件中设置一个项目访问路径的配置来测试互补问题;

#配置项目的访问路径
server.servlet.context-path=/ss

拓展运维小技巧指定位置加载配置文件

我们还可以通过spring.config.location来改变默认的配置文件位置

项目打包好以后,我们可以使用命令行参数的形式,启动项目的时候来指定配置文件的新位置;

这种情况,一般是后期运维做的多,相同配置,外部指定的配置文件优先级最高

java -jar spring-boot-config.jar --spring.config.location=F:/application.properties

05 自动配置原理

配置文件到底能写什么?怎么写?----联系---- spring.factories

SpringBoot官方文档中有大量的配置,我们无法全部记住,官网:https://docs.spring.io/spring-boot/docs/2.3.1.RELEASE/reference/html/appendix-application-properties.html#core-properties

Core properties
狂神说——SpringBoot学习_第23张图片

分析自动配置原理

  1. SpringBoot启动的时候加载主配置类,开启了自动配置功能 @EnableAutoConfiguration

  2. @EnableAutoConfiguration 作用

    2.1、 利用EnableAutoConfigurationImportSelector给容器中导入一些组件

    2.2、可以查看selectImports()方法的内容,他返回了一个autoConfigurationEnty,来自this.getAutoConfigurationEntry(autoConfigurationMetadata,annotationMetadata);这个方法我们继续来跟踪:

    2.3、这个方法有一个值:List configurations = getCandidateConfigurations(annotationMetadata, attributes);叫做获取候选的配置 ,我们点击继续跟踪

     - SpringFactoriesLoader.loadFactoryNames()
     - 扫描所有jar包类路径下META-INF/spring.factories
     -  把扫描到的这些文件的内容包装成properties对象
     - 从properties中获取到EnableAutoConfiguration.class类(类名)对应的值,然后把他们添加在容器中
    

    2.4、在类路径下,META-INF/spring.factories里面配置的所有EnableAutoConfiguration的值加入到容器中:

    # Initializers
    org.springframework.context.ApplicationContextInitializer=\
    org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
    org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
    
    # Application Listeners
    org.springframework.context.ApplicationListener=\
    org.springframework.boot.autoconfigure.BackgroundPreinitializer
    
    # Auto Configuration Import Listeners
    org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
    org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener
    
    # Auto Configuration Import Filters
    org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
    org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
    org.springframework.boot.autoconfigure.condition.OnClassCondition,\
    org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition
    
    # Auto Configure
    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
    org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
    org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
    org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
    org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
    org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
    org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
    org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
    org.springframework.boot.autoconfigure.context.LifecycleAutoConfiguration,\
    org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
    org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
    org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\
    org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveRepositoriesAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveDataAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveRepositoriesAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.couchbase.CouchbaseRepositoriesAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.elasticsearch.ReactiveElasticsearchRepositoriesAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.elasticsearch.ReactiveElasticsearchRestClientAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.jdbc.JdbcRepositoriesAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.ldap.LdapRepositoriesAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.mongo.MongoReactiveDataAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.mongo.MongoReactiveRepositoriesAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.neo4j.Neo4jRepositoriesAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.r2dbc.R2dbcDataAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.r2dbc.R2dbcRepositoriesAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.r2dbc.R2dbcTransactionManagerAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration,\
    org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchRestClientAutoConfiguration,\
    org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\
    org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,\
    org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration,\
    org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\
    org.springframework.boot.autoconfigure.h2.H2ConsoleAutoConfiguration,\
    org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,\
    org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration,\
    org.springframework.boot.autoconfigure.hazelcast.HazelcastJpaDependencyAutoConfiguration,\
    org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration,\
    org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration,\
    org.springframework.boot.autoconfigure.influx.InfluxDbAutoConfiguration,\
    org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration,\
    org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration,\
    org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\
    org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
    org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,\
    org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration,\
    org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration,\
    org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\
    org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration,\
    org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration,\
    org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration,\
    org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration,\
    org.springframework.boot.autoconfigure.jms.artemis.ArtemisAutoConfiguration,\
    org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration,\
    org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration,\
    org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration,\
    org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration,\
    org.springframework.boot.autoconfigure.availability.ApplicationAvailabilityAutoConfiguration,\
    org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapAutoConfiguration,\
    org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration,\
    org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\
    org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration,\
    org.springframework.boot.autoconfigure.mail.MailSenderValidatorAutoConfiguration,\
    org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration,\
    org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\
    org.springframework.boot.autoconfigure.mongo.MongoReactiveAutoConfiguration,\
    org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,\
    org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
    org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration,\
    org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration,\
    org.springframework.boot.autoconfigure.rsocket.RSocketMessagingAutoConfiguration,\
    org.springframework.boot.autoconfigure.rsocket.RSocketRequesterAutoConfiguration,\
    org.springframework.boot.autoconfigure.rsocket.RSocketServerAutoConfiguration,\
    org.springframework.boot.autoconfigure.rsocket.RSocketStrategiesAutoConfiguration,\
    org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,\
    org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration,\
    org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration,\
    org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration,\
    org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDetailsServiceAutoConfiguration,\
    org.springframework.boot.autoconfigure.security.rsocket.RSocketSecurityAutoConfiguration,\
    org.springframework.boot.autoconfigure.security.saml2.Saml2RelyingPartyAutoConfiguration,\
    org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration,\
    org.springframework.boot.autoconfigure.session.SessionAutoConfiguration,\
    org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration,\
    org.springframework.boot.autoconfigure.security.oauth2.client.reactive.ReactiveOAuth2ClientAutoConfiguration,\
    org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerAutoConfiguration,\
    org.springframework.boot.autoconfigure.security.oauth2.resource.reactive.ReactiveOAuth2ResourceServerAutoConfiguration,\
    org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration,\
    org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration,\
    org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration,\
    org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\
    org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,\
    org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration,\
    org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.reactive.error.ErrorWebFluxAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.reactive.function.client.ClientHttpConnectorAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
    org.springframework.boot.autoconfigure.websocket.reactive.WebSocketReactiveAutoConfiguration,\
    org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration,\
    org.springframework.boot.autoconfigure.websocket.servlet.WebSocketMessagingAutoConfiguration,\
    org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration,\
    org.springframework.boot.autoconfigure.webservices.client.WebServiceTemplateAutoConfiguration
    
    # Failure analyzers
    org.springframework.boot.diagnostics.FailureAnalyzer=\
    org.springframework.boot.autoconfigure.diagnostics.analyzer.NoSuchBeanDefinitionFailureAnalyzer,\
    org.springframework.boot.autoconfigure.flyway.FlywayMigrationScriptMissingFailureAnalyzer,\
    org.springframework.boot.autoconfigure.jdbc.DataSourceBeanCreationFailureAnalyzer,\
    org.springframework.boot.autoconfigure.jdbc.HikariDriverConfigurationFailureAnalyzer,\
    org.springframework.boot.autoconfigure.r2dbc.ConnectionFactoryBeanCreationFailureAnalyzer,\
    org.springframework.boot.autoconfigure.session.NonUniqueSessionRepositoryFailureAnalyzer
    
    # Template availability providers
    org.springframework.boot.autoconfigure.template.TemplateAvailabilityProvider=\
    org.springframework.boot.autoconfigure.freemarker.FreeMarkerTemplateAvailabilityProvider,\
    org.springframework.boot.autoconfigure.mustache.MustacheTemplateAvailabilityProvider,\
    org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAvailabilityProvider,\
    org.springframework.boot.autoconfigure.thymeleaf.ThymeleafTemplateAvailabilityProvider,\
    org.springframework.boot.autoconfigure.web.servlet.JspTemplateAvailabilityProvider
    

    每一个这样的 xxxAutoConfiguration类都是容器中的一个组件,都加入到容器中;用他们来做自动配置;

  3. 每一个自动配置类进行自动配置功能;

  4. 我们以**HttpEncodingAutoConfiguration(Http编码自动配置)**为例解释自动配置原理;

    //表示这是一个配置类,和以前编写的配置文件一样,也可以给容器中添加组件;
    @Configuration 
    
    //启动指定类的ConfigurationProperties功能;
      //进入这个HttpProperties查看,将配置文件中对应的值和HttpProperties绑定起来;
      //并把HttpProperties加入到ioc容器中
    @EnableConfigurationProperties({HttpProperties.class}) 
    
    //Spring底层@Conditional注解
      //根据不同的条件判断,如果满足指定的条件,整个配置类里面的配置就会生效;
      //这里的意思就是判断当前应用是否是web应用,如果是,当前配置类生效
    @ConditionalOnWebApplication(
        type = Type.SERVLET
    )
    
    //判断当前项目有没有这个类CharacterEncodingFilter;SpringMVC中进行乱码解决的过滤器;
    @ConditionalOnClass({CharacterEncodingFilter.class})
    
    //判断配置文件中是否存在某个配置:spring.http.encoding.enabled;
      //如果不存在,判断也是成立的
      //即使我们配置文件中不配置pring.http.encoding.enabled=true,也是默认生效的;
    @ConditionalOnProperty(
        prefix = "spring.http.encoding",
        value = {"enabled"},
        matchIfMissing = true
    )
    
    public class HttpEncodingAutoConfiguration {
        //他已经和SpringBoot的配置文件映射了
        private final Encoding properties;
        //只有一个有参构造器的情况下,参数的值就会从容器中拿
        public HttpEncodingAutoConfiguration(HttpProperties properties) {
            this.properties = properties.getEncoding();
        }
    
        //给容器中添加一个组件,这个组件的某些值需要从properties中获取
        @Bean
        @ConditionalOnMissingBean //判断容器没有这个组件?
        public CharacterEncodingFilter characterEncodingFilter() {
            CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
            filter.setEncoding(this.properties.getCharset().name());
            filter.setForceRequestEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpProperties.Encoding.Type.REQUEST));
            filter.setForceResponseEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpProperties.Encoding.Type.RESPONSE));
            return filter;
        }
        //。。。。。。。
    }
    

一句话总结 :根据当前不同的条件判断,决定这个配置类是否生效!

一但这个配置类生效;这个配置类就会给容器中添加各种组件;

这些组件的属性是从对应的properties类中获取的,这些类里面的每一个属性又是和配置文件绑定的;

所有在配置文件中能配置的属性都是在xxxxProperties类中封装着;

配置文件能配置什么就可以参照某个功能对应的这个属性类
//从配置文件中获取指定的值和bean的属性进行绑定
@ConfigurationProperties(prefix = "spring.http") 
public class HttpProperties {
    // .....
}

狂神说——SpringBoot学习_第24张图片

自动配置——精髓

  1. SpringBoot启动会加载大量的自动配置类
  2. 我们看我们需要的功能有没有在SpringBoot默认写好的自动配置类当中;
  3. 我们再来看这个自动配置类中到底配置了哪些组件;(只要我们要用的组件存在在其中,我们就不需要再手动配置了)
  4. 给容器中自动配置类添加组件的时候,会从properties类中获取某些属性。我们只需要在配置文件中指定这些属性的值即可;
    xxxxAutoConfigurartion:自动配置类;给容器中添加组件
    xxxxProperties:封装配置文件中相关属性;

@Conditional

了解完自动装配的原理后,我们来关注一个细节问题,自动配置类必须在一定的条件下才能生效;

@Conditional派生注解(Spring注解版原生的@Conditional作用)

作用:必须是@Conditional指定的条件成立,才给容器中添加组件,配置配里面的所有内容才生效;
狂神说——SpringBoot学习_第25张图片
那么多的自动配置类,必须在一定的条件下才能生效;也就是说,我们加载了这么多的配置类,但不是所有的都生效了。

自动配置类是否生效

我们可以在application.properties通过启用 debug=true属性;

在控制台打印自动配置报告,这样我们就可以很方便的知道哪些自动配置类生效;

#开启springboot的调试类
debug=true 
  • Positive matches:(自动配置类启用的:正匹配)

  • Negative matches:(没有启动,没有匹配成功的自动配置类:负匹配)

  • Unconditional classes: (没有条件的类)

  • 【演示:查看输出的日志】

06 Web 开发

要解决的问题:

  • 导入静态资源
  • 指定首页
  • jsp。模版引擎Thymeleaf
  • 装配扩展SpringMVC
  • 增删改查
  • 截器和国际化

静态资源源码

idea按两下shift,搜索webautoConfiguration - WebMvcAutoConfigurationAdapter - addResourceHandlers

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    if (!this.resourceProperties.isAddMappings()) {
        logger.debug("Default resource handling disabled");
        return;
    }
    Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
    CacheControl cacheControl = this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl();
    // 第一种方式 webjars
    if (!registry.hasMappingForPattern("/webjars/**")) {
        customizeResourceHandlerRegistration(registry.addResourceHandler("/webjars/**")
                                             .addResourceLocations("classpath:/META-INF/resources/webjars/")
                                             .setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
    }
    // 第二种方式
    String staticPathPattern = this.mvcProperties.getStaticPathPattern();
    if (!registry.hasMappingForPattern(staticPathPattern)) {
        customizeResourceHandlerRegistration(registry.addResourceHandler(staticPathPattern)
                                             .addResourceLocations(getResourceLocations(this.resourceProperties.getStaticLocations()))
                                             .setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
    }
}

三个静态资源的位置

  • 第一个if:没有

  • 第二个if:webjars不推荐使用,官网导包,url:META-INF/resources/webjars/

  • 第三个if:优先级:resources > static(默认) > public

    • classPath :/**
    • classpath:/META-INF/resources/
    • classpath:/resources/
    • classpath:/static/(默认创建)
    • classpath:/public/
private String staticPathPattern = "/**";
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = { "classpath:/META-INF/resources/","classpath:/resources/","classpath:/static/","classpath:/public/" };
//以上4个人位置加装静态资源

首页

WebMvcAutoConfiguration 下的 ,观察源码知:在静态资源目录下创建index.html即可

private Resource getIndexHtml(String location) {
   return this.resourceLoader.getResource(location + "index.html");
}

Thymeleaf

  • 注意:template/**下的任何页面都需要Controller来跳转才能访问,不能直接访问
  1. 导入依赖
<!--thymeleaf模版引擎在,都是基于3.x版本开发-->
<dependency>
    <groupId>org.thymeleaf</groupId>
    <artifactId>thymeleaf-spring5</artifactId>
</dependency>
<dependency>
    <groupId>org.thymeleaf.extras</groupId>
    <artifactId>thymeleaf-extras-java8time</artifactId>
</dependency>
  1. 查看源码,在template下使用
@ConfigurationProperties(prefix = "spring.thymeleaf")
public class ThymeleafProperties {

	private static final Charset DEFAULT_ENCODING = StandardCharsets.UTF_8;
	// 前缀
	public static final String DEFAULT_PREFIX = "classpath:/templates/";
	// 后缀
	public static final String DEFAULT_SUFFIX = ".html";
	...
}
  1. 第一次使用,传msg,接受:th:text="${msg}",(thymeleaf独有的语法)
@Controller
public class ThymeleafController {

    @RequestMapping("/thymeleaf")
    public String thy(Model model){
        model.addAttribute("msg","

hello Thymeleaf

"
); model.addAttribute("users", Arrays.asList("张三","李四","王五")); // 后缀默认是 .html return "thymeleafTest"; } }
<!DOCTYPE html>
<!--thymeleaf约束-->
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>thymeleaf</title>
</head>
<body>
<!--所有的html元素都可以被Th接管:th:xx元素名-->
<!--test=默认是转义文本-->
<h1 th:text="${msg}"></h1>
<!--utest=默认是不转义文本-->
<h1 th:utext="${msg}"></h1>
<h1>
    <!--遍历往前写item-->
    遍历一 推荐这么使用:
    <h2 th:each="user:${users}" th:text="${user}"></h2><br>
    遍历二:
    <h2 th:each="user:${users}" >[[${user}]]</h2>
</h1>
</body>
</html>
  1. SpringMVC扩展
    implements WebMvcConfigurer,重写一些方法
/**
 *  扩展SpringMVC:实现WebMvcConfigurer接口,重写某些方法让我们实现一些新的配置
 *      注意:不能再使用@EnableWebMvc,这个方法会让扩展方法失效
 */
@Configuration
public class MyMVCConfig implements WebMvcConfigurer {
    /**
     * 视图跳转 访问/ssl,跳转到config.html
     */
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController(
                "/ssl").setViewName("config");
    }
}

Thymeleaf

模板引擎的使用:第一步:引入Thymeleaf

  1. Thymeleaf官网:https://www.thymeleaf.org/
  2. Thymeleaf在Github的主页:https://github.com/thymeleaf/thymeleaf
  3. spring官方文档【springboot参考指南】:
    https://docs.spring.io/spring-boot/docs/2.1.6.RELEASE/reference/htmlsingle/#using-boot-starter

结论:要使用thymeleaf,只需要导入对应的依赖就可以了!我们将HTML放在我们的templates目录下即可!

public static final String DEFAULT_PREFIX="classpath:/templates/";

public static final String DEFAULT_SUFFIX=".html";

Thymeleaf的语法(手册网址:https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.pdf)

狂神说——SpringBoot学习_第26张图片

Springboot_web 简单案例:

  1. 首页配置:
    1.1. 注意点,所有页面的静态资源都需要使用thymeleaf接管
    <!--thymeleaf约束 -->
    <html lang="en" xmlns:th="http://www.thymeleaf.org"> 
    
    1.2. url:@{}前端url表达式语法: @{…} ,/表示默认默认从static下寻找
  2. 页面国际化
    2.1.我们需要配置i18n文件
    2.2.我们如果需要在项目中进行按钮自动切换,我们需要自定义一个组件LocaleResolver【自定义一个MyLocalResolver继承LocalResolver】
    2.3记得将自己写的组件配置到spring容器 @Bean注入
    2.4.#{}【页面使用th:text="#{login.btn}"等接收配置文件里的参数】
  3. 登录+拦截
  4. 员工列表展示
    4.1.提取公共页面
    th:fragment="sidebar"
    th:replace="~{commons/commons::topbar}"
    
    如果啊要传递参数,可以直接使用()传参,接收判断即可
    4.2.列表循环展示
  5. 添加员工
    5.1.按钮提交
    5.2.跳转掉添加页面
    5.3.添加员工成功
    5.4.返回首页
  6. CRUD搞定(增删查改)

如何写一个网站

  1. 前端
    模版:自己网站搜
    框架:组件,需要自己手动拼接:BootStrap,Layui,semantic-ui

  2. 设计数据库(真正的难点)

  3. 前端让他能够自动运行,独立化工程

  4. 数据接口如何对接:json,对象

  5. 前后端联调
    前端:自己能够通过“’框架”网站组合出一个网页
    后端:必须要有自己熟悉的一个后台模版,99%公司会让你自己写:推荐X-admin网站模版

SpringBoot 整合

狂神说——SpringBoot学习_第27张图片
狂神说——SpringBoot学习_第28张图片

07 整合JDBC

SpringData简介

  • 对于数据访问层,无论是 SQL(关系型数据库) 还是 NOSQL(非关系型数据库),Spring Boot 底层都是采用 Spring Data 的方式进行统一处理。

  • Spring Boot 底层都是采用 Spring Data 的方式进行统一处理各种数据库,Spring Data 也是 Spring 中与 Spring Boot、Spring Cloud 等齐名的知名项目。

  • Sping Data 官网:https://spring.io/projects/spring-data

  • 数据库相关的启动器 :可以参考官方文档:https://docs.spring.io/spring-boot/docs/2.2.5.RELEASE/reference/htmlsingle/#using-boot-starter

整合JDBC

创建测试项目测试数据源

  1. 我去新建一个项目测试:springboot-data-jdbc ; 引入相应的模块!基础模块(依赖)
    狂神说——SpringBoot学习_第29张图片
  2. 项目建好之后,发现自动帮我们导入了如下的启动器:
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>
  1. 编写yaml配置文件连接数据库;
spring:
  datasource:
    username: root
    password: admin
    #?serverTimezone=UTC解决时区的报错
    url: jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
    driver-class-name: com.mysql.cj.jdbc.Driver
  1. 配置完这一些东西后,我们就可以直接去使用了,因为SpringBoot已经默认帮我们进行了自动配置;去测试类测试一下
@SpringBootTest
class SpringbootDataJdbcApplicationTests {

    //DI注入数据源
    @Autowired
    DataSource dataSource;

    @Test
    public void contextLoads() throws SQLException {
        //看一下默认数据源
        System.out.println(dataSource.getClass());
        //获得连接
        Connection connection = dataSource.getConnection();
        System.out.println(connection);
        //关闭连接
        connection.close();
    }
}

结果:我们可以看到他默认给我们配置的数据源为 : class com.zaxxer.hikari.HikariDataSource , 我们并没有手动配置

我们来全局搜索一下,找到数据源的所有自动配置都在 :DataSourceAutoConfiguration文件:

@Import(
    {Hikari.class, Tomcat.class, Dbcp2.class, Generic.class, DataSourceJmxConfiguration.class}
)
protected static class PooledDataSourceConfiguration {
    protected PooledDataSourceConfiguration() {
    }
}

这里导入的类都在 DataSourceConfiguration 配置类下,可以看出 Spring Boot 2.2.5 默认使用HikariDataSource 数据源,而以前版本,如 Spring Boot 1.5 默认使用 org.apache.tomcat.jdbc.pool.DataSource 作为数据源;

HikariDataSource 号称 Java WEB 当前速度最快的数据源,相比于传统的 C3P0 、DBCP、Tomcat jdbc 等连接池更加优秀;

可以使用 spring.datasource.type 指定自定义的数据源类型,值为 要使用的连接池实现的完全限定名

关于数据源我们并不做介绍,有了数据库连接,显然就可以 CRUD 操作数据库了。但是我们需要先了解一个对象 JdbcTemplate

JDBCTemplate

  1. 有了数据源(com.zaxxer.hikari.HikariDataSource),然后可以拿到数据库连接(java.sql.Connection),有了连接,就可以使用原生的 JDBC 语句来操作数据库;

  2. 即使不使用第三方第数据库操作框架,如 MyBatis等,Spring 本身也对原生的JDBC 做了轻量级的封装,即JdbcTemplate。

  3. 数据库操作的所有 CRUD 方法都在 JdbcTemplate 中。

  4. Spring Boot 不仅提供了默认的数据源,同时默认已经配置好了 JdbcTemplate 放在了容器中,程序员只需自己注入即可使用

  5. JdbcTemplate 的自动配置是依赖 org.springframework.boot.autoconfigure.jdbc 包下的 JdbcTemplateConfiguration 类

JdbcTemplate主要提供以下几类方法:

  • execute方法:可以用于执行任何SQL语句,一般用于执行DDL语句;
  • update方法及batchUpdate方法:update方法用于执行新增、修改、删除等语句;batchUpdate方法用于执行批处理相关语句;
  • query方法及queryForXXX方法:用于执行查询相关语句;
  • call方法:用于执行存储过程、函数相关语句。

测试

编写一个Controller,注入 jdbcTemplate,编写测试方法进行访问测试;

package nuc.ss.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;
import java.util.Map;

@RestController
public class JDBCController {

    @Autowired
    JdbcTemplate jdbcTemplate;

    // 查询数据库的所有信息
    // 没有实体类,获取数据库的东西,怎么获取? Map
    @GetMapping("/userList")
    public List<Map<String,Object>> userList() {
        String sql = "select * from user";
        List<Map<String, Object>> maps = jdbcTemplate.queryForList(sql);
        return maps;
    }

    @GetMapping("/addUser")
    public String addUser() {
        String sql = "insert into mybatis.user(id, name, pwd) values(7,'小明','123456')";
        jdbcTemplate.update(sql);
        return "update-ok";
    }

    @GetMapping("/updateUser/{id}")
    public String updateUser(@PathVariable("id") int id) {
        String sql = "update mybatis.user set name  = ?,pwd = ? where id = " + id;
        //封装
        Object[] objects = new Object[2];

        objects[0] = "小明2";
        objects[1] = "aaaaaaa";

        jdbcTemplate.update(sql,objects);
        return "update-ok";
    }

    @GetMapping("/deleteUser/{id}")
    public String deleteUser(@PathVariable("id") int id) {
        String sql = "delete from mybatis.user where id = ?";
        jdbcTemplate.update(sql,id);
        return "update-ok";
    }
}

测试请求,结果正常;
到此,CURD的基本操作,使用 JDBC 就搞定了。

08 整合Druid

集成Druid

Druid简介

SpringBoot2.x配置Druid数据源及监控

  • Java程序很大一部分要操作数据库,为了提高性能操作数据库的时候,又不得不使用数据库连接池。

  • Druid 是阿里巴巴开源平台上一个数据库连接池实现,结合了 C3P0、DBCP 等 DB 池的优点,同时加入了日志监控。

  • Druid 可以很好的监控 DB 池连接和 SQL 的执行情况,天生就是针对监控而生的 DB 连接池。

  • Druid已经在阿里巴巴部署了超过600个应用,经过一年多生产环境大规模部署的严苛考验。

  • Spring Boot 2.0 以上默认使用 Hikari 数据源,可以说 Hikari 与 Driud 都是当前 Java Web 上最优秀的数据源,我们来重点介绍 Spring Boot 如何集成 Druid 数据源,如何实现数据库监控。

  • Github地址:https://github.com/alibaba/druid/

com.alibaba.druid.pool.DruidDataSource 基本配置参数如下:

配置 缺省值 说明
name 配置这个属性的意义在于,如果存在多个数据源,监控的时候可以通过名字来区分开来。 如果没有配置,将会生成一个名字,格式是:“DataSource-” + System.identityHashCode(this)
jdbcUrl 连接数据库的url,不同数据库不一样。例如: mysql : jdbc:mysql://10.20.153.104:3306/druid2 oracle : jdbc:oracle:thin:@10.20.149.85:1521:ocnauto
username 连接数据库的用户名
password 连接数据库的密码。如果你不希望密码直接写在配置文件中,可以使用ConfigFilter。详细看这里:https://github.com/alibaba/druid/wiki/%E4%BD%BF%E7%94%A8ConfigFilter
driverClassName 根据url自动识别 这一项可配可不配,如果不配置druid会根据url自动识别dbType,然后选择相应的driverClassName(建议配置下)
initialSize 0 初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时
maxActive 8 最大连接池数量
maxIdle 8 已经不再使用,配置了也没效果
minIdle 最小连接池数量
maxWait 获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁。
poolPreparedStatements false 是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭。
maxOpenPreparedStatements -1 要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100
validationQuery 用来检测连接是否有效的sql,要求是一个查询语句。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会其作用。
validationQueryTimeout 单位:秒,检测连接是否有效的超时时间。底层调用jdbc
Statement对象的void setQueryTimeout(int seconds)方法
testOnBorrow true 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能
testOnReturn false 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能
testWhileIdle false 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效
timeBetweenEvictionRunsMillis 1分钟( 1.0.14 ) 有两个含义: 1) Destroy线程会检测连接的间隔时间 2) testWhileIdle的判断依据,详细看testWhileIdle属性的说明
numTestsPerEvictionRun 不再使用,一个DruidDataSource只支持一个EvictionRun
minEvictableIdleTimeMillis 30分钟( 1.0.14 ) 连接保持空闲而不被驱逐的最长时间
connectionInitSqls 物理连接初始化的时候执行的sql
exceptionSorter 根据dbType自动识别 当数据库抛出一些不可恢复的异常时,抛弃连接
filters 属性类型是字符串,通过别名的方式配置扩展插件,常用的插件有: 监控统计用的filter:stat日志用的filter:log4j防御sql注入的filter:wall
proxyFilters 类型是List,如果同时配置了filters和proxyFilters,是组合关系,并非替换关系

狂神说——SpringBoot学习_第30张图片
狂神说——SpringBoot学习_第31张图片
Druid数据源专有配置:
狂神说——SpringBoot学习_第32张图片

配置数据源

  1. 添加上 Druid 数据源依赖,这个依赖可以从Maven仓库官网Maven Respository中获取
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.23</version>
</dependency>
  1. 切换数据源;之前已经说过 Spring Boot 2.0 以上默认使用 com.zaxxer.hikari.HikariDataSource 数据源,但可以通过 spring.datasource.type 指定数据源。
spring:
  datasource:
    username: root
    password: 123456
    url: jdbc:mysql://localhost:3306/springboot?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource # 自定义数据源
  1. 数据源切换之后,在测试类中注入 DataSource,然后获取到它,输出一看便知是否成功切换;
    在这里插入图片描述

  2. 切换成功!既然切换成功,就可以设置数据源连接初始化大小、最大连接数、等待时间、最小连接数 等设置项;可以查看源码

spring:
  datasource:
    username: root
    password: 123456
    #?serverTimezone=UTC解决时区的报错
    url: jdbc:mysql://localhost:3306/springboot?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource

    #Spring Boot 默认是不注入这些属性值的,需要自己绑定
    #druid 数据源专有配置
    initialSize: 5
    minIdle: 5
    maxActive: 20
    maxWait: 60000
    timeBetweenEvictionRunsMillis: 60000
    minEvictableIdleTimeMillis: 300000
    validationQuery: SELECT 1 FROM DUAL
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false
    poolPreparedStatements: true

    #配置监控统计拦截的filters,stat:监控统计、log4j:日志记录、wall:防御sql注入
    #如果允许时报错  java.lang.ClassNotFoundException: org.apache.log4j.Priority
    #则导入 log4j 依赖即可,Maven 地址:https://mvnrepository.com/artifact/log4j/log4j
    filters: stat,wall,log4j
    maxPoolPreparedStatementPerConnectionSize: 20
    useGlobalDataSourceStat: true
    connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
  1. 导入Log4j 的依赖
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>
  1. 现在需要程序员自己为 DruidDataSource 绑定全局配置文件中的参数,再添加到容器中,而不再使用 Spring Boot 的自动生成了;我们需要 自己添加 DruidDataSource 组件到容器中,并绑定属性;
package nuc.ss.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;

@Configuration
public class DruidConfig {

    /*
       将自定义的 Druid数据源添加到容器中,不再让 Spring Boot 自动创建
       绑定全局配置文件中的 druid 数据源属性到 com.alibaba.druid.pool.DruidDataSource从而让它们生效
       @ConfigurationProperties(prefix = "spring.datasource"):作用就是将 全局配置文件中
       前缀为 spring.datasource的属性值注入到 com.alibaba.druid.pool.DruidDataSource 的同名参数中
     */
    @ConfigurationProperties(prefix = "spring.datasource")
    @Bean
    public DataSource druidDataSource() {
        return new DruidDataSource();
    }

}
  1. 去测试类中测试一下;看是否成功!
@SpringBootTest
class SpringbootDataJdbcApplicationTests {

    //DI注入数据源
    @Autowired
    DataSource dataSource;

    @Test
    public void contextLoads() throws SQLException {
        //看一下默认数据源
        System.out.println(dataSource.getClass());
        //获得连接
        Connection connection =   dataSource.getConnection();
        System.out.println(connection);

        DruidDataSource druidDataSource = (DruidDataSource) dataSource;
        System.out.println("druidDataSource 数据源最大连接数:" + druidDataSource.getMaxActive());
        System.out.println("druidDataSource 数据源初始化连接数:" + druidDataSource.getInitialSize());

        //关闭连接
        connection.close();
    }
}
  1. 输出结果 :可见配置参数已经生效!
    狂神说——SpringBoot学习_第33张图片

配置Druid数据源监控

Druid 数据源具有监控的功能,并提供了一个 web 界面方便用户查看,类似安装 路由器 时,人家也提供了一个默认的 web 页面。

所以第一步需要设置 Druid 的后台管理页面,比如 登录账号、密码 等;配置后台管理;

//配置 Druid 监控管理后台的Servlet;
//内置 Servlet 容器时没有web.xml文件,所以使用 Spring Boot 的注册 Servlet 方式
@Bean
public ServletRegistrationBean statViewServlet() {
    ServletRegistrationBean bean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");

    // 这些参数可以在 com.alibaba.druid.support.http.StatViewServlet 
    // 的父类 com.alibaba.druid.support.http.ResourceServlet 中找到
    Map<String, String> initParams = new HashMap<>();
    initParams.put("loginUsername", "root"); //后台管理界面的登录账号
    initParams.put("loginPassword", "admin"); //后台管理界面的登录密码

    //后台允许谁可以访问
    //initParams.put("allow", "localhost"):表示只有本机可以访问
    //initParams.put("allow", ""):为空或者为null时,表示允许所有访问
    initParams.put("allow", "");
    //deny:Druid 后台拒绝谁访问
    //initParams.put("kuangshen", "192.168.1.20");表示禁止此ip访问

    //设置初始化参数
    bean.setInitParameters(initParams);
    return bean;
}

配置完毕后,我们可以选择访问 :http://localhost:8080/druid/login.html
狂神说——SpringBoot学习_第34张图片
进入之后
狂神说——SpringBoot学习_第35张图片
配置 Druid web 监控 filter 过滤器

//配置 Druid 监控 之  web 监控的 filter
//WebStatFilter:用于配置Web和Druid数据源之间的管理关联监控统计
@Bean
public FilterRegistrationBean webStatFilter() {
    FilterRegistrationBean bean = new FilterRegistrationBean();
    bean.setFilter(new WebStatFilter());

    //exclusions:设置哪些请求进行过滤排除掉,从而不进行统计
    Map<String, String> initParams = new HashMap<>();
    initParams.put("exclusions", "*.js,*.css,/druid/*,/jdbc/*");
    bean.setInitParameters(initParams);

    //"/*" 表示过滤所有请求
    bean.setUrlPatterns(Arrays.asList("/*"));
    return bean;
}

《SpringBoot整合Druid数据源页面访问报该页面无法正常运作》

09 整合mybatis

1. 导入整合依赖包:mybatis-spring-boot-starter
2. 配置文件
3. mybatis配置
4. 编写sql
5. service层调用dao层
6. controller调用service层

依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.2</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>

application.yml
注意:非常重要,解决绑定异常:mapper-locationsmapper.xml最好和接口的包名路径一致,避免出现问题

# 整合数据源
spring:
  datasource:
    username: root
    password: admin
    url: jdbc:mysql://localhost:3306/mybatis?userUnicode=true&chacacterEncoding=utf-8&serverTimezone=UTC
    # Springboot使用 com.mysql.cj.jdbc.Driver 针对Mysql8以上,5可能会有bug
    driver-class-name: com.mysql.cj.jdbc.Driver
# 整合mybatis
mybatis:
  type-aliases-package: com.ssl.bean
  # 解决绑定异常:mapper.xml最好和接口的包名路径一致
  mapper-locations: classpath:com.ssl.mapper/*.xml

mapper接口和xml,bean

@Mapper// 这个注解表示了这是一个mapper的注解类
@Repository
public interface UserMapper {

    List<User> getAllUser();

    User getUserById(@Param("id") int id);

    void addUser(User user);

    void deleteUser(@Param("id")int id);

    User updateUser(User user);
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private int id;
    private String name;
    private String pwd;
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ssl.mapper.UserMapper">

    <select id="getAllUser" resultType="User">
      SELECT * FROM USER;
    </select>

    <select id="getUserById" parameterType="int" resultType="User">
      select * from user where id=#{id};
    </select>

    <insert id="addUser" parameterType="User">
        insert into user(id,name,pwd) values (#{id},#{name},#{pwd})
    </insert>

    <update id="updateUser" parameterType="User">
        update user set name=#{name},pwd=#{pwd} where id = #{id}
    </update>

    <delete id="deleteUser" parameterType="int">
        delete from user where id = #{id}
    </delete>
</mapper>

controller

@Controller
public class UserController {

    @Autowired
    private UserMapper userMapper;

    @RequestMapping("/users")
    @ResponseBody
    public List<User> getUsers() {
        return userMapper.getAllUser();
    }
}

10 整合 SpringSecurity安全框架

SpringSecurity环境搭建

在web开发中安全第一位!接着过滤器,拦截器~
功能性需求:否
做网站(安全第一位),因为存在漏洞会泄露隐私

shiro和SpringSecurity很像,除了类和名称不一样
认证,授权(vip1,vip2,vip3)

- 功能权限
- 访问权限
- 菜单权限
- ……拦截器,过滤器:大量的原生代码(冗余)

简介

SpringSecurity 是针对spring项目的安全框架,也是springboot底层默认的安全模块技术选型,它可以实现强大的Web安全机制,只需要少数的spring-boot--spring-security依赖,进行少量的配置,就可以实现强大的安全管理

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

记住几个类 :

WebSecurityConfigurerAdapter:自定义Security策略
AuthenticationManagerBuilder:自定义认证策略
@EnableWebSecurity:开启WebSecurity模式【@Enablexxxx:开启某个功能】

SpringSecurity的两个主要目标认证(en)和授权(or)【访问控制】

认证方式:Authentication
权限:Authorization

SpringSecurity (里面有很多版本可以选)
Spring 安全参考手册

认证和授权

控制器controller

@Controller
public class RouterController {

    @RequestMapping({"/", "/index"})
    public String index() {
        return "index";
    }

    @RequestMapping("/toLogin")
    public String toLogin() {
        return "views/login";
    }

    @RequestMapping("/level1/{id}")
    public String level1(@PathVariable("id") int id) {
        return "views/level1/" + id;
    }

    @RequestMapping("/level2/{id}")
    public String level2(@PathVariable("id") int id) {
        return "views/level2/" + id;
    }


    @RequestMapping("/level3/{id}")
    public String level3(@PathVariable("id") int id) {
        return "views/level3/" + id;
    }
}

  • 写一个类继承WebSecurityConfigurerAdapter,使用@EnableWebSecurity开启web安全服务
    • 地址授权:使用HttpSecurity security
    • 账户认证和给予权限:使用AuthenticationManagerBuilder builder
      • SpringSecurity5 以后默认需要密码加密方式,推荐使用passwordEncoder(new BCryptPasswordEncoder())
@EnableWebSecurity
public class MySecurityConfig extends WebSecurityConfigurerAdapter {
    // url授权:  HttpSecurity
    @Override
    protected void configure(HttpSecurity security) throws Exception {
        // 首页所有人可以访问,但是功能也只有对有权限的人可以访问
        security
                .authorizeRequests() // 认证请求
                .antMatchers("/").permitAll()
                .antMatchers("/level1/**").hasRole("vip1")
                .antMatchers("/level2/**").hasRole("vip2")
                .antMatchers("/level3/**").hasRole("vip3")
        ;
        // 自带登录页面,http://localhost:8080/login
            // 定制登录页,loginPage("/toLogin")
            // 指定表单提交url:loginProcessingUrl("/user/login")
        security.formLogin().loginPage("/toLogin")
                .usernameParameter("username").passwordParameter("password")
                .loginProcessingUrl("/user/login");
        // 开启注销功能,源码http://localhost:8080/logout,并且注销成功后跳转到/的Controller
        security.logout().logoutSuccessUrl("/");
            // 版本不同问题,可能会出现注销失败,关闭csrf
            // security.csrf().disable();
        // 开启记住我功能:本质就是记住一个cookies,默认保存2周
        security.rememberMe().rememberMeParameter("remember");
    }

    // 用户认证:AuthenticationManagerBuilder
        // SpringSecurity5 以后默认需要新郑密码密码加密方式
    @Override
    public void configure(AuthenticationManagerBuilder builder) throws Exception {
        // 内存中测试数据
        builder.inMemoryAuthentication()   // SpringSecurity5 以后默认需要新郑密码密码加密方式
                .passwordEncoder(new BCryptPasswordEncoder())
                .withUser("admin").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1")
                .and()
                .withUser("admin1").password(new BCryptPasswordEncoder().encode("123456")).roles("vip2", "vip3")
        ;
    }
}

登录和拦截

  • 定制登录
    • 开启登录功能:formLogin(),Springboot默认自带一个登录页/login定制看下面
    • 定制登录页:loginPage("/toLogin")
    • 修改表单提交的name:.usernameParameter("username").passwordParameter("password")
    • 表单提交的action:loginProcessingUrl("/user/login")
// 自带登录页面,http://localhost:8080/login
    // 定制登录页,loginPage("/toLogin")
    // 指定表单提交url:loginProcessingUrl("/user/login")
security.formLogin().loginPage("/toLogin")
        .usernameParameter("username").passwordParameter("password")
        .loginProcessingUrl("/user/login");

  • 注销
    • springboot自带注销页:/logout
    • 注销成功后跳转到/控制器
// 开启注销功能,源码http://localhost:8080/logout,并且注销成功后跳转到/的Controller
security.logout().logoutSuccessUrl("/");

  • 记住我
    • 本质就是存一个cookies,默认保存2周
    • 自定前端记住我的name:rememberMeParameter(“remember”)
// 开启记住我功能:本质就是记住一个cookies,默认保存2周
security.rememberMe().rememberMeParameter("remember");
<div class="field">
     <input type="checkbox" name="remember"> 记住我
</div>

补充:前端权限验证

  • 前端根据用户权限选择性展示元素,可以使用SpringSecurity和thymeleaf的整合包,更方便授权
  • 依赖:thymeleaf-extras-springsecurity5
    • Springboot2.1.X以上需要springSecurity5的版本
<!--security整合thymeleaf,便于前端整合
            Springboot2.1.X以上需要springSecurity5的版本
            但是xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4"后缀还是4依然可以使用
-->
<dependency>
    <groupId>org.thymeleaf.extras</groupId>
    <artifactId>thymeleaf-extras-springsecurity5</artifactId>
    <version>3.0.4.RELEASE</version>
</dependency>

  • 前端
    • 登录授权检查: 没有登录就消失登录按钮sec:authorize="!isAuthenticated()",反之亦然
    • 登录后,展示用户名:
<!--登录注销-->
<div class="right menu">
    <!--如果未登录,就消失登录按钮-->
    <div sec:authorize="!isAuthenticated()">
        <a class="item" th:href="@{/toLogin}">
            <i class="address card icon"></i> 登录
        </a>
    </div>
    <!--如果登录,就显示用户名和注销-->
    <div sec:authorize="isAuthenticated()">
        <a class="item" th:href="@{/logout}">
            <!--从授权那里获取name-->
            用户名:<span sec:authentication="name"></span>
            <!-- 有bug不能使用,角色:<span sec:authentication="principal.getAuthorities()"></span>-->
        </a>
        <a class="item" th:href="@{/logout}">
            <i class="sign-out card icon"></i> 注销
        </a>
    </div>
</div>

11 整合Shiro

Shiro简介

shiro 是什么?

  • Apache Shiro 是 Java 的一个安全(权限)框架。
  • Shiro 可以非常容易的开发出足够好的应用,其不仅可以用在 JavaSE 环境,也可以用在 JavaEE 环境。
  • Shiro 可以完成:认证、授权、加密、会话管理、与Web 集成、缓存等。
  • 下载:
    Shiro入门(十分钟教程)
    shiro-main 源码 github链接

shiro 有哪些功能?

狂神说——SpringBoot学习_第36张图片

  • Authentication:身份认证/登录,验证用户是不是拥有相应的身份

  • Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能进行什么操作,如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限

  • Session Management:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通JavaSE环境,也可以是Web 环境的

  • Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储

  • Web Support:Web 支持,可以非常容易的集成到Web 环境

  • Caching:缓存,比如用户登录后,其用户信息、拥有的角色/权限不必每次去查,这样可以提高效率

  • Concurrency:Shiro支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去

  • Testing:提供测试支持

  • “Run As”:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问

  • Remember Me:记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了

Shiro架构(外部)

狂神说——SpringBoot学习_第37张图片

  • Subject:应用代码直接交互的对象是Subject,也就是说Shiro的对外API 核心就是Subject。Subject 代表了当前“用户”,这个用户不一定是一个具体的人,与当前应用交互的任何东西都是Subject,如网络爬虫,机器人等;与Subject 的所有交互都会委托给SecurityManager;Subject 其实是一个门面,SecurityManager才是实际的执行者

  • SecurityManager:安全管理器;即所有与安全有关的操作都会与SecurityManager交互;且其管理着所有Subject;可以看出它是Shiro的核心,它负责与Shiro的其他组件进行交互,它相当于SpringMVC中DispatcherServlet的角色

  • Realm:Shiro从Realm 获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm 获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm 得到用户相应的角色/权限进行验证用户是否能进行操作;可以把Realm 看成DataSource

Shiro架构(内部)
狂神说——SpringBoot学习_第38张图片

  • Subject:任何可以与应用交互的“用户”;
  • SecurityManager:相当于SpringMVC中的DispatcherServlet;是Shiro的心脏;所有具体的交互都通过SecurityManager进行控制;它管理着所有Subject、且负责进行认证、授权、会话及缓存的管理。
  • Authenticator:负责Subject 认证,是一个扩展点,可以自定义实现;可以使用认证策略(Authentication Strategy),即什么情况下算用户认证通过了;
  • Authorizer:授权器、即访问控制器,用来决定主体是否有权限进行相应的操作;即控制着用户能访问应用中的哪些功能;
  • Realm:可以有1 个或多个Realm,可以认为是安全实体数据源,即用于获取安全实体的;可以是JDBC 实现,也可以是内存实现等等;由用户提供;所以一般在应用中都需要实现自己的Realm;
  • SessionManager:管理Session 生命周期的组件;而Shiro并不仅仅可以用在Web 环境,也可以用在如普通的JavaSE环境 CacheManager:缓存控制器,来管理如用户、角色、权限等的缓存的;因为这些数据基本上很少改变,放到缓存中后可以提高访问的性能
  • Cryptography:密码模块,Shiro提高了一些常见的加密组件用于如密码加密/解密。

狂神说——SpringBoot学习_第39张图片
狂神说——SpringBoot学习_第40张图片

thymeleaf依赖:


    org.thymeleaf
    thymeleaf-spring5


    org.thymeleaf.extras
    thymeleaf-extras-java8time

Hello World

快速实践:
查看官方文档:http://shiro.apache.org/tutorial.html
官方的quickstart : https://github.com/apache/shiro/tree/master/samples/quickstart/

  1. 创建一个maven父工程,用来学习Shiro,删掉不必要的部分
  2. 创建一个普通的Maven子工程:hell-shiro
    狂神说——SpringBoot学习_第41张图片
  3. 根据官方文档,我们导入Shiro的依赖
<dependencies>
    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-core</artifactId>
        <version>1.5.3</version>
    </dependency>

    <!-- configure logging -->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>jcl-over-slf4j</artifactId>
        <version>1.7.26</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>1.7.26</version>
    </dependency>
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
    </dependency>
</dependencies>
  1. 相关配置文件
    log4j.properties——官网

    log4j.rootLogger=INFO, stdout
    
    log4j.appender.stdout=org.apache.log4j.ConsoleAppender
    log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
    log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m %n
    
    # General Apache libraries
    log4j.logger.org.apache=WARN
    
    # Spring
    log4j.logger.org.springframework=WARN
    
    # Default Shiro logging
    log4j.logger.org.apache.shiro=INFO
    
    # Disable verbose logging
    log4j.logger.org.apache.shiro.util.ThreadContext=WARN
    log4j.logger.org.apache.shiro.cache.ehcache.EhCache=WARN
    

    shiro.ini——官网

    [users]
    # user 'root' with password 'secret' and the 'admin' role
    root = secret, admin
    # user 'guest' with the password 'guest' and the 'guest' role
    guest = guest, guest
    # user 'presidentskroob' with password '12345' ("That's the same combination on
    # my luggage!!!" ;)), and role 'president'
    presidentskroob = 12345, president
    # user 'darkhelmet' with password 'ludicrousspeed' and roles 'darklord' and 'schwartz'
    darkhelmet = ludicrousspeed, darklord, schwartz
    # user 'lonestarr' with password 'vespa' and roles 'goodguy' and 'schwartz'
    lonestarr = vespa, goodguy, schwartz
    
    # -----------------------------------------------------------------------------
    # Roles with assigned permissions
    # 
    # Each line conforms to the format defined in the
    # org.apache.shiro.realm.text.TextConfigurationRealm#setRoleDefinitions JavaDoc
    # -----------------------------------------------------------------------------
    [roles]
    # 'admin' role has all permissions, indicated by the wildcard '*'
    admin = *
    # The 'schwartz' role can do anything (*) with any lightsaber:
    schwartz = lightsaber:*
    # The 'goodguy' role is allowed to 'drive' (action) the winnebago (type) with
    # license plate 'eagle5' (instance specific id)
    goodguy = winnebago:drive:eagle5
    

    启动类 Quickstart——官网

    /*
     * Licensed to the Apache Software Foundation (ASF) under one
     * or more contributor license agreements.  See the NOTICE file
     * distributed with this work for additional information
     * regarding copyright ownership.  The ASF licenses this file
     * to you under the Apache License, Version 2.0 (the
     * "License"); you may not use this file except in compliance
     * with the License.  You may obtain a copy of the License at
     *
     *     http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing,
     * software distributed under the License is distributed on an
     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
     * KIND, either express or implied.  See the License for the
     * specific language governing permissions and limitations
     * under the License.
     */
    
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.*;
    import org.apache.shiro.config.IniSecurityManagerFactory;
    import org.apache.shiro.mgt.SecurityManager;
    import org.apache.shiro.session.Session;
    import org.apache.shiro.subject.Subject;
    import org.apache.shiro.util.Factory;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    
    /**
     * Simple Quickstart application showing how to use Shiro's API.
     * 简单入门Shiro使用API
     *
     * @since 0.9 RC2
     */
    public class Quickstart {
    
        private static final transient Logger log = LoggerFactory.getLogger(Quickstart.class);
    
    
        public static void main(String[] args) {
    
            // The easiest way to create a Shiro SecurityManager with configured
            // realms, users, roles and permissions is to use the simple INI config.
            // We'll do that by using a factory that can ingest a .ini file and
            // return a SecurityManager instance:
    
            // Use the shiro.ini file at the root of the classpath
            // (file: and url: prefixes load from files and urls respectively):
            // 读取配置文件:
            Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
            SecurityManager securityManager = factory.getInstance();
    
            // for this simple example quickstart, make the SecurityManager
            // accessible as a JVM singleton.  Most applications wouldn't do this
            // and instead rely on their container configuration or web.xml for
            // webapps.  That is outside the scope of this simple quickstart, so
            // we'll just do the bare minimum so you can continue to get a feel
            // for things.
            SecurityUtils.setSecurityManager(securityManager);
    
            // Now that a simple Shiro environment is set up, let's see what you can do:
    
            // get the currently executing user:
            // 获取当前的用户对象 Subject
            Subject currentUser = SecurityUtils.getSubject();
    
            // Do some stuff with a Session (no need for a web or EJB container!!!)
            //通过当前用户拿到Shiro的Session 可以脱离web存值取值
            Session session = currentUser.getSession();
            session.setAttribute("someKey", "aValue");
            String value = (String) session.getAttribute("someKey");
            if (value.equals("aValue")) {
                log.info("Retrieved the correct value! [" + value + "]");
            }
    
            // let's login the current user so we can check against roles and permissions:
            //判断当前的用户是否被认证
            if (!currentUser.isAuthenticated()) {
                //Token 令牌
                UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
                //设置记住我
                token.setRememberMe(true);
                try {
                    //执行登录操作
                    currentUser.login(token);
                } catch (UnknownAccountException uae) {
                    log.info("There is no user with username of " + token.getPrincipal());
                } catch (IncorrectCredentialsException ice) {
                    log.info("Password for account " + token.getPrincipal() + " was incorrect!");
                } catch (LockedAccountException lae) {
                    log.info("The account for username " + token.getPrincipal() + " is locked.  " +
                            "Please contact your administrator to unlock it.");
                }
                // ... catch more exceptions here (maybe custom ones specific to your application?
                catch (AuthenticationException ae) {
                    //unexpected condition?  error?
                }
            }
    
            //say who they are:
            //print their identifying principal (in this case, a username):
            log.info("User [" + currentUser.getPrincipal() + "] logged in successfully.");
    
            //test a role:
            // 检查角色
            if (currentUser.hasRole("schwartz")) {
                log.info("May the Schwartz be with you!");
            } else {
                log.info("Hello, mere mortal.");
            }
    
            //test a typed permission (not instance-level)
            //粗粒度
            if (currentUser.isPermitted("lightsaber:wield")) {
                log.info("You may use a lightsaber ring.  Use it wisely.");
            } else {
                log.info("Sorry, lightsaber rings are for schwartz masters only.");
            }
    
            //a (very powerful) Instance Level permission:
            //细粒度
            if (currentUser.isPermitted("winnebago:drive:eagle5")) {
                log.info("You are permitted to 'drive' the winnebago with license plate (id) 'eagle5'.  " +
                        "Here are the keys - have fun!");
            } else {
                log.info("Sorry, you aren't allowed to drive the 'eagle5' winnebago!");
            }
    
            //all done - log out!
            //注销
            currentUser.logout();
    
            //结束
            System.exit(0);
        }
    }
    

狂神说——SpringBoot学习_第42张图片
Spring Secutrry都有~(只是换了个名字)

// 获取当前的用户对象 Subject
Subject currentUser = SecurityUtils.getSubject();
Session session = currentUser.getSession();
currentUser.isAuthenticated()
    currentUser.getPrincipal()
    currentUser.hasRole("schwartz")
    currentUser.isPermitted("lightsaber:wield")
    currentUser.logout();

SpringBoot 集成 Shiro

SpringBoot整合Shiro环境搭建

  1. 新建一个项目或模块,勾选依赖
    狂神说——SpringBoot学习_第43张图片
    pom.xml

    <dependencies>
        <!--thymeleaf-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
    
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>
    
  2. 测试环境是否正常
    新建一个controller页面

    @Controller
    public class MyController {
    
        @RequestMapping({"/","/index"})
        public String toIndex(Model model) {
            model.addAttribute("msg","hello,Shiro");
            return "index";
        }
        
        @RequestMapping("/user/add")
        public String add() {
            return "user/add";
        }
    
        @RequestMapping("/user/update")
        public String update() {
            return "user/update";
    	}
    }
    

    新建一个index.html页面

    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>首页</title>
    </head>
    <body>
    <div>
        <h1>首页</h1>
        <p th:text="${msg}"></p>
    
        <hr>
        <a th:href="@{/user/add}">add</a>   | <a th:href="@{/user/update}">update</a>
    </div>
    </body>
    </html>
    

    新建一个add.html页面

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        <h1>add</h1>
    </body>
    </html>
    

    新建一个update.html页面

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        <h1>update</h1>
    </body>
    </html>
    

    项目结构
    狂神说——SpringBoot学习_第44张图片
    狂神说——SpringBoot学习_第45张图片

  3. 导入shiro整合spring的包——官网,查看最新版本

<!--
      Subject  用户
      SecurityManager 管理所有用户
      Realm 连接数据库
-->

<!--shiro整合spring的包-->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.5.3</version>
</dependency>

4.编写导入配置类
编写一个自定义类UserRealm

//自定义的UserRealm
public class UserRealm extends AuthorizingRealm {
    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("执行了=>授权doGetAuthorizationInfo");
        return null;
    }

    //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("执行了=>认证doGetAuthorizationInfo");
        return null;
    }
}

编写配置ShiroConfig

创建realm对象,需要自定义类
DefaultWebSecurityManager
ShiroFilterFactoryBean
@Configuration
public class ShiroConfig {

    //3. shiroFilterFactoryBean

    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("getDefaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager) {
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        // 设置安全管理器
        bean.setSecurityManager(defaultWebSecurityManager);

        return bean;
    }

    //2. DefaultWebSecurityManager

    @Bean
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();

        // 关联userRealm
        securityManager.setRealm(userRealm);
        return securityManager;
    }
    //1. 创建realm对象,需要自定义类

    @Bean
    public UserRealm userRealm() {
        return new UserRealm();
    }
}

Shiro实现登录拦截

在ShiroConfig中的getShiroFilterFactoryBean方法中添加如下配置

anon: 无需认证就可以访问
authc: 必须认证了才能访问
user: 必须拥有记住我功能才能用
perms: 拥有对某个资源的权限才能访问
role: 拥有某个角色权限
Map<String, String> filterMap = new LinkedHashMap<>();
filterMap.put("/user/add","authc");
filterMap.put("/user/update","authc");
bean.setFilterChainDefinitionMap(filterMap);

点击首页的add或者update之后
狂神说——SpringBoot学习_第46张图片

添加拦截成功页面
登录页面login.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录页面</title>
</head>
<body>
<h1>登录</h1>
<hr>

<form action="">
    <p>用户名:<input type="text" name="username"></p>
    <p>密码:<input type="text" name="password"></p>
    <p>密码:<input type="submit"></p>
</form>
</body>
</html>

在MyConfig中添加

@RequestMapping("/toLogin")
public String toLogin() {
    return "login";
}

在ShiroConfig中的getShiroFilterFactoryBean方法中添加如下配置

//设置登录的请求
bean.setLoginUrl("/toLogin");

拦截成功页面
狂神说——SpringBoot学习_第47张图片

Shiro实现用户认证

  1. 在MyController中编写用户提交表单之后处理
@RequestMapping("/login")
public String login(String username, String password, Model model) {
    //获取一个用户
    Subject subject = SecurityUtils.getSubject();
    // 封装用户的登录数据
    UsernamePasswordToken token = new UsernamePasswordToken(username, password);

    try {
        subject.login(token);//执行登录的方法,如果没有异常就说明ok了
        return "index";
    } catch (UnknownAccountException e) {//用户名不存在
        model.addAttribute("msg","用户名错误");
        return "login";
    } catch (IncorrectCredentialsException e) {//密码不存在
        model.addAttribute("msg","密码错误");
        return "login";
    }

}
  1. login.html的修改
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>登录页面</title>
</head>
<body>
<h1>登录</h1>
<hr>

<p th:text="${msg}" style="color: red;"></p>
<form th:action="@{/login}">
    <p>用户名:<input type="text" name="username"></p>
    <p>密码:<input type="text" name="password"></p>
    <p>密码:<input type="submit"></p>
</form>
</body>
</html>

3.用户输入登录信息
页面:
狂神说——SpringBoot学习_第48张图片
4. 用户认证编写UserRealm中的认证(doGetAuthenticationInfo)

//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    System.out.println("执行了=>认证doGetAuthorizationInfo");
    // 用户名、密码, 数据中取
    String name = "root";
    String password = "123456";

    UsernamePasswordToken userToken = (UsernamePasswordToken) token;

    if (!userToken.getUsername().equals(name)) {
        return null;//抛出异常 UnknownAccountException
    }

    // 密码认证,shiro做
    return new SimpleAuthenticationInfo("",password,"");
}

Shiro整合Mybatis

  1. 导入依赖
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>

<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.23</version>
</dependency>

<!--引入mybatis,这是MyBatis官方提供的适配spring Boot的,而不是spring Boot自己的-->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
 <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.3</version>
</dependency>
  1. 配置application.yml
spring:
  datasource:
    username: root
    password: admin
    #?serverTimezone=UTC解决时区的报错
    url: jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource

    #Spring Boot 默认是不注入这些属性值的,需要自己绑定
    #druid 数据源专有配置
    initialSize: 5
    minIdle: 5
    maxActive: 20
    maxWait: 60000
    timeBetweenEvictionRunsMillis: 60000
    minEvictableIdleTimeMillis: 300000
    validationQuery: SELECT 1 FROM DUAL
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false
    poolPreparedStatements: true

    #配置监控统计拦截的filters,stat:监控统计、log4j:日志记录、wall:防御sql注入
    #如果允许时报错  java.lang.ClassNotFoundException: org.apache.log4j.Priority
    #则导入 log4j 依赖即可,Maven 地址:https://mvnrepository.com/artifact/log4j/log4j
    filters: stat,wall,log4j
    maxPoolPreparedStatementPerConnectionSize: 20
    useGlobalDataSourceStat: true
    connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
    
mybatis:
  type-aliases-package: nuc.ss.pojo
  mapper-locations: classpath:mapper/*.xml
  1. 编写User 类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private int id;
    private String name;
    private String pwd;
}
  1. UserMapper.xml映射
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
     PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace=绑定一个对应的Dao/Mapper接口-->
<mapper namespace="nuc.ss.mapper.UserMapper">
    
    <select id="queryUserList" resultType="User">
        select * from mybatis.user;
    </select>

    <select id="queryUserById" resultType="User">
        select * from mybatis.user where id = #{id};
    </select>

    <insert id="addUser" parameterType="User">
        insert into mybatis.user (id, name, pwd) values (#{id},#{name},#{pwd});
    </insert>

    <update id="updateUser" parameterType="User">
        update mybatis.user set name=#{name},pwd = #{pwd} where id = #{id};
    </update>

    <delete id="deleteUser" parameterType="int">
        delete from mybatis.user where id = #{id}
    </delete>
</mapper>
  1. 实现UserService 接口
public interface UserService {

    public User queryUserByName(String name);
}
  1. UserServiceImpl业务逻辑
@Service
public class UserServiceImpl implements UserService {

    @Autowired
    UserMapper userMapper;
    @Override
    public User queryUserByName(String name) {
        return userMapper.queryUserByName(name);
    }
}

7.测试环境

@SpringBootTest
class ShiroSpringbootApplicationTests {

    @Autowired
    UserService userService;
    @Test
    void contextLoads() {
        System.out.println(userService.queryUserByName("狂神"));
    }

}

狂神说——SpringBoot学习_第49张图片
8.UserRealm连接真实数据库

//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    System.out.println("执行了=>认证doGetAuthorizationInfo");

    UsernamePasswordToken userToken = (UsernamePasswordToken) token;
    
    // 真实数据库 用户名、密码, 数据中取
    User user = userService.queryUserByName(userToken.getUsername());

    if (user == null) {//没有这个人
        return null;
    }

    // 密码认证,shiro做
    return new SimpleAuthenticationInfo("",user.getPwd(),"");
}

狂神说——SpringBoot学习_第50张图片
9. 断点测试密码加密类型
打断点Debug:
狂神说——SpringBoot学习_第51张图片
默认是SimpleCredentialsMatcher加密
狂神说——SpringBoot学习_第52张图片
MD5加密(MD5盐值加密)——测试

123456——E10ADC3949BA59ABBE56E057F20F883E

所有加密
狂神说——SpringBoot学习_第53张图片

Shiro 实现用户授权

  1. ShiroConfig中的getShiroFilterFactoryBean方法添加认证代码
//授权,正常情况下,没有授权会跳转到为授权页面
filterMap.put("/user/add","perms[user:add]");
filterMap.put("/user/update","perms[user:update]");
  1. 登录之后点击add按钮会弹出如下页面
    狂神说——SpringBoot学习_第54张图片
  2. 添加为授权页面
  • MyController

    @RequestMapping("/noauto")
    @ResponseBody
    public String unauthorized() {
        return "未经授权,无法访问此页面";
    }
    
  • ShiroConfig中的getShiroFilterFactoryBean方法中添加

    //为授权页面
    bean.setUnauthorizedUrl("/noauto");
    
  1. 再次测试:
    狂神说——SpringBoot学习_第55张图片
    所以需要在UserRealm中为用户进行真正授权

  2. UserRealm类的修改

//自定义的UserRealm
public class UserRealm extends AuthorizingRealm {

    @Autowired
    UserService userService;
    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("执行了=>授权doGetAuthorizationInfo");

        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();

        //拿到当前登录的这个对象
        Subject subject = SecurityUtils.getSubject();
        User currentUser = (User)subject.getPrincipal();//拿到user对象

        //设置当前用户的权限
        info.addStringPermission(currentUser.getPerms());

        return info;
    }

    //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        ......
        // 密码认证,shiro做
        return new SimpleAuthenticationInfo(user,user.getPwd(),"");
    }
}
  1. 再次测试
    狂神说——SpringBoot学习_第56张图片

Shiro整合Thymeleaf

  1. 导包
    shiro-thymeleaf整合包导入——官网
<!--shiro-thymeleaf整合-->
<dependency>
    <groupId>com.github.theborakompanioni</groupId>
    <artifactId>thymeleaf-extras-shiro</artifactId>
    <version>2.0.0</version>
</dependency>
  1. 在ShiroConfig中整合ShiroDialect
// 整合ShiroDialect: 用来整合 Shiro thymeleaf
@Bean
public ShiroDialect getShiroDialect() {
    return new ShiroDialect();
}
  1. index.html页面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
      xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>

<div>
    <h1>首页</h1>
    <p th:text="${msg}"></p>

    <!--用session实现,配合UserRealm中的session实现-->
    <!--<div th:if="${session.loginUser==null}">
        <a th:href="@{/toLogin}">登录</a>
    </div>-->

    <div shiro:notAuthenticated>
        <a th:href="@{/toLogin}">登录</a>
    </div>

    <hr>

    <div shiro:hasPermission="user:add">
        <a th:href="@{/user/add}">add</a>
    </div>

    <div shiro:hasPermission="user:update">
        <a th:href="@{/user/update}">update</a>
    </div>

</div>
</body>
</html>
  1. 页面展示
    狂神说——SpringBoot学习_第57张图片

Shiro案例的所有代码

  • ShiroConfig
package nuc.ss.config;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.LinkedHashMap;
import java.util.Map;

@Configuration
public class ShiroConfig {

    //shiroFilterFactoryBean

    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("getDefaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager) {
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        // 设置安全管理器
        bean.setSecurityManager(defaultWebSecurityManager);

        // 添加shiro的内置过滤器
        /*
            anon: 无需认证就可以访问
            authc: 必须认证了才能访问
            user: 必须拥有记住我功能才能用
            perms: 拥有对某个资源的权限才能访问
            role: 拥有某个角色权限
         */

        //拦截
        Map<String, String> filterMap = new LinkedHashMap<>();
        //filterMap.put("/user/add","authc");
        //filterMap.put("/user/update","authc");


        //授权,正常情况下,没有授权会跳转到为授权页面
        filterMap.put("/user/add","perms[user:add]");
        filterMap.put("/user/update","perms[user:update]");

        filterMap.put("/user/*","authc");

        bean.setFilterChainDefinitionMap(filterMap);

        //设置登录的请求
        bean.setLoginUrl("/toLogin");

        //为授权页面
        bean.setUnauthorizedUrl("/noauto");

      return bean;
    }

    //DefaultWebSecurityManager

    @Bean
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();

        // 关联userRealm
        securityManager.setRealm(userRealm);
        return securityManager;
    }
    //创建realm对象,需要自定义类

    @Bean
    public UserRealm userRealm() {
        return new UserRealm();
    }

    // 整合ShiroDialect: 用来整合 Shiro thymeleaf
    @Bean
    public ShiroDialect getShiroDialect() {
        return new ShiroDialect();
    }
}
  • UserRealm
package nuc.ss.config;

import nuc.ss.pojo.User;
import nuc.ss.service.UserService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;

//自定义的UserRealm
public class UserRealm extends AuthorizingRealm {

    @Autowired
    UserService userService;
    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("执行了=>授权doGetAuthorizationInfo");

        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();

        //info.addStringPermission("user:add");

        //拿到当前登录的这个对象
        Subject subject = SecurityUtils.getSubject();
        User currentUser = (User)subject.getPrincipal();//拿到user对象

        //设置当前用户的权限
        info.addStringPermission(currentUser.getPerms());

        return info;
    }

    //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("执行了=>认证doGetAuthorizationInfo");

        UsernamePasswordToken userToken = (UsernamePasswordToken) token;

        // 虚拟用户
        //String name = "root";
        //String password = "123456";
        //if (!userToken.getUsername().equals(name)) {
        //    return null;//抛出异常 UnknownAccountException
        //}

        // 真实数据库 用户名、密码, 数据中取
        User user = userService.queryUserByName(userToken.getUsername());

        if (user == null) {//没有这个人
            return null;
        }

        //首页
        //Subject currentSubject = SecurityUtils.getSubject();
        //Session session = currentSubject.getSession();
        //session.setAttribute("loginUser",user);


        // 密码认证,shiro做
        return new SimpleAuthenticationInfo(user,user.getPwd(),"");
    }
}
  • MyController
package nuc.ss.controller;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class MyController {

    @RequestMapping({"/","/index"})
    public String toIndex(Model model) {
        model.addAttribute("msg","hello,Shiro");
        return "index";
    }

    @RequestMapping("/user/add")
    public String add() {
        return "user/add";
    }

    @RequestMapping("/user/update")
    public String update() {
        return "user/update";
    }

    @RequestMapping("/toLogin")
    public String toLogin() {
        return "login";
    }

    @RequestMapping("/login")
    public String login(String username, String password, Model model) {
        //获取一个用户
        Subject subject = SecurityUtils.getSubject();
        // 封装用户的登录数据
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);

        try {
            subject.login(token);//执行登录的方法,如果没有异常就说明ok了
            return "index";
        } catch (UnknownAccountException e) {//用户名不存在
            model.addAttribute("msg","用户名错误");
            return "login";
        } catch (IncorrectCredentialsException e) {//密码不存在
            model.addAttribute("msg","密码错误");
            return "login";
        }
    }

    @RequestMapping("/noauto")
    @ResponseBody
    public String unauthorized() {
        return "未经授权,无法访问此页面";
    }
}
  • pom依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>nuc.ss</groupId>
    <artifactId>shiro-springboot</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>shiro-springboot</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring-boot.version>2.3.0.RELEASE</spring-boot.version>
    </properties>

    <dependencies>

        <!--
            Subject  用户
            SecurityManager 管理所有用户
            Realm 连接数据库
        -->

        <!--shiro-thymeleaf整合-->
        <!-- https://mvnrepository.com/artifact/com.github.theborakompanioni/thymeleaf-extras-shiro -->
        <dependency>
            <groupId>com.github.theborakompanioni</groupId>
            <artifactId>thymeleaf-extras-shiro</artifactId>
            <version>2.0.0</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.23</version>
        </dependency>

        <!--引入mybatis,这是MyBatis官方提供的适配spring Boot的,而不是spring Boot自己的-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.3</version>
        </dependency>

        <!--shiro整合spring的包-->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.5.3</version>
        </dependency>
        <!--thymeleaf-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

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

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

推荐:《让 Apache Shiro 保护你的应用》

12 整合 swagger

swagger官网

目标:

  • 了解swagger的作用和概念
  • 了解前后端分离
  • 在springboot中集成swagger

简介

前后端分离:
vue+springboot
后端时代:前端只用管理静态页面:HTML==》后端。模板引擎 JSP ==》后端是主力

前后端分离式时代:

- 后端:后端控制层、服务层、数据访问层
- 前端:前端控制层、视图层
	伪造后端数据,JSON。已经存在了,不需要后端前端工程依旧可以跑起来
- 前后端如何交互?【API】
- 前后端相对独立,松耦合
- 前后端可以部署在不同的服务器上

产生一个问题: 前后端集成联调,前端人员和后端人员无法做到“及时协商,尽早解决”,最终导致问题集中爆发

解决方案

  • 首先指定schema(计划的方案/提纲),实时更新API,降低风险
  • 早些年指定Word计划文档
  • 前后端分离:前端测试后端接口:postman;后端提供接口,需要实时更新最新的消息及改动

swagger 优势

  • 号称世界上最流行的API框架
  • Restful Api 文档在线自动生成工具==》AP与API文档同步更新
  • 直接运行,可以在线测试API接口
  • 支持多种语言(Java、PHP、……)

在项目使用swagger时需要springbox;

  • swagger2
  • ui

第一个swagger

  1. 导依赖
 <!-- springfox-swagger2 -->
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
    <version>2.9.2</version>
</dependency>
<!-- springfox-swagger-ui -->
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger-ui</artifactId>
    <version>2.9.2</version>
</dependency>

  1. helloworld
@RestController
public class HelloController {

    @RequestMapping(value = "/hello")
    public String hello() {
        return "hello";
    }
}

  1. 配置Swagger,编写一个空白的MySwagger
@Configuration
@EnableSwagger2 // 开启Swagger2
public class MySwagger {
    
}

  1. 测试: http://localhost:8080/swagger-ui.html
    狂神说——SpringBoot学习_第58张图片

修改默认info信息

  • 修改apiInfo,访问http://localhost:8080/swagger-ui.html
@Configuration
@EnableSwagger2 // 开启Swagger2
public class MySwagger {

    // 配置SwaggerBean实例
    @Bean
    public Docket getDocker() {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(changeInfo());
    }

    // 更改默认info
    private ApiInfo changeInfo() {
        Contact author_contact = new Contact("laoSong", "http://123", "[email protected]");
        return new ApiInfo(
                        "swagger文档学习",
                        "学习swagger",
                        "v1.0",
                        "http://123",
                        author_contact,
                        "Apache 2.0",
                        "http://www.apache.org/licenses/LICENSE-2.0",
                        new ArrayList());

    }

}

配置扫描接口和路径

@Configuration
@EnableSwagger2 // 开启Swagger2
public class MySwagger {

    // 配置SwaggerBean实例
    @Bean
    public Docket getDocker() {
        return new Docket(DocumentationType.SWAGGER_2)
                // 是否启用swagger,则浏览器不能访问
                // .enable(false)
                .apiInfo(changeInfo())
                .select()
                        // 扫描接口:,点进去看源码有很多种方式指定扫描方式
                        .apis(RequestHandlerSelectors.basePackage("com.ssl.controller"))
                        // 扫描路劲:过滤路径
                        .paths(PathSelectors.ant("/hello"))
                        .build();
    }

    // 更改默认info
    private ApiInfo changeInfo() {
        Contact author_contact = new Contact("laoSong", "http://123", "[email protected]");
        return new ApiInfo(
                        "swagger文档学习",
                        "学习swagger",
                        "v1.0",
                        "http://123",
                        author_contact,
                        "Apache 2.0",
                        "http://www.apache.org/licenses/LICENSE-2.0",
                        new ArrayList());

    }

}

如何指定swagger在生产环境中使用

方法:

  1. 判断是不是生产环境的yml
  2. 注入enable(flag)
  • application.yml,指定环境配置时dev
server:
  port: 8080
# 选择使用哪些配置
spring:
  profiles:
    active: dev # 激活dev配置
---
server:
  port: 8081
spring:
  profiles: test
---
server:
  port: 8082
spring:
  profiles: dev

SwaggerConfig

@Configuration
@EnableSwagger2 // 开启Swagger2
public class MySwagger {

    // 配置SwaggerBean实例
    @Bean
    public Docket getDocker(Environment environment) {
        // 判断是否是生产环境的配置文件
        Profiles isDevPro = Profiles.of("dev");
        boolean flag = environment.acceptsProfiles(isDevPro);

        return new Docket(DocumentationType.SWAGGER_2)
                // 是否启用swagger,则浏览器不能访问
                // .enable(false)
                // 这里判断是dev生产环境才开启Swagger
                .enable(flag)
                .apiInfo(changeInfo())
                .select()
                // 扫描接口:,点进去看源码有很多种方式指定扫描方式
                .apis(RequestHandlerSelectors.basePackage("com.ssl.controller"))
                // 扫描路劲:过滤路径
                .paths(PathSelectors.ant("/hello"))
                .build();
    }

    // 更改默认info
    private ApiInfo changeInfo() {
        Contact author_contact = new Contact("laoSong", "http://123", "[email protected]");
        return new ApiInfo(
                "swagger文档学习",
                "学习swagger",
                "v1.0",
                "http://123",
                author_contact,
                "Apache 2.0",
                "http://www.apache.org/licenses/LICENSE-2.0",
                new ArrayList());

    }

}

API分组

设置docket分组,配置多个不同名注入bean,供多人开发使用

@Bean
public Docket docket2() {
    return new Docket(DocumentationType.SWAGGER_2)
        .groupName("AA");
}

常用api注释

@ApiModel("User实体类")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {

    @ApiModelProperty("用户名")
    private String username;
    @ApiModelProperty("用户密码")
    private String password;
}

@RestController
public class HelloController {

    @GetMapping(value = "/hello")
    public String hello() {
        return "hello";
    }

    //返回实体类,就会扫描进SwaggerUI中的modal
    @GetMapping(value = "/user")
    @ApiOperation("返回一个user")
    public User user() {
        return new User("张三", "123456");
    }
}

总结:

  1. 我们可以通过swagger给一些比较难理解的属性或者接口增加注释
  2. 接口文档实时更新
  3. 可以在线测试

Swagger 是一个优秀的工具,几乎所有大公司都在使用,所以要好好学习
注意:在正式发布的时候要关闭Swagger!!!?出于安全考虑而且节省 运行的内存

13 异步任务

方法上使用: @Async
启动类上使用:@EnableAsync

@Service
public class AsyncService {
    //告诉Spring这是一个异步的方法
    @Async
    public void hello() {
        try{
           Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("正在处理异步请求");
    }
}
@Controller
public class AsyncController {
    @Autowired
    private AsyncService asyncService;

    @RequestMapping("/hello")
    @ResponseBody
    public String hello() {
        return "hello";
    }
    // 开启异步后,先返回结果,在等待后台处理请求
    @RequestMapping("/sleep")
    @ResponseBody
    public String sleep() {
        // hello开启异步后,会创建另一个线程进行该操作
        asyncService.hello();
        return "hello";
    }
}
// 启动类开启异步注解功能
@EnableAsync
@SpringBootApplication
public class TestApplication {
    public static void main(String[] args) {
        SpringApplication.run(TestApplication.class, args);
    }
}

14 邮件任务

依赖

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

15 定时任务

1.开启异步定时器功能的注解 @EnableScheduling
2.编写service类 :ScheduledService ,注入service 写方法标注cron表达式(定时)

《Springboot异步、邮件发送、定时任务》

16 SpringBoot整合Redis

这个文章挺详细的
《springboot整合redis》——作者:渔光一点萤@

17 整合Dubbo+Zookeeper(分布式 dubbo+ zookeeper+ springboot)

分布式

什么是分布式系统?
在《分布式系统原理与范型》一书中有如下定义:“分布式系统是若干独立计算机的集合,这些计算机对于用户来说就像单个相关系统”;
分布式系统是由一组通过网络进行通信、为了完成共同的任务而协调工作的计算机节点组成的系统。分布式系统的出现是为了用廉价的、普通的机器完成单个计算机无法完成的计算、存储任务。其目的是利用更多的机器,处理更多的数据。
分布式系统(distributed system)是建立在网络之上的软件系统。
首先需要明确的是,只有当单个节点的处理能力无法满足日益增长的计算、存储任务的时候,且硬件的提升(加内存、加磁盘、使用更好的CPU)高昂到得不偿失的时候,应用程序也不能进一步优化的时候,我们才需要考虑分布式系统。因为,分布式系统要解决的问题本身就是和单机系统一样的,而由于分布式系统多节点、通过网络通信的拓扑结构,会引入很多单机系统没有的问题,为了解决这些问题又会引入更多的机制、协议,带来更多的问题。。。

随着互联网的发展,网站应用的规模不断扩大,常规的垂直应用架构已无法应对,分布式服务架构以及流动计算架构势在必行,急需一个治理系统确保架构有条不紊的演进。
在Dubbo的官网文档有这样一张图
在这里插入图片描述
狂神说——SpringBoot学习_第59张图片
狂神说——SpringBoot学习_第60张图片
狂神说——SpringBoot学习_第61张图片
狂神说——SpringBoot学习_第62张图片

Dubbo

dubbo 官网
Apache Dubbo |ˈdʌbəʊ| 是一款高性能、轻量级的开源Java RPC框架,使得应用可通过高性能的 RPC 实现服务的输出和输入功能,可以和 Spring框架无缝集成。它提供了三大核心能力:

  1. 面向接口的远程方法调用
  2. 智能容错和负载均衡
  3. 以及服务自动注册和发现。

dubbo架构:
狂神说——SpringBoot学习_第63张图片
Dobbo:阿里巴巴公司开源的一个高性能优秀的(分布式)服务框架,使得应用可通过高性能的RPC 实现服务的输出和输入功能,可以和 Spring框架无缝集成。(RPC:远程过程调用)

Dobbo三大核心能力

  • 面向接口的远程方法调用
  • 智能容错和负载均衡
  • 服务自动注册和发现

Dubbo提供的注册中心有如下几种类型可供选择:

  • Multicast注册中心
  • Zookeeper注册中心(推荐)
  • Redis注册中心
  • Simple注册中心

Dubbo优缺点

优点:

  1. 透明化的远程方法调用—— 像调用本地方法一样调用远程方法;只需简单配置,没有任何API侵入。
  2. 软负载均衡及容错机制——可在内网替代nginx 、lvs等硬件负载均衡器。
  3. 服务注册中心自动注册 & 配置管理——不需要写死服务提供者地址,注册中心基于接口名自动查询提供者ip。使用类似zookeeper等分布式协调服务作为服务注册中心,可以将绝大部分项目配置移入zookeeper集群。
  4. 服务接口监控与治理——Dubbo-admin与Dubbo-monitor提供了完善的服务接口管理与监控功能,针对不同应用的不同接口,可以进行多版本多协议多注册中心管理。

缺点:只支持JAVA语言

Zookeeper是一个分布式的服务框架,是树型的目录服务的数据存储,能做到集群管理数据 ,这里能很好的作为Dubbo服务的注册中心。
Dubbo能与Zookeeper做到集群部署,当提供者出现断电等异常停机时,Zookeeper注册中心能自动删除提供者信息,当提供者重启时,能自动恢复注册数据,以及订阅请求。
Dubbo本身并不是一个服务软件,它其实就是一个jar包,能够帮你的Java程序连接到zookeeper ,并利用zookeeper 消费、提供服务。为了让用户更好的管理监控众多的dubbo服务,官方提供了一个可视化的监控程序dubbo-admin。

RPC(两大核心:通讯,序列化)

什么是RPC? 推荐

  • Http:是无状态的,基于网络通信的协议
  • RPC:远程过程调用,是允许程序调用另一个地址空间的接口,两个核心:通讯和序列化
    狂神说——SpringBoot学习_第64张图片

Zookeeper

zookeeper下载地址
推荐去dubbo官网了解一下zookeeper
注意:一台电脑只能存在一个版本的zookeeper,否则会出现zookeeper版本不一致出现重连!!!!!

ZooKeeper是一个分布式的服务,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件。

它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。
ZooKeeper的目标就是封装好复杂易出错的关键服务,将简单易用的接口和性能高效、功能稳定的系统提供给用户。
ZooKeeper包含一个简单的原语集,提供Java和C的接口。
ZooKeeper代码版本中,提供了分布式独享锁、选举、队列的接口,代码在$zookeeper_home\src\recipes。
其中分布锁和队列有Java和C两个版本,选举只有Java版本。

狂神说——SpringBoot学习_第65张图片
总结:

  • zookeeper :注册中心(要)
  • dubbo-admin:是一 个监控管理后台-查看我们注册了哪些服务,哪些服务被消费了(可以不用)
  • Dubbo: jar包

步骤:

  1. 提供者提供服务

     导入依赖
     配置注册中心的地址,以及服务发现名,和要扫描的包~
     在想要被注册的服务上添加 @Service 注解
    
  2. 消费者如何消费

     导入依赖
     配置注册中心的地址,配置自己的服务名
     从远程注入服务 @Reference
    

总结

三层架构 + MVC (model 、view、controller)
架构 —> 解耦
狂神说——SpringBoot学习_第66张图片
狂神说——SpringBoot学习_第67张图片

微服务的优势

1)将复杂的业务拆分成多个小的业务,每个业务拆分成一个服务,将复杂的问题简单化。利于分工,降低新人的学习成本。
2)微服务系统是分布式系统,业务与业务之间完全解耦,随着业务的增加可以根据业务再拆分,具有极强的横向扩展能力。面对搞并发的场景可以将服务集群化部署,加强系统负载能力。
3)服务间采用HTTP协议通信,服务与服务之间完全独立。每个服务可以根据业务场景选取合适的编程语言和数据库。
4)微服务每个服务都是独立部署的,每个服务的修改和部署对其他服务没有影响。

微服务和SOA的关系

SOA即面向服务的架构,SOA是根据企业服务总线(ESB)模式来整合集成大量单一庞大的系统,微服务可以说是SOA的一种实现,将复杂的业务组件化。但它比ESB实现的SOA更加的轻便敏捷和简单。
狂神说——SpringBoot学习_第68张图片
狂神说——SpringBoot学习_第69张图片
狂神说——SpringBoot学习_第70张图片
狂神说——SpringBoot学习_第71张图片
Javascript框架:jQuery、Angular/ˈæŋɡjələ(r)/ 、React/riˈækt/ 、Vue、Axios ……
前端三大框架:Angular/ˈæŋɡjələ(r)/ 、React/riˈækt/ 、Vue

  • jQuery: 大家熟知的JavaScript框架,优点是简化了DOM操作,缺点是DOM操作太频繁,影响前端性能;在前端眼里使用它仅仅是为了兼容IE6、7、8;

  • Angular(前端的模块化开发MVC模式): Google收购的前端框架,由一群Java程序员开发,其特点是将后台的MVC模式搬到了前端并增加了模块化开发的理念,与微软合作,采用TypeScript语法开发;对后台程序员友好,对前端程序员不太友好;最大的缺点是版本迭代不合理(如: 1代-> 2代,除了名字,基本就是两个东西;截止发表博客时已推出了Angular6)
    (MVC 三层架构: Model-View-Controller)

  • React(虚拟DOM放进内存做): Facebook出品,一款高性能的JS前端框架;特点是提出了新概念[虚拟DOM]用于减少真实DOM操作,在内存中模拟DOM操作,有效的提升了前端渲染效率;缺点是使用复杂,因为需要额外学习一门[JSX] 语言;
    (DOM:HTML操作的元素)

  • Vue:一款渐进式JavaScript框架,所谓渐进式就是逐步实现新特性的意思,如实现模块化开发、路由、状态管理等新特性。其特点是综合了Angular (模块化)和React (虚拟DOM)的优点;

Vue在线cdn:

<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.min.js"></script> (压缩版的)

Axios :前端通信框架;因为Vue 的边界很明确,就是为了处理DOM,所以并不具备通信能力,此时就需要额外使用一个通信框架与服务器交互;当然也可以直接选择使用jQuery提供的AJAX通信功能

你可能感兴趣的:(java,spring,spring,boot,java,spring)