SpringBoot整合

前言

在此之前学习的有:
javaSE: OOP(面向对象)
MySQL:持久化层
html+CSS+js+jquery+框架:视图层
javaweb:独立开发MVC三层框架的原始网站
ssm整合:框架极大的简化了整体的开发流程,配置也开始较为复杂;
此阶段项目打包都是在war包,整体程序也都是在Tomcat中运行

springBoot就是spring的简化升级版本,核心就是自动装配
springBoot打包都是用的jar包,同时内嵌Tomcat; 微服务架构
在这个时候服务越来越多,就出现了springCloud;
微服务整题构架示意图:
在这里插入图片描述

springBoot快速入门

回忆:什么是spring

spring是一个轻量级开源框架,2003年兴起,
是为了解决企业及应用开发的复杂性而创建的,用来简化开发

spring是如何简化java开发的:

spring为了降低java开发的复杂性,采用了以下四种策略

  • 基于POJO(实体类)的轻量级和最小侵入性编程
  • 通过IOC,依赖注入(DI)和面向接口实现松耦合;
  • 基于切面(AOP)和管理进行声明式编程
  • 通过切面和模板减少样式代码;

springBoot简介:

springBoot其实不是什么新的框架,它只是默认配置了很多框架的使用方式,就像maven整合了所有的jar包,springBoot整合了所有的框架.
springBoot出身名门,从一开始就站在一个比较高的起点,又经过这几年的发展,生态足够完善,SpringBoot已经当之无愧称为java领域最热门的技术

springBoot主要优点:

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

微服务

微服务是一种架构风格,它要求我们在开发一个应用的时候,这个应用必须构建成一系列小服务的组合;可通过http(不唯一)的方式进行互通.
说白了微服务就是一个个的不同业务,但是由于分布式开发的原因,需要在不同的电脑上部署同一项目的不同业务,也就是将一个个的业务拆分出来,这就是微服务
比如写好springmvc和Controller之后只需要提供一个接口

单体应用架构

单体应用架构就是指将一个应用的所有服务都封装在一个应用中,就比如之前做的超市订单管理系统

  • 好处是易于开发和测试;也十分方便部署,当需要进行扩展时,只需要将war包进行复制,然后放在多个服务器上,再做个负载均衡就可以了.
  • 单体应用架构的缺点是,无论要修改什么地方都需要停掉整个服务,然后再重新打包部署这个应用war包,特别是对于一个大型应用,不可能将所有内容都放在一个应用中,不然如何维护如何分工合作都是问题

微服务架构成型

所谓微服务架构,就是把每个功能元素独立出来,再把土里出来的功能元素进行动态组合,需要的功能元素拿去组合,需要多一些时可以整合多个功能元素,
所以微服务无架构是对功能元素进行复制,而没有对整个应用进行复制
这么做的好处是节省调用资源,
并且每一个功能元素的服务都是一个可替换的,可独立升级的软件代码
满足高内聚低耦合的需求
一个大型系统的微服务架构就像一个复杂交织的神经网络,每一个神经元都是一个功能元素,他们各自玩橙子姐功能,然后通过http相互请求调用,比如一个电商项目,差缓存,连接数据库,浏览网页,结账,支付等服务都是一个个独立的功能服务,都被微小化了,他们作为一个个微服务共同构建了一个庞大的系统,如果修改其中一个功能,只需要更新其中一个功能服务单元就行了,
但是这种庞大的系统架构给部署和运维带来了不少压力,但是spring也带来了构建大型分布式微服务的全套全程产品:

  • 构建一个个功能独立的微服务应用单元,可以使用springboot来进行快速构建应用
  • 大型分布式网络微服务的调用,这部分由springcloud来完成,实现分布式
  • 在分布式中间,进行流式数据计算,壁橱里,我们可以用spring could data flow
    在这里插入图片描述

第一个springboot程序

首先环境:
jdk 1.8
maven 3.6.1
springboot 最新版
idea
官方网站可以直接快捷创建一个项目:
在这里插入图片描述
idea中也可以创建,但本质上还是利用官网
idea创建springboot大坑注意:
创建一个初始项目时会,默认给你一个新的maven仓库然后在里面下载插件,由于没有阿里云镜像加速,会在加载pom.xml文件时卡住甚至卡死,
解决办法是找到已经默认下载的maven仓库,给它删了

C:\Users\20458\.m2\wrapper

然后在idea中设置原本的maven地址,包括setting.xml配置文件
在这里插入图片描述
即便有阿里云镜像加速,可能依然需要下载一定时间的配置文件,
标准项目结构如下:
controller,dao,pojo,service都是二次创建的
创建包也必须在XXXApplication.java同级目录下,否则无法读取
(约定大于配置)
在这里插入图片描述
而这个Application.java为程序的主入口,重申:二次创建包必须在Application.java同目录下
运行Application.java时
出现控制台打印以下代表成功:
在这里插入图片描述
之后就可以直接访问设置的8080端口了,甚至直接写一个Controller然后@RequestMapping("/XXX")都能直接访问,注意,到现在我们没有写任何xml配置文件

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

//震惊了,这特娘什么都没有配置就能直接访问,当时ssm光一个依赖配置都能拉半天
@RestController
//@RestController用来返回字符串
public class HelloController {
    @RequestMapping("/hello")
    public String hello(){
        //调用业务,接收前端参数
        return "hello,World";
    }
}

在这里插入图片描述
并且通过点击主入口的注释@SpringBootApplication/@SpringBootConfiguration/@Configuration就会发现到最后本质上还是一个spring的@Component组件
springBoot项目pom.xml字段解释:
如下为依赖文件:

<?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>
<!--    parent:有一个父项目-->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.0</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
<!--        web依赖:集成tomcat,dispatcherServlet,xml-->

<!--        web服务启动器,如果没有导入web依赖,在pom文件中直接插入此标签就可以与web项目一样-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

<!--        spring-boot-starter所有的springBoot依赖都是用这段开头的-->

<!--        单元测试-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

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

</project>

可以发现pom.xml主要有四个组成部分:

  • 项目元数据信息:创建时候输入的Project Metadata部分,也就是Maven项目的基本元素,包括:groupld、
    artifactld、version、name、 description等
  • parent:继承s pring - boot -starter-parent的依赖管理,控制版本与打包等内容
  • dependencies:项目具体依赖,这里包含了s
    pring-boot-starter-web用于实现HTTP接口(该依赖中包含了Spring MVC),官网对它的描述是:使用Spring
    MVC构建Web(包括RESTful)应用程序的入门者,使用Tomcat作为默认嵌入式容器。;
    spring-boot-starter-test用于编写单元测试的依赖包。更多功能模块的使用我们将在后面逐步展开。
  • build:构建配置部分。默认使用了spring-boot-maven-plugin,配合spring-boot-starter-parent就可以把SpringBoot应用打包成JAR来直接运行。
    PS:关于项目打包只需要在右侧maven菜单栏中点击package就可以自动打包为一个可执行的jar文件,打包后会以缓存文件的形式出现在target栏中
    在这里插入图片描述
    这样就是打包成功了,打开方式老秦用的windows PowerShell,我不知道是什么玩意,明天再弄吧,已经两点了
    注意:
    新建项目老是使用默认maven仓库地址,可在如下中进行设置
    在这里插入图片描述
    注:修改新建项目信息还是不行,网上搜说是idea2020.2.2版本的bug
    可以创建之后卡住的时候结束进程,然后找到地址删除.mvn文件夹,再打开试试

扩展小知识:

快捷改端口号:
springboot改端口号只需要在配置文件中添加下列一行代码后重启即可
在这里插入图片描述
springboot修改彩蛋显示(banner,也就是运行时显示的spring立体字):
首先在网上搜索springboot banner在线生成,找到喜欢的内容然后复制,
再在springboot项目中的resource目录下创建一个banner.txt文件,之后就会被自动识别,并更换了:
在这里插入图片描述

springboot自动装配原理:

(加薪就靠用这个吹b了)
自动装配:
pom.xml

核心版本依赖:

  • spring-boot-dependencies: 核心依赖在父工程中

首先打开pom.xml,连续点击parent标签中的artifactId进入父级目录文件
也就是点击spring-boot-starter-parent,进入后再点击spring-boot-dependencies可以发现有大量的jar包版本
在写或者引入一些springboot依赖的时候,不需要指定版本,就是因为有这些版本仓库

启动器:

可自定义

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

说白了,就是springboot的启动场景;
比如创建了一个spring-boot-starter-web,他就会帮我们自动导入web环境所有的依赖
springboot会将所有的功能场景都变成一个个的启动器,
如果我们需要使用什么功能,就只需要找到对应的启动器starter

主程序:


import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
//@SpringBootApplication  :标注这个类是springboot的一个应用
public class DemoApplication {

    public static void main(String[] args) {
        //将springboot应用启动
        SpringApplication.run(DemoApplication.class, args);
    }

}

CSDN某博主版讲解:

springboot关于自动装配的源码在spring-boot-autoconfigure-2.4.0.jar中:
在这里插入图片描述
首先在主程序上就可以看出自动装配肯定和注解有关系

工作原理剖析:

@EnableAutoConfiguration

在这里插入图片描述
@SpringBootApplication是一个复合注解,或者说是派生注解,在@SpringBootApplication中有一个注解1@EnableAutoConfiguration,翻译成人话就是开启自动配置,点开之后其源码定义如下:
在这里插入图片描述
而这个注解也是一个派生注解,其中的关键功能由@Import提供,其导入的AutoConfigurationImportSelector的selectImports()方法通过SpringFactoriesLoader.loadFactoryNames()扫描所有具有META-INF/spring.factories的jar包

在这里插入图片描述

spring-boot-autoconfigure-x.x.x.x.jar里就有一个这样的spring.factories文件。
这个spring.factories文件也是一组一组的key=value的形式,只不过其中一个key是EnableAutoConfiguration类的全类名,而它的value是一系列xxxxAutoConfiguration的类名的列表,这些类名以逗号分隔:
在这里插入图片描述
这个@EnableAutoConfiguration注解通过@SpringBootApplication被间接的标记在了Spring Boot的启动类上。在SpringApplication.run(…)的内部就会执行selectImports()方法,找到所有JavaConfig自动配置类的全限定名对应的class,然后将所有自动配置类加载到Spring容器中

个人整合

老秦说selectImports()组件选择器选择的是pom.xml中配置信息
然后并且引入了同类另外的一个方法AutoConfigurationEntry(),引入的方法中发现有一个集合:

List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);

在这里插入图片描述
这个集合本身获取了所有的配置,如何获取的呢?
点开getCandidateConfigurations方法:

	protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
		List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
				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;
	}

从这里开始终于特娘的连接上了csdn博主说的SpringFactoriesLoader.loadFactoryNames()
在这里插入图片描述
同时注意的是此方法导入了两个参数:
getSpringFactoriesLoaderFactoryClass(),
getBeanClassLoader()
点开第一个方法参数,可以看到该方法返回了EnableAutoConfiguration注解的类,也就可以获得类中的所有配置说白了就是将用到@EnableAutoConfiguration注解的类当做参数了
在这里插入图片描述
然后点开SpringFactoriesLoader的loadFactoryNames()方法
发现结尾返回了
loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
再点开loadSpringFactories()方法,发现利用了一个常量获取了项目资源配置
在这里插入图片描述
而这个常量在一开始就已经被声明了:
在这里插入图片描述
也就是:
在这里插入图片描述

那么接下来就是从这些资源中遍历所有的urls.nextElement();
遍历完成之后,封装为一个Properties最后供我们使用

加载过程概述

  1. @SpringBootApplication
  2. @EnableAutoConfiguration自动导入包
  3. @Import(AutoConfigurationImportSelector.class),点开AutoConfigurationImportSelector
  4. AutoConfigurationImportSelector类中selectImports()声明了AutoConfigurationEntry()对象
  5. AutoConfigurationEntry()类本身又声明了一个集合:
    List< String> configurations =
    getCandidateConfigurations(annotationMetadata, attributes);
  6. 点开集合的组合参数方法getCandidateConfigurations()发现此方法又有一个集合 List< String>configurations =
    SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),getBeanClassLoader());
  7. 再点开loadFactoryNames()方法,就会发现所有资源都被加载到配置类中

在这里插入图片描述
那么加载了什么资源呢?点开classLoader.getResources(FACTORIES_RESOURCE_LOCATION);中的FACTORIES_RESOURCE_LOCATION,
就会发现类中定义的一句核心代码:

public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

一切真相大白,每一个maven包下已经定义好的spring.factories资源文件封装的资源被一步一步整合到了一句@SpringBootApplication中,在run方法启动时直接全部自动装配进去了

注意

springboot所有的自动配置都在启动类中被扫描并加载:spring.factories所有的自动配置类都在这里面,但是不一定生效,要判断条件是否成立,只要导入对应的start,就有对应的启动器,有了启动器,我们的自动装配就会生效,然后就配置成功

  • springboot在启动的时候,从类路径下/META-INF/spring.factories获取指定的值
  • 将这些自动配置的类导入容器,自动配置就会生效,帮助进行自动装配
  • 整合javaEE,解决方案和自动配置的东西都在springboot-autoconfigure-2.2.0.RELEASE.jar这个jar包
  • 这个包会把所有需要导入的组件,以类名的方式返回,这些组件就会被添加到容器
  • 容器中也会存在非常多的XXXautoConfiguration的文件(@Bean),就是这些类给容器中导入了这个场景需要的所有组件,并自动配置,@Configuration,javaConfig

主启动类运行原理:

主启动类可以看到main方法(注意,此处的main方法代表开启了一个服务)只有一行代码:

SpringApplication.run(DemoApplication.class, args);

该方法主要分两部分,一部分是SpringApplication的实例化,二是run方法的执行
SpringApplication类主要做了下面四件事:

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

面试点:

关于springboot谈谈你的理解:
应该注意自动装配和run()方法方面
而run方法回答时注意以下几点:

首先springApplication判断应用的类型是普通项目还是web项目,推断当前设置main方法的定义类,并找到运行的主类
然后run方法中有一些全局存在的监听器,监听器用来获取上下文并处理一些bean,

yaml语法讲解

为什么要使用yaml语法?
首先springboot使用的是一个全局配置文件,配置文件名称是固定的同时在配置文件中也有说明properties文件和yaml文件都是可以使用的,而官方不推荐使用properties文件,所以就有必要了解了.
并且yaml可以直接给实体类赋值
application.properties语法结构为:

  • key=value

application.yaml语法结构为:

  • key: (空格) value

yaml语法要注意的点不多,如下:

#yaml存普通的键值对:
name: xige

#yaml还可以存properties文件不能存的对象
#yaml语法对空格的要求很严格,这里代表name为student的属性
#但是如果name行开头的空格没了,就变为两个不同的对象了
#同样的,如果name属性下面的age那行再多个空格就代表age是name的属性了
student: 
  name: xige
  age: 22
  
#行内写法:
students: {name: xige,age: 2}

#数组:
pets:
  -cat
  -dog
  -pig
  
pet: {cat,dog,pig}

给属性赋值的几种方式

普通声明赋值:

在这里插入图片描述
输出在最下面,此处略过不说

yaml赋值

yaml赋值要利用到一个特殊注解:@ConfigurationProperties(prefix = “XXX”)
注意prefix内为application.yaml内的对象名
不管yaml文件类名首字母是否大写,prefix注解首字母一定要小写
此注解作用就是将配置文件中配置的每一个属性的值,映射到这个组件中去;
告诉springboot降本类中所有属性和配置文件中相关的配置进行绑定
但是!!这个组件是容器中的组件,才能使用容器提供的@ConfigurationProperties功能,说人话就是必须和@Component搭配使用
使用properties格式的文件好像也可以使用成功,但是这里不做过多尝试,
往后mybatis或者其他地方需要使用到配置文件的都可以使用yaml来进行复制
如下图:(红色警告不用管也可以运行)
在这里插入图片描述
同时,利用yaml文件赋值还可以直接使用一些简易的语法进行进阶操作,比如直接在配置文件中进行加密,节省到吗量之类的:
在这里插入图片描述

指定配置文件赋值

首先说明,这个方法很蠢,这里只做演示
首先自定义一个文件名,然后利用注解@PropertySource(value = “classpath:XXX.properties”)进行引入配置文件,
引入之后还要一个一个从配置文件中给属性进行定义
不过需要注意的是使用properties文件进行赋值的时候要注意在idea的Setting中设置一下编码格式,setting–>FileEncoding–>utf-8
在这里插入图片描述
属性赋值结论:
配置yml和配置properties都可以获取到值,但是推荐使用yml
如果在某个业务中只需要获取配置文件中的某个值,可以使用@Value
如果说专门编写了一个javaBean来和配置文件进行映射的时候就可以直接使用@ConfigurationProperties,这是最新也是最简便的

JSR303验证

首先讲一下一些拓展术语:
松散绑定:
松散绑定就是比如yaml中定义的属性为last-name,那么这个和lastName是一样的,后面跟着的字母默认是代谢的,这就是松散绑定(感觉没用)
JSR303校验就是用来规范输入内容的,
但是JSR303校验不可以与@Value搭配使用,因为@Value会跳过校验进行赋值
比如说要输入邮箱,就必须按照邮箱的数据格式以.com结尾
如下:
首先导入一个springboot的启动器,不然无法在属性上使用@Email注解

<dependency>
            <groupId>org.hibernate.validator</groupId>
            <artifactId>hibernate-validator</artifactId>
            <version>6.0.17.Final</version>
            <scope>compile</scope>
        </dependency>

导入依赖之后记得刷新一下maven,不然无法检测
之后对其进行规范格式之外的赋值时就会报错:
在这里插入图片描述
这只是一种最简单的使用方法除了@Email拓展的还有:
在这里插入图片描述
还有:
在这里插入图片描述

多环境装配以及配置文件位置

配置文件读取位置

首先根据官网说明环境配置文件一共有四种路径可供直接读取:
在这里插入图片描述
说人话就分别是
项目根目录新建的config文件夹下的application.yaml
直接在项目根目录下的application.yaml
src/main/java源码文件夹或者resource文件夹下新建config文件夹下的application.yaml
再有就是resource文件夹下直接新建application.yaml了
优先级就是从上往下数的,相同目录优先使用config文件夹下的配置文件

多环境装配:

首先实际开发的时候一般来说需要配置多套环境的,比如开发一个环境,测试一个环境,运行一个环境等等,而springboot已经帮我们想好了
可以通过application-XXX.properties的形式来建立配置文件:
在这里插入图片描述
如图所示,上述文件都会被自动转化为配置文件,不过默认使用application.properties,使用其他环境的时候需要在application.properties中专门进行声明:

#springboot的多环境配置,可以选择激活哪一个配置环境
spring.profiles.active=dev

后面的dev就是-xxx的后缀,如果使用测试环境就是:
在这里插入图片描述
并且如果使用yaml格式的配置文件的话,可以实现多文档模块,用人话来说就是单个文件,可以读取不同配置环境

server:
  port: 8088
spring:
  profiles:
    active: dev

---
server:
  port: 8081
spring:
  profiles: dev

---
server:
  port: 8082
spring:
  profiles: test

在这里插入图片描述

自动配置原理再理解

看过了自动装配的原理流程,又学过了application配置文件的作用,两者之间其实也是有联系的,
首先所有可以在application配置文件中可以生米那个修改属配置类中已经声明过了,
如下为一个在spring.factories文件中的一个HttpEncodingAutoConfiguration(翻译为http编码自动组态)配置,
这个配置可以看到开头的注解就导入了一个properties类在这里插入图片描述
再次点开这个类就会发现这个类又导入了一个server开头的文件,并且这个类自身定义的属性就是application.yaml可以配置的属性,或者说application.yaml能配置的属性必须是在配置类中已经声明的
在这里插入图片描述
甚至直接点application.yaml配置文件中定义的属性都能直接跳转到对应的类:
在这里插入图片描述

总结:

在我们配置文件中能配置的属性,都存在一个固定的规律一定会有spring.factories文件声明的XXXautoConfiguration组件进行自动装配,
在这里插入图片描述
而自动装配又有自带的默认值,而这些默认值都是通过一个XXXproperties的文件进行修改,
在这里插入图片描述

而这个XXXproperties又和application.yaml进行绑定,所以就可以使用自定义的配置了

老秦の精髓:
  1. springboot启动会加载大量的自动配置类
  2. 我们看需要的功能有没有在springboot默认写好的自动配置类中存在
  3. 再看这根自动配置类中到底配置了哪些组件(只要问要用的组件存在其中,我们就不需要在进行手动配置了)
  4. 给容器中自动配置类添加组件的时候,会从properties类中获取某些属性,我们只需要在配置文件中指定这些属性的值就行了

XXXAutoConfiguration: 自动配置类,给容器中添加组件
XXXProperties: 封装配置文件中相关的属性

拓展一下:
ConditionalOn是spring的底层注解: 根据不同的条件来判断当前配置或者类是否生效
在这里插入图片描述
另外当在application.yaml配置debug: true时就可以在运行时查看有哪些自动配置类是效,哪些没有生效:
在这里插入图片描述
在这里插入图片描述
包括以后遇到二手项目的未知配置信息也可以点进去查看配置信息,然后在spring.factories中CTRL+F搜索一下就可以找到具体的配置组件了

WEB开发探究:

springboot的自动装配,说到底都是MATA-INF下面的spring.factiories文件中XXXXAutoConfiguration向容器中自动配置组件
XXXXProperties自动配置类,装配配置文件中自定义的一些内容,而这些组件的配置信息也可以通过绑定的application.yaml进行修改,但是能修改的都是组件中已经声明过的信息
那么springboot的web项目要解决的问题就有:

  • 导入静态资源
  • 首页
  • springboot没有能写jsp的地方,所以就需要使用模板引擎(Thymeleaf)
  • 装配扩展springmvc
  • 增删改查
  • 拦截器
  • 国际化(中英文切换)

静态资源导入探究

一般新建的springboot项目中没有静态资源目录,那么静态资源怎么导入呢?
可以通过源码的三个if得出结论:

获取静态资源源码

首先双击shift搜索WebMvcAutoConfiguration可以发现有个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();
	if (!registry.hasMappingForPattern("/webjars/**")) {
		customizeResourceHandlerRegistration(registry.addResourceHandler("/webjars/**")
				.addResourceLocations("classpath:/META-INF/resources/webjars/")
				.setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl)
				.setUseLastModified(this.resourceProperties.getCache().isUseLastModified()));
	}
	String staticPathPattern = this.mvcProperties.getStaticPathPattern();
	if (!registry.hasMappingForPattern(staticPathPattern)) {
		customizeResourceHandlerRegistration(registry.addResourceHandler(staticPathPattern)
				.addResourceLocations(getResourceLocations(this.resourceProperties.getStaticLocations()))
				.setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl)
				.setUseLastModified(this.resourceProperties.getCache().isUseLastModified()));
	}
}
第一个if:

首先判断是否在application中声明过静态资源路径,如果已经有声明,则往后的几个静态资源路径直接无效化

第二个if:

判断中看到资源可以在一个叫webjars的目录中导入的
什么是webjars?
webjars其实就是另一种maven,都是用来导入资源的
百度webjars官网往下拉就可以发现像maven一样的资源导入标签,使用方法也与maven一样,导入到pom.xml中就行了
(图片中网络加载太慢导致maven的信息没有及时刷新出来)
在这里插入图片描述
导入之后刷新一下就可以在lib目录下找到相关的文件了:
在这里插入图片描述
运行时http://localhost:8080/webjars/jquery/3.4.1/jquery.js也可以搜素到相关的代码,但是一般来说不会使用这种方法

第三个if:

判断的方法就可以看出可以直接识别静态资源的路径,点开getStaticPathPattern(),会发现该方法用this指定了一个private String staticPathPattern = “/**”;也就是说只要是在当前目录的都可以识别
点开getStaticLocations()会发现这个方法是用this绑定的staticLocations,而staticLocations的资源监测路径已经被定义好了:

public static class Resources {

		private static final String[] CLASSPATH_RESOURCE_LOCATIONS = { "classpath:/META-INF/resources/",
				"classpath:/resources/", "classpath:/static/", "classpath:/public/" };

		/**
		 * Locations of static resources. Defaults to classpath:[/META-INF/resources/,
		 * /resources/, /static/, /public/].
		 */
		private String[] staticLocations = CLASSPATH_RESOURCE_LOCATIONS;

分别是:
classpath:/META-INF/resources/
classpath:/resources/
classpath:/static/
classpath:/public/
经测试,在以上四个目录下的页面可以直接以http://localhost:8080/hello.js的方式获取,并且相同的页面四个路径的优先级为:
resources>static>public

首页和图标定制

首页设置:

首先依然是WebMvcAutoConfiguration类,然后搜索index就可以发现如下方法:
在这里插入图片描述
单纯从index.html就可以看出定义为index.html的就会被判定为首页,
所以,按照通常来讲,一般把首页定义在resources目录下的public,static,sources文件夹都可以
但是正常来说,一般我们都会通过Controller来跳到首页中,
按照这么来说的话就需要将首页放到template目录中,在templates目录下的所有页面,只能通过Controller来进行跳转,相当于原来的WEB-INF目录,
并且要想在Conroller中跳转到index页面,还需要thymeleaf模板引擎依赖.

图标定制:

图标定制其实就是是修改网页上的title标签左侧的小图标
图标定制功能可以在springboot的低版本中找到相关的方法,高版本没有找到,但是依然可以使用,
老版本需要首先在application.yaml中设置默认图标失效:

#设置标签图标首先要关闭默认的图标
spring:
  mvc:
    favicon:
      enabled: false

然后在resource目录下粘贴一个图片,并改名为:favicon.ico
新版本只需要粘贴并改名就可以了:
在这里插入图片描述
效果如下:
在这里插入图片描述

thymeleaf模板引擎:

springboot中不建议使用jsp,而是使用更加成熟的模板引擎,这里老秦推荐使用thymeleaf模板引擎,地址:
1、Thymeleaf官网:
https://www.thymeleaf.org/
2、Thymeleaf在Github 的主页: https://github.com/thymeleaf/thymeleaf
3、Spring官方文档https://docs.spring.io/springboot/docs/2.1.6.RELEASE/reference/htmlsingle/#using-boot-starter
PS:这里我springboot2.4.1版本pom依赖导入总是变红,搞得我又重新建立了一个模板,直接添加了thymeleaf依赖,

如果需要使用thymeleaf,只需要导入对应的starte就行了

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

需要注意的是往后项目的静态网页都必须放在template目录下
thymeleaf的简单测试:
建立一个简单的controller,写一个html,访问没问题
但是controller中model.addAttribute(“msg”,“hello,thymeleaf”);传递一个值后是取不出来的
在这里插入图片描述
需要使用thymeleaf的专用语法,th:XXX,与vue的v-bind含义一样
并且html文件头需要导入一下
< html lang=“en” xmlns:th=“http://www.thymeleaf.org”>

<h1>hello</h1>
<!--所有的html元素都可以被thymeleaf替换接管,语法格式为th: 元素名-->
<div th:text="${msg}"></div>

thymeleaf语法

thymeleaf常用th属性解读
html有的属性,Thymeleaf基本都有,而常用的属性大概有七八个。其中th属性执行的优先级从1~8,数字越低优先级越高。

  • th:text :

设置当前元素的文本内容,相同功能的还有th:utext,两者的区别在于前者不会转义html标签,后者会。优先级不高:order=7

  • th:value:

设置当前元素的value值,类似修改指定属性的还有th:src,th:href。优先级不高:order=6

  • th:each:

遍历循环元素,和th:text或th:value一起使用。注意该属性修饰的标签位置,详细往后看。优先级很高:order=2

  • th:if:

条件判断,类似的还有th:unless,th:switch,th:case。优先级较高:order=3

  • th:insert:

代码块引入,类似的还有th:replace,th:include,三者的区别较大,若使用不恰当会破坏html结构,常用于公共代码块提取的场景。优先级最高:order=1

  • th:fragment:

定义代码块,方便被th:insert引用。优先级最低:order=8

  • th:object:

声明变量,一般和*{}一起配合使用,达到偷懒的效果。优先级一般:order=4

  • th:attr:

修改任意属性,实际开发中用的较少,因为有丰富的其他th属性帮忙,类似的还有th:attrappend,th:attrprepend。优先级一般:order=5

标准表达式语法
  1. 简单表达式 simple expressions
    ${…} 变量表达式 Variable Expressions
    *{…} 选择变量表达式 Selection Variable Expressions
    #{…} 消息表达式 Message Expressions
    @{…} 链接url表达式 Link URL Expressions
    ~{…} 代码块表达式 Fragment Expressions
    常用th属性使用
Thymeleaf属性的使用注意点:

一、若要使用Thymeleaf语法,首先要声明名称空间: xmlns:th=“http://www.thymeleaf.org”
二、设置文本内容 th:text,设置input的值 th:value,循环输出 th:each,条件判断 th:if,插入代码块 th:insert,定义代码块 th:fragment,声明变量 th:object
三、th:each 的用法需要格外注意,打个比方:如果你要循环一个div中的p标签,则th:each属性必须放在p标签上。若你将th:each属性放在div上,则循环的是将整个div。
四、变量表达式中提供了很多的内置方法,该内置方法是用#开头,不要与#{}消息表达式弄混。
五、th:insert,th:replace,th:include 三种插入代码块的效果相似,但区别很大。

拓展注意点

字面量
‘one text’,‘another text’…文本
0,1,3.0,12.45… 数值
true false 布尔类型
null 空
one,sometext,main 文本字符

文本操作

  • 字符串连接
    |The name is ${name}| 字符串连接

算数运算
,+ - * / % 二元运算符

  • 负号 一元运算符

布尔操作
and or 二元操作符
! not 非 一元操作符

关系操作符
< > >= <= gt lt ge le
== != eq ne

条件判断,不要在前端页面使用if…else.推荐使用三元运算符
(if) ? (then) if-then
(if) ? (then):(else) if-then-else

测试:
//在template目录下的所有页面只能通过Controller来进行跳转
//这个需要模板引擎的支持,thymeleaf
@Controller
public class InedxController {

    @RequestMapping("/test")
    public String test(Model model){
        model.addAttribute("msg","<h1>hello,thymeleaf</h1>");
        //Arrays.asList(),直接将一系列字符串转为集合
        model.addAttribute("users", Arrays.asList("xige","jianshen","xiaoguoguo"));
        return "test";
    }
}

前端页面:

<h1>hello</h1>
<div th:text="${msg}"></div>
<!--utext代表不被转义-->
<div th:utext="${msg}"></div>
<hr>
<!--这里意思为先将users对象foreach,然后再将每个对象都转为user进行展示输出-->
<h3 th:each="user:${users}" th:text="${user}"></h3>
<!--使用下面这句效果一样,但是更建议使用上面这种-->
<h3 th:each="user:${users}">[[ ${user} ]]</h3>

效果展示:
在这里插入图片描述

MVC自动装配(重点)

首先查看官方文档:

https://docs.spring.io/spring-boot/docs/2.1.6.RELEASE/reference/html/boot-features-developing-web-applications.html

从官方文档中可以看出:

If you want to keep Spring Boot MVC features and you want to add additional MVC configuration (interceptors, formatters, view controllers, and other features), you can add your own @Configuration class of type WebMvcConfigurer but without @EnableWebMvc

翻译过来就是:

如果您想保留Spring Boot MVC功能并且想要添加其他MVC配置(拦截器,格式化程序,视图控制器和其他功能),则可以添加自己的类型为WebMvcConfigurer的@Configuration类,但不添加@EnableWebMvc

PS:一旦标注@EnableWebMvc,mvc就会全面接管,你的自定义配置也就不会生效了
也就是说需要使用@Configuration注解,并且类型必须为WebMvcConfigurer
搜索WebMvcConfigurer发现这是一个接口,那么实现它就好了

PS:其中原理老秦在源码打断点讲的我有点蒙,听不懂,老秦也说不怎么用,暂时挖个坑
代码如下:

//全面扩展 springmvc
//如果想定制一些功能,只需要写这个组件,然后把它交给springboot,springboot就会帮我们自动装配
@Configuration //将这个类变为一个配置类
public class MyMvcConfig  implements WebMvcConfigurer {
    //实现了视图解析器接口的类,我们就可以把他看做视图解析器

    //@Bean就可以放在Bean里面
    @Bean
    public ViewResolver myViewResolver(){
        return new MyViewResolver();
    }

    //自定义一个视图解析器 MyViewResolver
    public static class MyViewResolver implements ViewResolver{
        @Override
        public View resolveViewName(String viewName, Locale locale) throws Exception {
            return null;
        }
    }
}

扩展springMVC

##自定义的配置日期格式化
spring:
  mvc:
    format:
      date-time: yyyy-MM-dd HH:mm:ss
    //视图跳转
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        //设置指定视图名(/xige)控制其跳转到另外一个特定视图(test)
        registry.addViewController("/xige").setViewName("test");
    }

在springboot中有很多的XXX Configuration,会帮助我们进行扩展配置,只要看到了这个东西,我们就要注意了,因为它改变或者扩展了springboot原有的一些东西,

员工管理系统

要建立系统,这里首先要使用bootstrap模板,官网地址:
https://v3.bootcss.com/getting-started/
在这里插入图片描述

这里没弄好,不会用bootstrap的源码模板,直接找到了群里面的web素材

直接新建两个pojo实体类,员工表和部门表然后私有化属性
部门表只有id和name
员工表五个基本数据和一个日期,全部使用lombok,日期直接在有参构造中new date了

javaweb注解:
Service 业务类专用
Repository dao实现类专用
Controller web层专用
Component 通用

然后是两个表的dao实现类创建方法(当做执行sql),(一般整合mybatis才会叫mapper,我这边直接用了)

//部门dao
@Repository
public class DepartmentMapper {

    //模拟数据库中的数据
    private static Map<Integer, Department> departments = null;
    static{
        departments=new HashMap<Integer,Department>(); //创建一个部门表

        departments.put(101,new Department(101,"生产部"));
        departments.put(102,new Department(102,"技术部"));
        departments.put(103,new Department(103,"销售部"));
        departments.put(104,new Department(104,"财务部"));
        departments.put(105,new Department(105,"市场部"));

    }

    //获得所有部门信息
    public Collection<Department> getDeparment(){
        return departments.values();
    }

    //通过id得到部门
    public Department getDeartmentById(Integer id){
        return departments.get(id);
    }

}

员工dao:

//员工dao
@Repository  //spring托管dao层实现类专用
public class EmployeeMapper {

    //模拟数据库中的数据
    private static Map<Integer, Employee> employees = null;
    //员工有所属的部门
    @Autowired
    private DepartmentMapper departmentMapper;
    static{
        employees=new HashMap<Integer,Employee>(); //创建一个部门表

        employees.put(1001,new Employee(1001,"AA","Q2045807586@qq.com",1,new Department(101,"生产部")));
        employees.put(1002,new Employee(1002,"BB","W2045807586@qq.com",0,new Department(102,"技术部")));
        employees.put(1003,new Employee(1003,"CC","E2045807586@qq.com",0,new Department(103,"销售部")));
        employees.put(1004,new Employee(1004,"DD","R2045807586@qq.com",1,new Department(104,"财务部")));
        employees.put(1005,new Employee(1005,"EE","A2045807586@qq.com",1,new Department(105,"市场部")));

    }
    //主键自增
    private static Integer initid=1006;
    // 增加一个员工
    public void save(Employee employee){
        if (employee.getId()==null){
            employee.setId(initid++);
        }
        //这里要先看括号里面的,总体是利用传过来的员工对象得到部门,再得到部门的id,然后再利用部门的id得到部门,然后再用这个部门给传递过来的员工对象对的部门赋值,
        //哈哈哈哈哈.原地TP,不知道老秦这代码具体的含义是什么 
        employee.setDepartment(departmentMapper.getDeartmentById(employee.getDepartment().getId()));
        employees.put(employee.getId(),employee);
    }

    //查询全部员工信息
    public Collection<Employee> getAll(){
        return employees.values();
    }
    //通过id查询员工
    public Employee getEmployeeById(Integer id){
        return employees.get(id);
    }
    //根据id删除员工,id为主键,主键一没有,信息也就没了
    public void delete(Integer id){
        employees.remove(id);
    }

}

首页实现:

所有页面的静态资源都需要使用shymeleaf接管
首先看一下config包下的配置类,config配置类可以有多个,不同配置类对应不同的配置信息
配置类也可以配置首页,这样就不用在controller中再配置了:

@Configuration
public class MyMvcConfig implements WebMvcConfigurer {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("index");
    }
}

我这里直接在官网模板F12后CV的,有些样式没有按照标准来,这里统一改一下:
首先html标签头需要修改为以下内容:
注意不要搞成w3school了

<html lang="en"  xmlns:th="http://www.thymeleaf.org">

然后还需要按照thymeleaf的模板规范来写,比如:
超链接标签的href需要改为th:href,并且标签内的超链接需要包含在@{}中,
需要注意的是,括号中是以static包下开始的,并且不知为何,静态资源包在static包下的时候不能被其他包包括,不然就报错
比如bootstrap的css核心文件链接:

<!-- Bootstrap core CSS -->
    <link th:href="@{/css/bootstrap-theme.min.css}" rel="stylesheet">

并且使用@{}后,如果在application配置文件中修改servlet的映射目录,页面的样式也会自动匹配,依然有效

#关闭模板引擎的缓存
spring:
  thymeleaf:
    cache: false

#更改映射目录
server:
  servlet:
    context-path: /xige

在这里插入图片描述

页面的国际化

国际化其实就是修改页面显示哪国语言
页面国际化的步骤有:
配置i18n文件
如果需要在项目中进行按钮自动切换,我们需要自定义一个组件LocaleResolver
记得将自己写的组件配置到spring容器中,@Bean
首页传参使用#{}

首先需要设置编码格式:
在这里插入图片描述
保证设置正确之后,在resources目录下新建一个叫i18n的包,(PS:i18n其实就是国际化internationalization的简读,18代表i和n之间有18个字母,与k8s一样),然后在包中创建login.properties和login_zh_CN.properties文件,就会发现两个文件夹被自动合并成为一个了,并且右击还可以再直接创建资源包:
在这里插入图片描述
在这里插入图片描述
按图添加词条属性之后,文本中也都会全部添加进内容
然后在application配置文件中写好声明:
在这里插入图片描述
再然后在页面中需要使用国际化的标签中写好thymeleaf的国际化语法
th:text="#{login.tip}

<h1 class="h3 mb-3 font-weight-normal" th:text="#{login.tip}">Please sign in</h1>

利用此方法将所有的固定词改为国际化动态语句,到最后就可以进行显示了
但是还需要再修改一下,要点下面的中英文切换,进行自动转化才行
那么就需要点击后有不同的请求才行:
在这里插入图片描述
然后就是写一个方法接收请求并注册Bean到spring容器中了

//国际化解析器必须实现LocaleResolver接口
public class MyLocaleResolver implements LocaleResolver {

    //解析请求
    @Override
    public Locale resolveLocale(HttpServletRequest request) {

        //获取请求中的语言参数
        String language = request.getParameter("l");
        Locale locale = Locale.getDefault();//如果没有就使用默认语言
        //如果请求的连接携带了国际化参数
        if (!StringUtils.isEmpty(language)){
            //将传递过来的参数分割成国家和地区
            String[] split = language.split("_");
            locale = new Locale(split[0], split[1]);
        }
        return locale;
    }

    @Override
    public void setLocale(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) {

    }
}

接下来就是在MyMvcConfig类中注册bean:

    //自定义的国际化组件就生效了
    @Bean
    public LocaleResolver localeResolver(){
        return new MyLocaleResolver();
    }

这样一来就完成了,虽然我不知道注册bean之后,写的i18n.login文件会以怎样的形式被执行,当然对于这种轮子我们只需要会用就可以了,吃饭没必要从种地开始

登录功能实现

这里登录因为没有数据库所以简单化,

@Controller
public class LoginController {
    @RequestMapping("/user/login")
    public String login(@RequestParam("username") String username,
                        @RequestParam("password") String password,
                        Model model){
        //具体的业务,登录成功重定向到主体页面
        if(!StringUtils.isEmpty(username) && "666666".equals(password)){
            return "redirect:/main";
        }
        //登录失败
        model.addAttribute("msg","密码是666666");
        return "index";
    }
}

首页显示model模型返回的消息

<!--			如果msg的消息为空,则不显示,这里的三元运算符其实没什么用-->
			<p th:text="${msg}" style="color: red" th:if="${not #strings.isEmpty(msg)}"></p>

登录拦截器

跟之前ssm的超市管理系统一样,利用的HandlerInterceptor接口进行拦截,
首先新建一个拦截器类,然后实现拦截器接口,利用请求登录成功之后会有用户session这一条件进行拦截,当然,之前的登录成功请求还没有携带session参数,这些都是需要再额外手动添加上的:(这里还可以利用这条用户名信息,显示在用户登录后的页面上进行个性化显示)
在这里插入图片描述

在这里插入图片描述

设置这里的拦截器条件也是十分简陋直接,有了用户session就过,没有就不过
注意:返回true就是放行,反之则拦截

//登录拦截器
//只要实现了接口的方法就是一个拦截器,当然也需要实现对应的方法
//此方法返回true就是放行,否则就拦截
public class LoginHandInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response,
                             Object handler) throws Exception {

        //登录成功之后应该有用户的session,就按照这个来进行指定拦截
        Object loginUser = request.getSession().getAttribute("loginUser");
        if (loginUser==null){
            request.setAttribute("msg","非法登录");
            request.getRequestDispatcher("/index").forward(request,response);
            return false;
        }
        return true;
    }
}

然后就是在MvcConfig包下新建拦截器,设置拦截器拦截范围,然后再额外设置一下自动跳过拦截器的页面,如下就是拦截器类的的代码:

    //添加拦截器配置
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //注册拦截器
        registry.addInterceptor(new LoginHandInterceptor()).
                //设定拦截范围为全部
                addPathPatterns("/**").
                //设定自动跳过拦截的页面,这里添加了首页的两个跳转链接,登录本身的链接以及静态资源的提取
                excludePathPatterns("/index","/","/user/login","/css/*","/js/**","/img/**");
    }

展示员工列表

写一个员工列表的Controller:
组合Mapper层,并利用model传递mapper层方法的结果并返回页面展示

@Controller
public class EmployeeController {

    @Autowired
    EmployeeMapper employeeMapper;
    @RequestMapping("/emps")
    public String list(Model model){

        Collection<Employee> emps = employeeMapper.getAll();
        model.addAttribute("emps",emps);
        return "emp/list";
    }
}

当然,前端页面也需要进行接收:
在这里插入图片描述

分段复用:(侧边栏及顶部栏)

只需要在页面侧边栏标签中插入下列代码(sidebar为分段 自定义的名称)

th:fragment="sidebar"

然后在其他要用到侧边栏的地方插入一句:
(dashboard为带有fragment的侧边栏,sidebar为引用分段的名称)

<div th:insert="~{dashboard::sidebar}"></div>

或者使用replace(替换)

<div th:replace="~{dashboard::sidebar}"></div>

如果使用include,则会丢失样式

<div th:include="~{dashboard::sidebar}"></div>

就可以实现代码的复用,就像vue一样,相当于变为了一个组件,然后其他用的地方再插入就可以了
这里最后甚至专门写了一个commons.html页面来存放所有页面的公共部分

侧边栏选项高亮:

其实就是选中侧边栏后显示内容时,侧边栏的选项高亮
效果样式为:
在这里插入图片描述
原理也很简单,就是在侧边栏复用的情况下,
例如员工管理页面,员工管理页面复用侧边栏时设置使其传递一个参数
方法就是括号,然后设置传递的值(active=‘list’)
active只是自定义的属性名称

<div th:replace="~{commons/commons::sidebar(active='list')}"></div>

然后commons的侧边栏公共组件搞一个三元运算符,
如果有这个参数就执行带有高亮的样式,如果是别的页面选用时自然不会有指定参数传递过来,也就不会显示高亮了
PS:三元运算符就是th:class标签中的
${active==‘list’?‘nav-link active’:‘nav-link’}

<a th:class="${active=='list'?'nav-link active':'nav-link'}" th:href="@{/emps}">
表单内容展示

那么最后终于到了将表单信息编写到页面上的时候了,
这里直接将list页面中< table>标签的< thead>表头内容修改为员工表的姓名,id之类的
同时将< tbody>标签,也就是具体的内容,设置为自动获取特定员工的id,姓名之类的,
如下:

<main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4">
	<h2>Section title</h2>
	<div class="table-responsive">
		<table class="table table-striped table-sm">
			<thead>
				<tr>
					<th>id</th>
					<th>lastName</th>
					<th>eamil</th>
					<th>sex</th>
					<th>department</th>
					<th>birth</th>
					<th>操作</th>
				</tr>
			</thead>
			<tbody>
				<tr th:each="emp:${emps}">
					<td th:text="${emp.getId()}"></td>
					<td th:text="${emp.getLastName()}"></td>
					<td th:text="${emp.getEamil()}"></td>
<!--由于男女设置的0和1代替,所以这里就直接用了三元运算符来简单判断一下-->
					<td th:text="${emp.getSex()==0?'':''}"></td>
					<td th:text="${emp.getDepartment().getName()}"></td>
<!--这里利用了一个工具类改了日期的显示样式-->
					<td th:text="${#dates.format(emp.getBirth(),'yyyy-MM-dd HH:mm:ss')}"></td>
					<td>
						<button class="chartjs-render-monitor">编辑</button>
						<button class="chartjs-render-monitor">删除</button>
					</td>
				</tr>
			</tbody>
		</table>
	</div>
</main>

效果如下,按钮具体的效果也可以选用bootstrap的,
三条属性,一个代表按钮,一个代表大小,一个代表样式
比如老秦就用的
< button class=“btn btn-sm btn-primary”>编辑< /button>
< button class=“btn btn-sm btn-danger”>删除< /button>
在这里插入图片描述

增加员工:

这里直接新建了一个添加页面,其实就是把list.html表单页面重新复制了一份改名为add.html,原有的侧边栏以及顶部不变,表单本身样式变一下,老秦是从bootstrap里面搞得,我也直接在评论区底部找到了:
这里只复制了add.html中main标签里面的内容,其他的都一样就不写了,注意要把复制过来的添加员工按钮给删了

<form th:action="@{/emp}" method="post">
	<div class="form-group">
		<label>LastName</label>
		<input type="text" name="lastName" class="form-control" placeholder="请输入姓名">
	</div>
	<div class="form-group">
		<label>Email</label>
		<input type="email" name="eamil" class="form-control" placeholder="2045807586@qq.com">
	</div>
	<div class="form-group">
		<label>Sex</label><br>
		<div class="form-check form-check-inline">
			<input class="form-check-input" type="radio" name="gender" value="1">
			<label class="form-check-label"></label>
		</div>
		<div class="form-check form-check-inline">
			<input class="form-check-input" type="radio" name="gender" value="0">
			<label class="form-check-label"></label>
		</div>
	</div>
	<div class="form-group">
		<label>department</label>
		<select class="form-control" name="department.id">
<!--	遍历部门表,并将遍历的每一个的名字都显示出来,注意,显示的是部门名字,但是提交的是部门的id-->
			<option th:each="dept:${deparment}" th:text="${dept.getName()}" th:value="${dept.getId()}"></option>
		</select>
	</div>
	<div class="form-group">
		<label>Birth</label>
		<input name="birth" type="text" class="form-control" placeholder="生日是什么时候呢">
	</div>
	<button type="submit" class="btn btn-primary">确认添加</button>
</form>

这里需要注意的点有:
所有的选项都必须有name而且与pojo的属性一一对应,

<select class="form-control" name="department.id">
<!--	遍历部门表,并将遍历的每一个的名字都显示出来,注意,显示的是部门名字,但是提交的是部门的id-->
			<option th:each="dept:${deparment}" th:text="${dept.getName()}" th:value="${dept.getId()}"></option>
		</select>

这里是将部门表全部通过名字的方式展示出来,然后提交的时候使按照value来进行提交的,
并且这里的name=“department.id是因为th:value=”${dept.getId()中value是以id来获得部门的,这部分老秦讲的很模糊,我也不是太明白,不过报错的确是说无法进行类型转换的原因
然后就是controller层的跳转控制了:

@Controller
public class EmployeeController {

    @Autowired
    EmployeeMapper employeeMapper;
    @Autowired
    DepartmentMapper departmentMapper;

    @RequestMapping("/emps")
    public String list(Model model){

        Collection<Employee> emps = employeeMapper.getAll();
        model.addAttribute("emps",emps);
        return "emp/list";
    }

    //添加员工的页面,这里用了Restfor风格,请求一样,但提交的方式不一样,一个是get,一个是post提交
    @GetMapping("/emp")
    public String toAddpage(Model model){
        //查出所有部门的信息
        Collection<Department> deparment = departmentMapper.getDeparment();
        model.addAttribute("deparment",deparment);
        return "emp/add";
    }
    
    //将填写的员工添加到list表单页面(数据库)中
    @PostMapping("/emp")
    public String addEmp(Employee employee){
        System.out.println("员工信息"+employee);
        //添加的操作
        employeeMapper.save(employee);//b调用底层业务方法,保存员工信息

        //添加成功之后返回到员工展示界面  还有转发 forword
        return "redirect:/emps";
    }
    
}

整体逻辑就是点击添加员工之后超链接get的方式提交到emp控制层,
然后控制层控制其跳转到add页面
add页面添加完内容之后post的方式提交到emp控制层,
然后控制层将内容利用底层代码的save方法保存到list表单,
再跳转到list页面进行保存内容之后的展示
效果如下:
在这里插入图片描述
这里需要注意的是这里的生日格式默认必须为2000/12/16的格式,而不能以2000-12-16的格式,否则就会报错,除非在application.yaml中进行如下设置:

spring:
  mvc:
    date-format: yyyy-MM-dd

修改员工信息:

首先要想修改员工信息,就要首先得到要修改的员工的初始数据,
并且还要有修改时的页面:
这里首先继续复制add页面并改名为update:
并且还需要将原本需要输入的地方先给展示出原本的数据:都是取值,然后展示

<form th:action="@{/updateEmp}" method="post">
	<input type="hidden" name="id" th:value="${emp.getId()}">
	<div class="form-group">
		<label>LastName</label>
		<input th:value="${emp.getLastName()}" type="text" name="lastName" class="form-control" placeholder="请输入姓名">
	</div>
	<div class="form-group">
		<label>Email</label>
		<input th:value="${emp.getEamil()}" type="email" name="eamil" class="form-control" placeholder="2045807586@qq.com">
	</div>
	<div class="form-group">
		<label>Sex</label><br>
		<div class="form-check form-check-inline">
			<input th:checked="${emp.getSex()==1}" class="form-check-input" type="radio" name="gender" value="1">
			<label class="form-check-label"></label>
		</div>
		<div class="form-check form-check-inline">
			<input th:checked="${emp.getSex()==0}" class="form-check-input" type="radio" name="gender" value="0">
			<label class="form-check-label"></label>
		</div>
	</div>
	<div class="form-group">
		<label>department</label>
		<select class="form-control" name="department.id">
<!--								遍历部门表,并将遍历的每一个的名字都显示出来,注意,显示的是部门名字,但是提交的是部门的id-->
			<option th:selected="${dept.getId()==emp.getDepartment().getId()}" th:each="dept:${deparment}"
					th:text="${dept.getName()}"
					th:value="${dept.getId()}"></option>
		</select>
	</div>
	<div class="form-group">
		<label>Birth</label>
		<input name="birth" th:value="${#dates.format(emp.getBirth(),'yyyy-MM-dd')}" type="text" class="form-control" placeholder="生日是什么时候呢">
	</div>
	<button type="submit" class="btn btn-primary">确认修改</button>
</form>

这里一定要仔细看看,不同的条目是怎么进行输出展示的
然后还需要注意的是,日期一定要跟application上的格式对应
到最后与add页面一样,到最后使用save方法,保存:

 //去员工的修改页面
    @GetMapping("/emp/{id}")
    public String toUpdateEmp(@PathVariable("id")Integer id,Model model){
        //查出需要修改的员工数据
        Employee employee = employeeMapper.getEmployeeById(id);

        model.addAttribute("emp",employee);
        //同样需要查到部门信息
        Collection<Department> deparment = departmentMapper.getDeparment();
        model.addAttribute("deparment",deparment);
        return "emp/update";
    }
    //修改完之后重定向到展示页面
    @PostMapping("/updateEmp")
    public String updateEmp(Employee employee){
        employeeMapper.save(employee);
        return "redirect:/emps";
    }

PS:这里有一个很神奇的地方,创建和修改的时候性别只能为男,就算创建时选中为女,也会改为男,绝了

删除及404处理:

删除数据贼简单,点击删除携带id调转到controller,然后controller获取到id执行删除方法再跳转到展示页面就可以了:

//删除员工
    @GetMapping("/deleteEmp/{id}")
    public String deleteEmp(@PathVariable("id")Integer id){
        employeeMapper.delete(id);
        return "redirect:/emps";
    }

404

springboot的404以及500等报错页面也十分简单,只需要在template包下面新建一个error包,然后将报错页面放里面就可以了
整体文件结构如下:
在这里插入图片描述

在这里插入图片描述

退出登录:

这个不能直接点超链接到首页,因为这样的话退回页面就可以重新回来,
所以也要进controller然后注销session再重定向到首页:

@RequestMapping("/user/logout")
    public String loginOut(HttpSession session){
        session.invalidate();
        return "redirect:/index";
    }

到此,完结(才怪)撒花!!!

一共四万多字,纯手打,三天打鱼两天晒网共计花了一个月左右吧,纯小白自学,平常还得复习补充,真特奶奶的令人绝望,不过还是加油吧,静态资源在老秦二群,以后想重新写也是完全可以的,
整合数据库之类的写到下一个博客里面吧,
2021年1月6日 23:18:46 xi哥努力的样子今天还是那么的帅

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Spring Boot整合指的是将Spring Boot与其他技术或框架集成在一起,以实现更强大的功能或特性。其中,常见的整合包括数据库整合、Swagger整合等。 对于数据库整合,可以使用Spring Boot提供的JDBC Starter和相应的数据库驱动依赖(如MySQL Connector)来连接数据库并进行数据操作。通过配置数据源的相关参数,可以在应用启动时自动加载对应的自动装配类,实现数据库的初始化和连接。 对于Swagger整合,可以使用Spring Boot提供的Swagger2注解@EnableSwagger2,开启Swagger的注解。通过引入相应的Swagger依赖,可以生成API文档,并提供交互式的API界面,方便开发人员调试和测试API接口。 需要注意的是,Spring Boot的设计目的是简化Spring应用的搭建和开发过程。它采用特定的配置方式,减少了冗余的配置代码,提高了开发效率。同时,Spring Boot也具有自动装配、内嵌服务器等特性,使得应用部署和运行更加方便。 总结起来,Spring Boot整合是通过引入相应的依赖和注解,将Spring Boot与其他技术或框架集成在一起,以实现更强大的功能和特性。常见的整合包括数据库整合和Swagger整合等。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [JAVA高级篇--springboot自动装配](https://blog.csdn.net/lpt1314/article/details/125943497)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [springboot框架整合](https://blog.csdn.net/DDHuao/article/details/130077877)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值