SpringBoot
1. SpringBoot 介绍(熟悉)
1.1 原有Spring优缺点分析
Spring的优点分析
:- Spring是Java企业版(Java Enterprise Edition,JEE,也称J2EE)的轻量级代替品。无需开发重量级的Enterprise JavaBean(EJB),Spring为企业级Java开发提供了一种相对简单的方法,通过依赖注入和面向切面编程,用简单 的Java对象(Plain Old Java Object,POJO)实现了EJB的功能。
Spring的缺点分析
:- 虽然Spring的组件代码是轻量级的,但它的配置却是重量级的
- 一开始,Spring用XML配置,而且是很多XML配 置。
Spring 2.5引入了基于注解的组件扫描
,这消除了大量针对应用程序自身组件的显式XML配置 Spring 3.0引入 了基于Java的配置,这是一种类型安全的可重构配置方式,可以代替XML
- 所有这些配置都代表了开发时的损耗。因为在思考Spring特性配置和解决业务问题之间需要进行思维切换,所以编 写配置挤占了编写应用程序逻辑的时间。和所有框架一样,Spring实用,但与此同时它要求的回报也不少
- 除此之外,项目的依赖管理也是一件耗时耗力的事情。在环境搭建时,需要分析要导入哪些库的坐标,而且还需要 分析导入与之有依赖关系的其他库的坐标,一旦选错了依赖的版本,随之而来的不兼容问题就会严重阻碍项目的开发进度。
1.2 SpringBoot概念简介
- Spring Boot是Spring公司的一个顶级项目,和Spring Framework是一个级别的。
- Spring Boot实际上是利用Spring Framework 4 自动配置特性完成。
编写项目时不需要编写xml文件
。发展到现在,Spring Boot已经具有很很大的生态圈,各种主流技术已经都提供了Spring Boot的启动器
。
1.3 什么是启动器
- Spring框架在项目中作用是Spring整合各种其他技术,让其他技术使用更加方便。
Spring Boot的启动器实际上就是一个依赖
- 这个依赖中包含了整个这个技术的相关jar包,还包含了这个技术的自动配置,
以前绝大多数XML配置都不需要配置了
- 当然了,
启动器中自动配置无法实现所有内容的自动配置,在使用Spring Boot时还需要进行少量的配置
(这个配置不是在xml中了,而是在properties或yml中即可) - 如果是Spring自己封装的启动器的artifact id名字满足:spring-boot-starter-xxxx,如果是第三方公司提供的启动满足:xxxx-spring-boot-starter。
以后每次使用Spring Boot整合其他技术时首先需要考虑导入启动器
。
1.4 Spring Boot优点
- 使用Spring Boot可以
创建独立的Spring应用程序
- 在Spring Boot中直接
嵌入了Tomcat、Jetty、Undertow等 Web 容器
,在使用SpringBoot做Web开发时不需要部署WAR文件
- 通过
提供自己的启动器(Starter)依赖
,简化项目构建配置 - 尽量的
自动配置Spring和第三方库
- 绝对没有代码生成,也
不需要XML配置文件
1.5 Spring Boot版本介绍
SNAPSHOT
:快照版,即开发版。CURRENT
:最新版,但是不一定是稳定版。GA
:General Availability,正式发布的版本。
1.6 Spring Boot的核心
起步依赖
:起步依赖本质上是一个Maven项目对象模型(Project Object Model,POM),定义了对其他库的传递依赖,这些东西加在一起即支持某项功能。 简单的说,起步依赖就是将具备某种功能的坐标打包到一起,并提供一些默认的功能。自动配置
:Spring Boot的自动配置是一个运行时(更准确地说,是应用程序启动时)的过程,考虑了众多因素,才决定 Spring配置应该用哪个,不该用哪个。该过程是Spring自动完成的。
2 项目搭建方式1(掌握)
2.1 pom导入相关依赖与parent
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>top.mikevane</groupId>
<artifactId>springboot01</artifactId>
<version>1.0-SNAPSHOT</version>
<!--继承父项目方式-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.5</version>
</parent>
<dependencies>
<!-- springboot启动器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.4.5</version>
</dependency>
</dependencies>
</project>
2.2 依赖传递
2.3 新建Controller
- 新建controller,作为测试用
@Controller
public class MyController {
@RequestMapping("firstController")
@ResponseBody
public String firstController(){
return "hello springboot";
}
}
2.4 新建启动类
- Spring Boot的启动类的作用是启动Spring Boot项目,是基于Main方法来运行的。
注意
:- 启动类在启动时会做注解扫描(@Controller、@Service、@Repository……),扫描位置为
同包或者子包下的注解
,所以启动类的位置应放于包的根下 - 必须在包中新建这个类,不能直接放入到java文件夹
命名规范
:XXXXApplication,可以是项目上下文路径Application
- 启动类在启动时会做注解扫描(@Controller、@Service、@Repository……),扫描位置为
@SpringBootApplication
public class Springboot01Application {
public static void main(String[] args) {
SpringApplication.run(Springboot01Application.class,args);
}
}
2.5 启动类与启动器区别
- 启动类表示
项目的启动入口
- 启动器表示
jar包的坐标
2.6 项目启动日志
- 端口号默认8080
- 项目上下文路径默认为空
2.7 测试访问
3 项目搭建方式2(掌握)
- 在公司中可能会出现必须继承某个项目,如果Spring Boot用了继承就不能继承别的项目了。所以Spring Boot还提供了依赖的方式
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>top.mikevane</groupId>
<artifactId>springboot02</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.4.5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.4.5</version>
</dependency>
</dependencies>
</project>
3.1 使用idea自带springBoot项目初始化插件
4 SpringBoot启动原理(理解)
4.1 依赖导入原理
- 父项目版本控制
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
- ctrl+鼠标左键点击进入
spring-boot-starter-parent
- 再次点击进入
spring-boot-dependencies
- 这里管理着springboot中所有依赖的版本,版本控制中心,导入依赖如果不写版本就用这里的版本
spring-boot-starter-web
,也就是web启动器,导入和很多web的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
4.2 springboot 包扫描原理
进入
@SpringBootApplication
注解进入
@EnableAutoConfiguration(启动自动配置功能)
进入
@AutoConfigurationPackage
可以看到,这里导入了
Registrar类
,包扫描应该就是在这个类中,进入这个类@Import是spring的 底层注解,给容器导入一个组件
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
Registrar() {
}
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
AutoConfigurationPackages.register(registry, (String[])(new AutoConfigurationPackages.PackageImports(metadata)).getPackageNames().toArray(new String[0]));
}
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new AutoConfigurationPackages.PackageImports(metadata));
}
}
}
- 这里可以看到,获取包名应该是在
registerBeanDefinitions
这个方法,在这里打断点,然后测试 可以发现这里就是包扫描的原理,这里将配置类@SpringBootApplication标注的类所在包及下面所有子包里的所有组件扫描到spring容器
4.3 springboot自动配置原理
- 在前面的
SpringBootApplication注解
中,可以找到导入默认配置类的注解 - 在
AutoConfigurationImportSelector
类中找到AutoConfigurationImportSelector
方法
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = new ArrayList(SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader()));
ImportCandidates.load(AutoConfiguration.class, this.getBeanClassLoader()).forEach(configurations::add);
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories nor in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. If you are using a custom packaging, make sure that file is correct.");
return configurations;
}
可以发现,配置与
spring.factories
文件有关查找springboot自带的
配置类
并找到对应的spring.factories
找到对应的
EnableAutoConfiguration
,下面信息即默认的配置类在上面的getCandidateConfigurations方法打断点,测试
spring.factories
的配置信息是否会被读取- 这些自动配置类给我们当前项目的场景提供了一些组件和配置,有了自动配置就免除类手动编写配置文件,注入等等功能
并且自带的jar包配置类中还有其他配置信息
5 项目配置(掌握)
5.1 properties配置文件
- SpringBoot默认读取项目下名字为application开头的 yml yaml properties配置文件
<resources>
<resource>
<directory>${basedir}/src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>**/application*.yml</include>
<include>**/application*.yaml</include>
<include>**/application*.properties</include>
</includes>
</resource>
</resources>
- 在项目中的
application.properties
里修改端口号和项目上下文路径
server.servlet.context-path=/springboot03
server.port=8090
注意
:- 遇到记不住的配置信息,去官网 https://docs.spring.io/spring-boot/docs/current/reference/html/application-properties.html#appendix.application-properties.web 查找
- 这里的每一个
.
都代表一个层级
5.2 yml文件
- yml中使用
缩进
代表层级关系 yml格式要求
- 大小写敏感
- 使用缩进代表层级关系
- 相同的部分只出现一次
- 注意空格
server:
servlet:
context-path: /springboot_02
port: 8080
spring:
datasource:
password:
username:
url:
driver-class-name:
yml的数据类型
#[1]普通数据类型
server:
port: 8888
#[2]配置对象类型数据
person:
name: zs
age: 12
sex: 男
#或者写成json格式
person2: {name: zs,age: 19 }
#[3]配置数组类型
city:
- beijing
- tianjin
- shanghai
- chongqing
#或者
city2: [beijing,tianjin,shanghai,chongqing]
注意
- 如果同一个目录下,有application.yml也有application.properties,
默认先读取application.properties
。 - 如果同一个配置属性,在多个配置文件都配置了,默认使用第1个读取到的,
后面读取的不覆盖前面读取到的
。
- 如果同一个目录下,有application.yml也有application.properties,
5.2.1 配置文件存放位置
- 当前项目根目录中
- 当前项目根目录下的一个/config子目录中
- 项目的resources即classpath根路径中
- 项目的resources即classpath根路径下的/config目录中
5.2.2 配置文件存放读取优先级
当前项目根目录下的一个/config子目录中(最高)
- config/application.properties
- config/application.yml
当前项目根目录中(其次)
- application.properties
- application.yml
项目的resources即classpath根路径下的/config目录中(一般)
- resources/config/application.properties
- resources/config/application.yml
项目的resources即classpath根路径中(最后)
- resources/application.properties
- resources/application.yml
5.3 bootstrap配置文件
Spring Boot 中有两种上下文对象,一种是 bootstrap, 另外一种是 application(ServletContext), bootstrap 是应用程序的父上下文,也就是说 bootstrap 加载优先于 applicaton
bootstrap 主要用于从额外的资源来加载配置信息,还可以在本地外部配置文件中解密属性。这两个上下文共用一个环境,它是任何Spring应用程序的外部属性的来源。bootstrap 里面的属性会优先加载,它们默认也不能被本地相同配置覆盖。
bootstrap配置文件特征
- boostrap 由父 ApplicationContext 加载,比 applicaton 优先加载。
- boostrap 里面的属性不能被覆盖。
bootstrap与 application 的应用场景
- application 配置文件主要用于 Spring Boot 项目的自动化配置。
- bootstrap 配置文件有以下几个应用场景。
- 使用 SpringCloudConfig 配置中心时,这时需要在 bootstrap 配置文件中添加连接到配置中心的配置属性来加载外部配置中心的配置信息。
- 一些固定的不能被覆盖的属性。
- 一些加密/解密的场景。
5.4 SpringBoot项目结构
-- 项目名
--src
--main
--java
java代码
--resources
--public 公共资源。所有共享的内容。对外公开的内容。
--static静态资源。图片、js、css。不会被服务器解析。
--js
-- jquery.js 访问路径:http://ip:port/js/jquery.js
注意:该目录是SpringBoot可以直接识别的目录,会将其中的
静态资源编译到web项目中,并放到tomcat中使用。静态资源的
访问路径中无需声明static 例如:localhost:8080/a.png
--templates
FreeMarker thymeleaf 页面所在目录。
--webapp 只有当页面使用jsp时才有。
--WEB-INF
设置WEB-INF
6 整合MyBatis(掌握)
6.1 导入依赖
<!--MyBatis启动器-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.3</version>
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.21</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
</dependency>
- 如果不指定mysql版本,则使用
springboot默认指定的版本号
(具体见4.1)- 可以通过
properties
标签指定mysql版本号
- 可以通过
6.2 编写配置文件
appliction.yml
中添加如下配置
spring:
datasource:
username: root
password: 123456
url: jdbc:mysql://127.0.0.1:3306/mydb?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
type-aliases-package: top.mikevane.pojo
mapper-locations: classpath:mybatis/*.xml
mapper-locations: classpath:mybatis/*.xml
:mapper映射文件包扫描type-aliases-package
:实体类别名包扫描
6.3 编写功能代码
在启动类上添加注解,表示mapper接口所在位置
@SpringBootApplication
@MapperScan("top.mikevane.mapper")
public class Springboot03Application {
public static void main(String[] args) {
SpringApplication.run(Springboot03Application.class, args);
}
}
定义mapper接口
//@Mapper
public interface UserMapper {
List<User> selectAll();
}
注意
:如果不在
MyApplication启动类上添加@MapperScan,那么必须在UserMapper接口上添加@Mapper注解
定义mapper.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" >
<mapper namespace="top.mikevane.mapper.UserMapper">
<select id="selectAll" resultType="top.mikevane.pojo.User">
select * from user
</select>
</mapper>
- 在resource下新建mybatis文件夹,mapper.xml文件名没有要求了,不需要和接口名完全对应了,是根据namespace去找接口。
但是最好还是和接口名字保持一致
controller层代码
@Controller
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("findAll")
@ResponseBody
public List<User> findAll(){
return userService.findAll();
}
}
service层代码
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public List<User> findAll() {
return userMapper.selectAll();
}
}
- idea中往往会误报代码错误,如果我们确定代码无问题,可以通过降低idea检查代码的严格程度来消除报错
快捷键:ctrl+alt+shift+h
7 整合logback(掌握)
- Spring Boot默认使用Logback组件作为日志管理。Logback是由log4j创始人设计的一个开源日志组件。
- 在Spring Boot项目中我们不需要额外的添加Logback的依赖,因为在spring-boot-starter或者spring-boot-starter-web中已经包含了Logback的依赖。
Logback读取配置文件的步骤
- 在classpath下查找文件logback-test.xml
- 如果文件不存在,则查找logback.xml
配置代码
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径-->
<property name="LOG_HOME" value="${catalina.base}/logs/" />
<!-- 控制台输出 -->
<appender name="Stdout" class="ch.qos.logback.core.ConsoleAppender">
<!-- 日志输出格式 -->
<layout class="ch.qos.logback.classic.PatternLayout">
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
</pattern>
</layout>
</appender>
<!-- 按照每天生成日志文件 -->
<appender name="RollingFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--日志文件输出的文件名-->
<FileNamePattern>${LOG_HOME}/server.%d{yyyy-MM-dd}.log</FileNamePattern>
<MaxHistory>30</MaxHistory>
</rollingPolicy>
<layout class="ch.qos.logback.classic.PatternLayout">
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
</pattern>
</layout>
<!--日志文件最大的大小-->
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<MaxFileSize>10MB</MaxFileSize>
</triggeringPolicy>
</appender>
<!-- 日志输出级别 -->
<root level="info">
<appender-ref ref="Stdout" />
<!-- <appender-ref ref="RollingFile" />-->
</root>
<logger name="top.mikevane.mapper" level="DEBUG"></logger>
<!--日志异步到数据库 -->
<!--<appender name="DB" class="ch.qos.logback.classic.db.DBAppender">
日志异步到数据库
<connectionSource class="ch.qos.logback.core.db.DriverManagerConnectionSource">
连接池
<dataSource class="com.mchange.v2.c3p0.ComboPooledDataSource">
<driverClass>com.mysql.jdbc.Driver</driverClass>
<url>jdbc:mysql://127.0.0.1:3306/databaseName</url>
<user>root</user>
<password>root</password>
</dataSource>
</connectionSource>
</appender> -->
</configuration>
8 整合PageHelper(掌握)
- 我们在正常的查询业务之中,只需要加上一行代码就可以
实现分页的数据的封装处理
MyBatis整合PageHelper插件
,自行阅读即可
8.1 实现原理
- PageHelper方法使用了静态的ThreadLocal参数,分页参数和线程是绑定的
- 内部流程是ThreadLocal中设置了分页参数(pageIndex,pageSize),之后在查询执行的时候,获取当线程中的分页参数,执行查询的时候通过拦截器在sql语句中添加分页参数,之后实现分页查询,查询结束后在 finally 语句中清除ThreadLocal中的查询参数
8.2 使用方法
调用PageHelper方法:
PageHelper.startPage(pageNum, pageSize)
MyBatis 查询方法
注意
:只要你可以保证在PageHelper方法调用后紧跟 MyBatis 查询方法,这就是安全的。因为PageHelper在finally代码段中自动清除了ThreadLocal存储的对象
8.3 整合步骤
添加PageHelper启动器依赖
<!--PageHelper启动器-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.12</version>
</dependency>
Controller层代码编写
@Controller
@RequestMapping("/emp")
public class EmpController {
@Autowired
private EmpService empService;
@RequestMapping("findByPage/{pageNum}/{pageSize}")
@ResponseBody
public List<Emp> findByPage(@PathVariable("pageNum") Integer pageNum, @PathVariable("pageSize") Integer pageSize){
return empService.findByPage(pageNum,pageSize);
}
}
Service层代码编写
@Service
public class EmpServiceImpl implements EmpService {
@Autowired
private EmpMapper empMapper;
@Override
public List<Emp> findByPage(Integer pageNum, Integer pageSize) {
Page<Emp> empPage = PageHelper.startPage(pageNum, pageSize);
List<Emp> emps = empMapper.selectAll();
// 页码 页大小 当前业数据 总页数 总记录数
// 方式1(不推荐)
System.out.println("当前页:"+empPage.getPageNum());
System.out.println("总页数:"+empPage.getPages());
System.out.println("页大小:"+empPage.getPageSize());
System.out.println("总记录数:"+empPage.getTotal());
System.out.println("当前页数据:"+empPage.getResult());
// 方式二(推荐) PageInfo >>> PageBean
PageInfo<Emp> pageInfo = new PageInfo<>(emps);
System.out.println("当前页:"+pageInfo.getPageNum());
System.out.println("总页数:"+pageInfo.getPages());
System.out.println("页大小:"+pageInfo.getPageSize());
System.out.println("总记录数:"+pageInfo.getTotal());
System.out.println("当前页数据:"+pageInfo.getList());
return pageInfo.getList();
}
}
8.4 PageInfo对象和Page对象的区别
9 整合Druid(掌握)
- Druid是由阿里巴巴推出的数据库连接池。它结合了C3P0、DBCP、PROXOOL等数据库连接池的优点。之所以从众多数据库连接池中脱颖而出,还有一个
重要的原因就是它包含控制台,很方便的帮助我们实现对于sql执行的监控。
9.1 整合步骤
添加依赖
<!--德鲁伊启动器-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
修改配置文件application.yml
spring:
datasource:
# 使用阿里的Druid连接池
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
# 填写你数据库的url、登录名、密码和数据库名
url: jdbc:mysql://127.0.0.1:3306/mydb?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
username: root
password: 123456
druid:
# 连接池的配置信息
# 初始化大小,最小,最大
initial-size: 5
min-idle: 5
maxActive: 20
# 配置获取连接等待超时的时间
maxWait: 60000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
timeBetweenEvictionRunsMillis: 60000
# 配置一个连接在池中最小生存的时间,单位是毫秒
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
# 打开PSCache,并且指定每个连接上PSCache的大小
poolPreparedStatements: true
maxPoolPreparedStatementPerConnectionSize: 20
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
filters: stat,wall,slf4j
# 通过connectProperties属性来打开mergeSql功能;慢SQL记录
connectionProperties: druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000
# 配置DruidStatFilter
web-stat-filter:
enabled: true
url-pattern: "/*"
exclusions: "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*"
# 配置DruidStatViewServlet
stat-view-servlet:
url-pattern: "/druid/*"
# IP白名单(没有配置或者为空,则允许所有访问)
allow: 127.0.0.1,192.168.8.109
# IP黑名单 (存在共同时,deny优先于allow)
deny: 192.168.1.188
# 禁用HTML页面上的“Reset All”功能
reset-enable: false
# 登录名
login-username: admin
# 登录密码
login-password: 123456
访问监控界面
10 整合JSP(了解)
添加依赖
<!--JSP依赖-->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<scope>provided</scope>
</dependency>
添加webapp目录并设置目录(见5.4)
设置工作目录
,如果在IDEA中项目结构为聚合工程。那么在运行jsp是需要指定路径。如果项目结构为独立项目则不需要。在yml配置文件中配置视图解析器参数
spring:
mvc:
view:
prefix: /WEB-INF/
suffix: .jsp
在控制类中声明单元方法请求转发jsp资源
@Controller
public class PageController {
@RequestMapping("/{uri}")
public String getPage(@PathVariable("uri") String uri){
return uri;
}
}
11 整合FreeMarker(熟悉)
11.1 FreeMarker的简介
FreeMarker是一款模板引擎
: 即一种基于模板和要改变的数据, 并用来生成输出文本(HTML网页、电子邮件、配置文件、源代码等)的通用工具。 它不是面向最终用户的,而是一个Java类库,是一款程序员可以嵌入他们所开发产品的组件。- FreeMarker是免费的,基于Apache许可证2.0版本发布。其模板编写为FreeMarker Template Language(FTL),属于简单、专用的语言。需要准备数据在真实编程语言中来显示,比如数据库查询和业务运算, 之后模板显示已经准备好的数据。
在模板中,主要用于如何展现数据, 而在模板之外注意于要展示什么数据
常用的java模板引擎还有哪些
?- Jsp、Freemarker、Thymeleaf 、Velocity 等
- 模板+数据模型=输出
- freemarker并不关心数据的来源,只是根据模板的内容,将数据模型在模板中显示并输出文件(通常为html,也可以生成其它格式的文本文件)
- freemarker作为springmvc一种视图格式,
默认情况下SpringMVC支持freemarker视图格式
。 需要创建Spring Boot+Freemarker工程用于测试模板。
11.2 FreeMarker初次使用
导入依赖
<!--freeMaker启动器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
创建controller
@RequestMapping("/freemarker")
@Controller
public class FreeMarkerController {
@RequestMapping("/show")
public String freemarker(Map<String, Object> map){
map.put("name","mikevane");
//返回模板文件名称
return "show";
}
}
书写模板文件 show.ftlh
- 通过查阅配置信息发现,
默认前缀为 '' ,后缀为.ftlh,默认路径为templates
- 通过查阅配置信息发现,
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
this is show page
${name}
</body>
</html>
11.3 FreeMarker常用指令
注释
,即<#‐‐‐‐>,介于其之间的内容会被freemarker忽略插值
(Interpolation):即${..}部分,freemarker会用真实的值代替${..}FTL指令
:和HTML标记类似,名字前加#予以区分,Freemarker会解析标签中的表达式或逻辑。文本
,仅文本信息,这些不是freemarker的注释、插值、FTL指令的内容会被freemarker忽略解析,直接输出内容
11.3.1 遍历List集合
controller层代码
@RequestMapping("/showEmp")
public ModelAndView testList(){
ModelAndView modelAndView = new ModelAndView();
List<Emp> list = empService.findAll();
modelAndView.addObject("empList",list);
modelAndView.setViewName("showEmp");
return modelAndView;
}
页面代码 showEmp.ftlh
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style type="text/css">
#empTable{
width: 80%;
border: 1px solid blue;
margin: 0px auto;
}
#empTable th,td{
border: 1px solid green;
text-align: center;
}
</style>
</head>
<body>
<table id="empTable" cellpadding="0px" cellspacing="0px">
<tr>
<th>索引</th>
<th>工号</th>
<th>姓名</th>
<th>岗位</th>
<th>薪资</th>
<th>部门号</th>
</tr>
<#list empList as emp>
<tr>
<td>${emp_index}</td>
<td>${emp.empno}</td>
<td>${emp.ename}</td>
<td>${emp.job}</td>
<td>${emp.sal}</td>
<td>${emp.deptno}</td>
</tr>
</#list>
</table>
</body>
</html>
注意
:_index
:得到循环的下标,使用方法是在stu后边加”_index”,它的值是从0开始
11.3.2 遍历Map数据
controller代码
@RequestMapping("/showEmpMap")
public ModelAndView testMap(){
ModelAndView modelAndView = new ModelAndView();
List<Emp> list = empService.findAll();
Map<String,Emp> empMap = new HashMap<>();
for (Emp emp : list) {
empMap.put(emp.getEmpno().toString(),emp);
}
modelAndView.addObject("empMap",empMap);
modelAndView.setViewName("showEmpMap");
return modelAndView;
}
注意
:这里map的key类型必须是String
页面代码 showEmpMap.ftlh
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style type="text/css">
#empTable{
width: 80%;
border: 1px solid blue;
margin: 0px auto;
}
#empTable th,td{
border: 1px solid green;
text-align: center;
}
</style>
</head>
<body>
<#--查看map集合中单独的一个元素-->
查看7521员工信息<br/>
工号${empMap['7521'].empno}<br/>
姓名${empMap['7521'].ename}<br/>
岗位${empMap['7521'].job}<br/>
薪资${empMap['7521'].sal}<br/>
部门号${empMap['7521'].deptno}<br/>
<hr/>
<table id="empTable" cellpadding="0px" cellspacing="0px">
<tr>
<th>索引</th>
<th>工号</th>
<th>姓名</th>
<th>岗位</th>
<th>薪资</th>
<th>部门号</th>
</tr>
<#list empMap?keys as key>
<tr>
<td>${key_index}</td>
<td>${key}</td>
<td>${empMap[key].ename}</td>
<td>${empMap[key].job}</td>
<td>${empMap[key].sal}</td>
<td>${empMap[key].deptno}</td>
</tr>
</#list>
</table>
</body>
</html>
- 因为传入的key是String类型,所以这里的key是String类型的
11.3.3 if指令
if 指令即判断指令,是常用的FTL指令,freemarker在解析时遇到if会进行判断,条件为真则输出if中间的内容,否则跳过内容不再输出。
if中支持的运算符
算数运算符
FreeMarker表达式中完全支持算术运算
,FreeMarker支持的算术运算符包括:+, - , * , / , %逻辑运算符
有如下几个: 逻辑与:&& 逻辑或:|| 逻辑非:!逻辑运算符只能作用于布尔值,否则将产生错误
比较运算符
有如下几个:
①=或者==
:判断两个值是否相等.
②!=
:判断两个值是否不等.
③> 或者gt
:判断左边值是否大于右边值
④>=或者gte
:判断左边值是否大于等于右边值
⑤<或者lt
:判断左边值是否小于右边值
⑥<=或者lte
:判断左边值是否小于等于右边值注意
:=和!=可以用于字符串,数值和日期来比较是否相等,但=和!=两边必须是相同类型的值,否则会产生错误
而且FreeMarker是精确比较,”x”,”x “,”X”是不等的.
其它的运行符可以作用于数字和日期,但不能作用于字符串
大部分的时候,
使用gt等字母运算符代替>会有更好的效果
,因为 FreeMarker会把>解释成FTL标签的结束字符,当然,也可以使用括号来避免这种情况,如:<#if (x>y)>
如何判断空值
判断某变量是否存在
:使用 “??” 用法为:variable??,如果该变量存在返回true,否则返回false- 例:为防止mgr为空报错可以加上判断如下:${emp.mgr!’无’}
缺失变量默认值
:使用 “!” 使用 ! 需要指定一个默认值,当变量为空时显示默认值- 例: ${name!’’}表示如果name为空显示空字符串。如果是
嵌套对象则建议使用()括起来
- 例: ${name!’’}表示如果name为空显示空字符串。如果是
例如
<table id="empTable" cellpadding="0px" cellspacing="0px">
<tr>
<th>索引</th>
<th>工号</th>
<th>姓名</th>
<th>岗位</th>
<th>上级工号</th>
<th>薪资</th>
<th>补助</th>
<th>部门号</th>
</tr>
<#if empList??>
<#list empList as emp>
<#--如果序号为偶数则背景色变为灰色-->
<tr <#if emp_index%2 == 0>style="background-color: gray"</#if>>
<td>${emp_index}</td>
<td>${emp.empno}</td>
<#--标红姓名为KING-->
<td <#if emp.ename=='KING'>style="color: red"</#if>>
${emp.ename}
</td>
<td>${emp.job}</td>
<#--防止mgr为空报错,默认赋值为"空"-->
<td>${emp.mgr!'无'}</td>
<#--标红sal大于等于2000-->
<td <#if emp.sal gte 2000>style="color: red"</#if>>
${emp.sal}
</td>
<#--防止comm为空报错,默认赋值为0-->
<td>${emp.comm!'0'}</td>
<td>${emp.deptno}</td>
</tr>
</#list>
</#if>
</table>
11.3.4 内置函数
内置函数语法格式
: 变量+?+函数名称
内建函数获取某个集合的大小
- ${集合名?size}
内建函数日期格式化
显示年月日
:${today?date}显示时分秒
:${today?time}显示日期+时间
:${today?datetime}自定义格式化
:${today?string(“yyyy年MM月”)}
内建函数将json字符串转成对象
assign
的作用是定义一个变量
<#--相当于var text = {},然后使用evel(text),将text转换为json对象,-->
<#assign text="{'bank':'工商银行','account':'10101920201920212'}" />
<#assign data=text?eval />
开户行:${data.bank} 账号:${data.account}
页面测试
<#if empList??>
<#list empList as emp>
<#--如果序号为偶数则背景色变为灰色-->
<tr <#if emp_index%2 == 0>style="background-color: gray"</#if>>
<td>${emp_index}</td>
<td>${emp.empno}</td>
<#--标红姓名为KING-->
<td <#if emp.ename=='KING'>style="color: red"</#if>>
${emp.ename}
</td>
<td>${emp.job}</td>
<#--防止mgr为空报错-->
<td>${emp.mgr!'无'}</td>
<#--内置函数,显示时间日期等-->
<td>${emp.hiredate?date}</td>
<td>${emp.hiredate?time}</td>
<td>${emp.hiredate?datetime}</td>
<td>${emp.hiredate?string("yyyy年MM月dd日 HH-mm-ss")}</td>
<#--标红sal大于等于2000-->
<td <#if emp.sal gte 2000>style="color: red"</#if>>
${emp.sal}
</td>
<td>${emp.comm!'0'}</td>
<td>${emp.deptno}</td>
</tr>
</#list>
</#if>
12 整合Thymeleaf(掌握)
- Thymeleaf的主要目标是将优雅的自然模板带到开发工作流程中,并将HTML在浏览器中正确显示,并且可以作为静态原型,让开发团队能更容易地协作。
Thymeleaf能够处理HTML,XML,JavaScript,CSS甚至纯文本
。 - 长期以来,jsp在视图领域有非常重要的地位,随着时间的变迁,出现了一位新的挑战者:Thymeleaf,
Thymeleaf是原生的,不依赖于标签库.它能够在接受原始HTML的地方进行编辑和渲染
.因为它没有与Servelet规范耦合
,因此Thymeleaf模板能进入jsp所无法涉足的领域。 - Thymeleaf在Spring Boot项目中放入到
resources/templates
中。这个文件夹中的内容是无法通过浏览器URL直接访问的(和WEB-INF效果一样),所有Thymeleaf页面必须先走控制器
关于Thymeleaf默认配置
- 项目中添加依赖
<dependencies>
<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>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.3</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.21</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.12</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
<version>2.4.5</version>
</dependency>
</dependencies>
- 在resources下新建templates文件夹。新建index.html
- 新建controller
@Controller
public class ThymeleafController {
@RequestMapping("showIndex")
public String showIndex(){
return "index";
}
}
12.1 Thymeleaf基础语法
- Thymeleaf通过标准变量表达式完成数据的展示和处理
- 标准变量表达式
必须依赖标签,不能独立使用
- 标准变量表达式 `一般在开始标签中,以 th开头``
- ``语法为`:<tag th:***=”${key}” ></tag>
- 表达式中可以
通过${}取出域中的值并放入标签的指定位置
${}在这里不能单独使用
,必须在 th:后面的双引号里使用
- 标准变量表达式
为了让IDEA有提示
,修改html页面中<html>标签为- <html xmlns:th=”http://www.thymeleaf.org" >
12.1.1 th:text属性
向HTML标签内部输出信息
Controller代码
@RequestMapping("showIndex")
public String showIndex(Map<String,Object> map){
map.put("msg","testMsg");
return "index";
}
页面代码
<!--向span双标签内部添加文本-->
<span th:text="pageMessage"></span>
<!--从域中根据参数名取出参数值放在双标签中-->
<span th:text="${msg}"></span>
12.1.2 th:value
- 表单元素,
设置HTML标签中表单元素value属性时使用
- 注意:
对象的属性可以为空,但是对象不能为空
<!--向input标签中的value属性赋值-->
<input type="text" th:value="pageMessage"/>
<!--从域中根据参数名取出参数值 向input标签中的value属性赋值-->
<input type="text" th:value="${msg}"/>
12.1.3 th:if
- 与java if关键字类似
<span th:if="${name}!='张三'">会显示</span>
12.1.4 循环遍历.th:each
示例中u为迭代遍历。
th:each=”u,i :${list}” 其中i表示迭代状态。
index
:当前迭代器的索引 从0开始count
:当前迭代对象的计数 从1开始size
:被迭代对象的长度even/odd
:布尔值,当前循环是否是偶数/奇数 从0开始first
:布尔值,当前循环的是否是第一条,如果是返回true否则返回falselast
:布尔值,当前循环的是否是最后一条,如果是则返回true否则返回false
Controller代码
@RequestMapping("showEmp")
public String showEmp(Map<String,Object> map){
List<Emp> empList = empService.findAll();
map.put("emp",null);
map.put("empList",empList);
return "showEmp";
}
页面代码
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style type="text/css">
#empTable{
width: 80%;
border: 1px solid blue;
margin: 0px auto;
}
#empTable th,td{
border: 1px solid green;
text-align: center;
}
</style>
</head>
<body>
<table id="empTable" cellpadding="0px" cellspacing="0px">
<tr>
<th>索引</th>
<th>序号</th>
<th>总人数</th>
<th>偶数索引?</th>
<th>奇数索引?</th>
<th>第一?</th>
<th>最后?</th>
<th>工号</th>
<th>姓名</th>
<th>职务</th>
<th>上级</th>
<th>入职日期</th>
<th>工资</th>
<th>补助</th>
<th>部门号</th>
</tr>
<tr th:each="emp,i:${empList}">
<td th:text="${i.index}"></td>
<td th:text="${i.count}"></td>
<td th:text="${i.size}"></td>
<td th:text="${i.odd}"></td>
<td th:text="${i.even}"></td>
<td th:text="${i.first}"></td>
<td th:text="${i.last}"></td>
<td th:text="${emp.empno}"></td>
<td th:text="${emp.ename}"></td>
<td th:text="${emp.job}"></td>
<td th:text="${emp.mgr}"></td>
<td th:text="${emp.hiredate}"></td>
<td th:text="${emp.sal}"></td>
<td th:text="${emp.comm}"></td>
<td th:text="${emp.deptno}"></td>
</tr>
</table>
</body>
</html>
页面效果
12.1.5 标准变量表达式运算符支持
在早期的thymeleaf模板引擎框架中 逻辑运算符要写在${}的外边,后面的版本可以写在${}里面
算数运算符
算术运算
:+ , - , * , / , %
<span th:text="1+1"></span>
<span th:text="'1'+1"></span>
<span th:text="${emp.empno}+1"></span>
<span th:text="${emp.empno+1}"></span>
关系运算符
gt
:great than(大于)>ge
:great equal(大于等于)>=eq
:equal(等于)==lt
:less than(小于)<le
:less equal(小于等于)<=ne
:not equal(不等于)!=
逻辑运算符
&& 或 and
:表示并且|| 或 or
: 表示或者
<div th:text="1>0 and 2<3"></div>
<div th:text="1>0 and 2>3"></div>
<div th:text="1>0 or 2<3"></div>
<div th:text="1>0 or 2>3"></div>
<hr/>
<div th:text="${emp.sal ge 800}"></div>
<div th:text="${emp.sal } ge 800"></div>
<div th:text="${emp.sal ge 800} and ${emp.deptno eq 20}"></div>
<div th:text="(${emp.sal }ge 800) or (${emp.deptno } ne 20)"></div>
<div th:text="${emp.sal ge 800 or emp.deptno ne 20 }"></div>
三目运算符
<tr th:each="emp,i:${empList}" th:class="${i.odd}?a:b">
对空值作出处理
<tr th:each="emp,i:${empList}" th:class="${i.odd}?a:b">
<td th:text="${i.index}"></td>
<td th:text="${i.count}"></td>
<td th:text="${i.size}"></td>
<td th:text="${i.odd}"></td>
<td th:text="${i.even}"></td>
<td th:text="${i.first}"></td>
<td th:text="${i.last}"></td>
<td th:text="${emp.empno}"></td>
<td th:text="${emp.ename}"></td>
<td th:text="${emp.job}"></td>
<td th:text="${emp.mgr} eq null ?老板:${emp.mgr}"></td>
<td th:text="${emp.hiredate}"></td>
<td th:text="${emp.sal}"></td>
<td th:text="${emp.comm} eq null ?0:${emp.comm}"></td>
<td th:text="${emp.deptno}"></td>
</tr>
12.1.6 th:href
- th:href:
设置href属性
使用@{}取值
<a th:href="@{/getParam(id=1,name='msb')}" >跳转</a>
<!-- 获取作用域值-->
<a th:href="@{/getParam(name=${stu.name},age=${stu.age})}">跳转二</a>
示例:删除数据后跳转回原页面
Controller层
@RequestMapping("showEmp")
public String showEmp(Map<String,Object> map){
List<Emp> empList = empService.findAll();
map.put("emp",empList.get(0));
map.put("empList",empList);
return "showEmp";
}
@RequestMapping("removeEmp")
public String removeEmp(Integer empno, String ename){
Boolean success = empService.deleteEmp(empno,ename);
return "redirect:/showEmp";
}
页面代码
<tr th:each="emp,i:${empList}">
<td th:text="${i.index}"></td>
<td th:text="${i.count}"></td>
<td th:text="${i.size}"></td>
<td th:text="${i.odd}"></td>
<td th:text="${i.even}"></td>
<td th:text="${i.first}"></td>
<td th:text="${i.last}"></td>
<td th:text="${emp.empno}"></td>
<td th:text="${emp.ename}"></td>
<td th:text="${emp.job}"></td>
<td th:text="${emp.mgr}"></td>
<td th:text="${emp.hiredate}"></td>
<td th:text="${emp.sal}"></td>
<td th:text="${emp.comm}"></td>
<td th:text="${emp.deptno}"></td>
<td>
<a th:href="@{/removeEmp(empno=${emp.empno},ename=${emp.ename})}">删除</a>
</td>
</tr>
12.1.7 th:onclick
给元素绑定事件,单击事件并传递参数
写法1
:仅仅支持数字和布尔类型参数的传递,字符串不支持
<a href="javascript:viod(0)" th:onclick="'del('+${emp.empno}+')'">删除</a>
写法2
:支持数字和文本类型的参数传递
<a href="javascript:void(0)" th:onclick="delEmp([[${emp.empno}]],[[${emp.ename}]])">删除</a>
示例
:使用写法2实现点击删除按钮,需要确认后再进行删除操作页面代码
<table id="empTable" cellpadding="0px" cellspacing="0px">
<tr>
<th>工号</th>
<th>姓名</th>
<th>职务</th>
<th>上级</th>
<th>入职日期</th>
<th>工资</th>
<th>补助</th>
<th>部门号</th>
<th>操作</th>
</tr>
<tr th:each="emp:${empList}">
<td th:text="${emp.empno}"></td>
<td th:text="${emp.ename}"></td>
<td th:text="${emp.job}"></td>
<td th:text="${emp.mgr}"></td>
<td th:text="${emp.hiredate}"></td>
<td th:text="${emp.sal}"></td>
<td th:text="${emp.comm}"></td>
<td th:text="${emp.deptno}"></td>
<td>
<a href="javascript:void(0)" th:onclick="removeClick([[${emp.empno}]],[[${emp.ename}]])">删除</a>
</td>
</tr>
</table>
<script>
function removeClick(empno,ename) {
var result = confirm("是否要删除编号为"+empno+"的"+ename);
if(result){
window.location.href = "removeEmp?empno="+empno+"&ename="+ename;
}
}
</script>
- 后端代码同
12.1.6
12. 2 Thymeleaf内置对象
- Thymeleaf提供了一些内置对象,内置对象可直接在模板中使用。这些对象是以#引用的。
- 使用内置对象的语法
引用内置对象需要使用#
- 大部分内置对象的名称都以s结尾。如:strings、numbers、dates
- 常见内置对象如下
#arrays
:数组操作的工具;#aggregates
:操作数组或集合的工具;#bools
:判断boolean类型的工具;#calendars
:类似于#dates,但是是java.util.Calendar类的方法;#ctx
:上下文对象,可以从中获取所有的thymeleaf内置对象;#dates
:日期格式化内置对象,具体方法可以参照java.util.Date;#numbers
: 数字格式化;#strings:字符串格式化,具体方法可以参照String,如startsWith、contains等;#objects
:参照java.lang.Object;#lists
:列表操作的工具,参照java.util.List;#sets
:Set操作工具,参照java.util.Set;#maps:Map操作工具,参照java.util.Map;#messages
:操作消息的工具。更多内置对象
: https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#number-literals
12.2.1 strings对象
12.2.2 dates对象
12.2.3 numbers对象
#numbers.formatDecimal(numbwe,整数位,整数位千分位标识符,小数位,小数位表示符)
- $