文章目录
- 1.SpringBoot简介
- 2.SpringBoot快速入门
- 3.SpringBoot原理分析
- 4.SpringBoot的配置文件
-
- 4.1SpringBoot配置文件的类型及作用
-
- 4.1.1配置文件类型及作用
- [4.1.2 application.properties](#4.1.2 application.properties)
- [4.1.3 application.yml或application.yaml](#4.1.3 application.yml或application.yaml)
- 4.1.4读取配置文件数据
- 4.2配置文件的加载顺序
- 4.3多环境配置
- 5.SpringBoot整合其他技术
-
- 5.1SpringBoot整合Junit
- 5.2SpringBoot整合Mybatis
- 5.3SpringBoot整合Redis
- [5.4SpringBoot整合Mybatis Plus](#5.4SpringBoot整合Mybatis Plus)
-
- 5.4.1导入依赖
- 5.4.2yml配置
- 5.4.3用户信息实体
- 5.4.4配置类
- 5.4.5启动类
- 5.4.6Dao层
- 5.4.7Service层
- [5.4.8扩展:Mybatis Plus的核心](#5.4.8扩展:Mybatis Plus的核心)
- 5.5SpringBoot整合RabbitMQ
- 6.SpringBoot实现拦截器
- 7.文件上传/下载
- 8.SpringBoot访问静态资源
1.SpringBoot简介
1.1原有Spring优缺点分析
1.1.1Spring优点
Spring是Java企业版(Java Enterprise Edition,JEE,也称J2EE)的轻量级代替品。无需开发重量级的Enterprise JavaBean(EJB),Spring为企业级Java开发提供了一种相对简单的方法,通过依赖注入和面向切面编程,用简单的Java对象(Plain Old Java Object,POJO)实现了EJB的功能。
1.1.2Spring缺点
虽然Spring的组件代码是轻量级的,但它的配置却是重量级的。一开始,Spring用XML配置,而且是很多XML配置。Spring 2.5引入了基于注解的组件扫描,这消除了大量针对应用程序自身组件的显式XML配置。Spring 3.0引入了基于Java的配置,这是一种类型安全的可重构配置方式,可以代替XML。
所有这些配置都代表了开发时的损耗。因为在思考Spring特性配置和解决业务问题之间需要进行思维切换,所以编写配置挤占了编写应用程序逻辑的时间。和所有框架一样,Spring实用,但与此同时它要求的回报也不少。
除此之外,项目的依赖管理也是一件耗时耗力的事情。在环境搭建时,需要分析要导入哪些库的坐标,而且还需要分析导入与之有依赖关系的其他库的坐标,一旦选错了依赖的版本,随之而来的不兼容问题就会严重阻碍项目的开发进度。
1.2SpringBoot概述
1.2.1SpringBoot解决上述Spring的缺点
SpringBoot对上述Spring的缺点进行的改善和优化,基于约定优于配置的思想,可以让开发人员不必在配置与逻辑业务之间进行思维的切换,全身心的投入到逻辑业务的代码编写中,从而大大提高了开发的效率,一定程度上缩短了项目周期。
1.2.2SpringBoot特点
- 为基于Spring的开发提供更快的入门体验
- 开箱即用,没有代码生成,也无需XML配置。同时也可以修改默认值来满足特定的需求
- 提供了一些大型项目中常见的非功能性特性,如嵌入式服务器、安全、指标,健康检测、外部配置等
- SpringBoot不是对Spring功能上的增强,而是提供了一种快速使用Spring的方式
1.2.3SpringBoot核心功能
- 起步依赖:
- 起步依赖本质上是一个Maven项目对象模型(Project Object Model,POM),定义了对其他库的传递依赖,这些东西加在一起即支持某项功能。
- 简单的说,起步依赖就是将具备某种功能的坐标打包到一起,并提供一些默认的功能。
- 自动配置 :
- Spring Boot的自动配置是一个运行时(更准确地说,是应用程序启动时)的过程,考虑了众多因素,才决定Spring配置应该用哪个,不该用哪个。该过程是Spring自动完成的。
2.SpringBoot快速入门
2.1代码实现
2.1.1创建Maven工程
- 使用idea工具创建一个maven工程,该工程为普通的java工程
2.1.2添加SpringBoot的起步依赖
- SpringBoot要求:项目要继承SpringBoot的起步依赖spring-boot-starter-parent
xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.1.RELEASE</version>
</parent>
SpringBoot要集成SpringMVC进行Controller的开发,所以项目要导入web的启动依赖:
xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
2.1.3编写SpringBoot引导类
java
@SpringBootApplication
public class MySpringBootApplication {
public static void main(String[] args) {
SpringApplication.run(MySpringBootApplication.class);
}
}
2.1.4编写controller
java
@Controller
public class QuickStartController {
@RequestMapping("/quick")
@ResponseBody
public String quick(){
return "springboot 访问成功!";
}
}
2.1.5测试
执行SpringBoot起步类的主方法,控制台打印日志如下:
通过日志发现,Tomcat started on port(s): 8080 (http) with context path ''
tomcat已经起步,端口监听8080,web应用的虚拟工程名称为空
打开浏览器访问url地址为:http://localhost:8080/quick
2.2快速入门解析
2.2.1引导类解析
- @SpringBootApplication:标注SpringBoot的启动类,该注解具备多种功能(后面详细剖析)
- SpringApplication.run(MySpringBootApplication.class) 代表运行SpringBoot的启动类,参数为SpringBoot启动类的字节码对象
2.2.2工程热部署解析
可以在修改代码后不重启就能生效,在 pom.xml 中添加如下配置就可以实现这样的功能,我们称之为热部署:
xml
<!--热部署配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
注意:IDEA进行SpringBoot热部署失败原因
出现这种情况,并不是热部署配置问题,其根本原因是因为Intellij IEDA默认情况下不会自动编译,需要对IDEA进行自动编译的设置,如下:
- 然后 Shift+Ctrl+Alt+/,选择Registry
2.3使用Idea快速创建SpringBoot项目(自动导入依赖)
- 通过idea快速创建的SpringBoot项目的pom.xml中已经导入了我们选择的web的起步依赖的坐标
java
<?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>com.itheima</groupId>
<artifactId>springboot_quick2</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>springboot_quick2</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>9</java.version>
</properties>
<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>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
可以使用快速入门的方式创建Controller进行访问,此处不再赘述
3.SpringBoot原理分析
3.1起步依赖原理分析
3.1.1分析spring-boot-starter-parent
按住Ctrl点击pom.xml中的spring-boot-starter-parent,跳转到了spring-boot-starter-parent的pom.xml,xml配置如下(只摘抄了部分重点配置):
xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.0.1.RELEASE</version>
<relativePath>../../spring-boot-dependencies</relativePath>
</parent>
按住Ctrl点击pom.xml中的spring-boot-starter-dependencies,跳转到了spring-boot-starter-dependencies的pom.xml,xml配置如下(只摘抄了部分重点配置):
xml
<properties>
<activemq.version>5.15.3</activemq.version>
<antlr2.version>2.7.7</antlr2.version>
<appengine-sdk.version>1.9.63</appengine-sdk.version>
<artemis.version>2.4.0</artemis.version>
<aspectj.version>1.8.13</aspectj.version>
<assertj.version>3.9.1</assertj.version>
<atomikos.version>4.0.6</atomikos.version>
<bitronix.version>2.1.4</bitronix.version>
<build-helper-maven-plugin.version>3.0.0</build-helper-maven-plugin.version>
<byte-buddy.version>1.7.11</byte-buddy.version>
... ... ...
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot</artifactId>
<version>2.0.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
<version>2.0.1.RELEASE</version>
</dependency>
... ... ...
</dependencies>
</dependencyManagement>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>${kotlin.version}</version>
</plugin>
<plugin>
<groupId>org.jooq</groupId>
<artifactId>jooq-codegen-maven</artifactId>
<version>${jooq.version}</version>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.0.1.RELEASE</version>
</plugin>
... ... ...
</plugins>
</pluginManagement>
</build>
从上面的spring-boot-starter-dependencies的pom.xml中我们可以发现,一部分坐标的版本、依赖管理、插件管理已经定义好,所以我们的SpringBoot工程继承spring-boot-starter-parent后已经具备版本锁定等配置了。所以起步依赖的作用就是进行依赖的传递。
3.1.2分析spring-boot-starter-web
按住Ctrl点击pom.xml中的spring-boot-starter-web,跳转到了spring-boot-starter-web的pom.xml,xml配置如下(只摘抄了部分重点配置):
xml
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starters</artifactId>
<version>2.0.1.RELEASE</version>
</parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.0.1.RELEASE</version>
<name>Spring Boot Web Starter</name>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.0.1.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
<version>2.0.1.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<version>2.0.1.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.9.Final</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.0.5.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.0.5.RELEASE</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
从上面的spring-boot-starter-web的pom.xml中我们可以发现,spring-boot-starter-web就是将web开发要使用的spring-web、spring-webmvc等坐标进行了"打包",这样我们的工程只要引入spring-boot-starter-web起步依赖的坐标就可以进行web开发了,同样体现了依赖传递的作用
3.2自动配置原理解析
- 按住Ctrl点击查看启动类MySpringBootApplication上的注解@SpringBootApplication
java
@SpringBootApplication
public class MySpringBootApplication {
public static void main(String[] args) {
SpringApplication.run(MySpringBootApplication.class);
}
}
- 注解@SpringBootApplication的源码
java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
/**
* Exclude specific auto-configuration classes such that they will never be applied.
* @return the classes to exclude
*/
@AliasFor(annotation = EnableAutoConfiguration.class)
Class<?>[] exclude() default {};
... ... ...
}
其中有三个核心注解:
- @SpringBootConfiguration:等同与@Configuration,既标注该类是Spring的一个配置类
- @EnableAutoConfiguration:SpringBoot自动配置功能开启
- @ComponentScan:组件扫描,默认扫描当前引导类所在包及其子包
按住Ctrl点击查看注解@EnableAutoConfiguration:
java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
... ... ...
}
其中,@Import(AutoConfigurationImportSelector.class) 导入了AutoConfigurationImportSelector类
按住Ctrl点击查看AutoConfigurationImportSelector源码
java
public String[] selectImports(AnnotationMetadata annotationMetadata) {
... ... ...
List<String> configurations = getCandidateConfigurations(annotationMetadata,
attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return StringUtils.toStringArray(configurations);
}
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
return configurations;
}
其中,SpringFactoriesLoader.loadFactoryNames 方法的作用就是从META-INF/spring.factories文件中读取指定类对应的类名称列表:
该spring.factories文件格式为键值对,键为自动配置类全类名,值(多个),为该自动配置类所对应的配置类的全类名
- 这些配置类定义的Bean会根据条件注解所指定的条件判断是否将其注入到spring容器中
- 条件判断注解(举例)
- @ConditionalOnClass:类加载器中是否存在对应的类(引入jar包一般就会有对应的类,就会将该配置类 里面的bean注入到spring容器中)
- @ConditionalOnBean:容器中是否存在对应的bean
4.SpringBoot的配置文件
4.1SpringBoot配置文件的类型及作用
4.1.1配置文件类型及作用
如果想使用自己的配置替换默认配置的话,就可以使用application.properties或者application.yml(application.yaml)进行配置。
SpringBoot默认会从Resources目录下加载==application.properties或application.yml(application.yaml)==文件
其中,application.properties文件是键值对类型的文件,之前一直在使用,所以此处不在对properties文件的格式进行阐述。除了properties文件外,SpringBoot还可以使用yml文件进行配置,下面对yml文件进行讲解。
4.1.2 application.properties
properties
# 数据库连接配置
spring.datasource.url=jdbc:mysql://localhost:3306/mydatabase
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
4.1.3 application.yml或application.yaml
yaml
# 数据库连接配置
spring:
datasource:
url: jdbc:mysql://localhost:3306/mydatabase
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
4.1.4读取配置文件数据
- application.yml
yaml
person:
name: zhangsan
age: 18
- 读取
java
@Value("${person.name}")
private String name;
@Value("${person.age}")
private Integer age;
4.2配置文件的加载顺序
4.2.1项目内配置文件加载顺序
(1)不同目录的配置文件的加载顺序
-
Springboot程序启动时,会从以下位置加载配置文件:优先级由高到底,高优先级的配置会覆盖低优先级的配置,没有的配置进行互补配置
优先级1:项目路径下的config文件夹配置文件
优先级2:项目的根目录下面配置文件
优先级3:资源路径下的config文件夹配置文件
优先级4:资源路径下配置文件
(2)同一目录、不同后缀的配置文件加载顺序
当properties、yaml和yml三种文件路径相同时,三个文件中的配置信息都会生效,但是当三个文件中有配置信息冲突时,加载顺序是
优先级低的配置会被先加载,所以优先级高的配置会覆盖优先级低的配置。
properties(最高)> yml > yaml(最低)
- 验证:
4.2.2外部配置文件
当我们把项目打包后,如何在配置SpringBoot项目呢?
(1)命令行参数
项目打包好以后,我们可以使用命令行参数的形式,来改变想改变的几个参数,直接在启动命令后添加启动参数,如果有多个配置项,可以用空格分开。
java -jar springboot-configuration.jar --server.port=8088 --server.servlet.context-path=/spring
(2)spirng.config.location
在第一种情况下,如果参数数量过多,我们就要考虑配置文件了,我们在启动项目的时候可以用spring.config.location
来指定配置文件的新位置。指定的配置文件和jar包中默认加载的配置文件共同起作用形成互补配置。
指定配置文件从F盘下读取
java -jar springboot-configuration.jar --spring.config.location=F:/application.properties
4.3多环境配置
4.3.1多文件配置多个环境
- 环境区分:
dev:开发环境
test:测试环境
prod:生产环境(线上)
- 三个环境配置文件
application-dev.yml
application-test.yml
application-prod.yml
- 指定文件生效:在application.yml指定
yaml
spring:
profiles:
active:dev
4.3.2单个文件配置多个环境
不需要创建多个文件来区分了,直接以 三个横杠 来当做一个配置文件环境。
以下案例就是分为了两个环境,然后最上方active来指定对应的profiles环境
yaml
spring:
profiles:
active: prod
---
server:
port: 8083
spring:
profiles: dev
---
server:
port: 8084
spring:
profiles: prod
4.3.3补充:命令行指定
java -jar spring-boot-02-config-0.0.1-SNAPSHOT.jar --spring.profiles.active=dev;
5.SpringBoot整合其他技术
5.1SpringBoot整合Junit
5.1.1添加依赖
xml
<!--测试的起步依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
5.1.2编写测试类
java
@RunWith(SpringRunner.class)
@SpringBootTest(classes = MySpringBootApplication.class)
public class MapperTest {
@Autowired
private UserMapper userMapper;
@Test
public void test() {
List<User> users = userMapper.queryUserList();
System.out.println(users);
}
}
其中,SpringRunner继承自SpringJUnit4ClassRunner,使用哪一个Spring提供的测试测试引擎都可以
java
public final class SpringRunner extends SpringJUnit4ClassRunner
@SpringBootTest的属性指定的是引导类的字节码对象
5.1.3控制台
5.2SpringBoot整合Mybatis
5.2.1添加依赖
xml
<!--mybatis起步依赖-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.1.1</version>
</dependency>
5.2.2添加数据库驱动
xml
<!-- MySQL连接驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
5.2.3添加数据库连接
yaml
spring:
datasource:
url: jdbc:mysql://localhost:3306/mydatabase
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
5.2.4创建user表
在test数据库中创建user表
sql
-- ----------------------------
-- Table structure for `user`
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(50) DEFAULT NULL,
`password` varchar(50) DEFAULT NULL,
`name` varchar(50) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES ('1', 'zhangsan', '123', '张三');
INSERT INTO `user` VALUES ('2', 'lisi', '123', '李四');
5.2.5创建实体Bean
java
public class User {
// 主键
private Long id;
// 用户名
private String username;
// 密码
private String password;
// 姓名
private String name;
//此处省略getter和setter方法 .. ..
}
5.2.6编写Mapper接口
java
@Mapper
public interface UserMapper {
public List<User> queryUserList();
}
5.2.7配置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="com.itheima.mapper.UserMapper">
<select id="queryUserList" resultType="user">
select * from user
</select>
</mapper>
5.2.8在application.properties添加映射文件信息
xml
#spring集成Mybatis环境
#pojo别名扫描包
mybatis.type-aliases-package=com.itheima.domain
#加载Mybatis映射文件
mybatis.mapper-locations=classpath:mapper/*Mapper.xml
5.2.9controller层
java
@Controller
public class MapperController {
@Autowired
private UserMapper userMapper;
@RequestMapping("/queryUser")
@ResponseBody
public List<User> queryUser(){
List<User> users = userMapper.queryUserList();
return users;
}
}
5.2.10测试
5.3SpringBoot整合Redis
5.4SpringBoot整合Mybatis Plus
5.4.1导入依赖
xml
<!-- mybatisPlus 核心库 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
5.4.2yml配置
yaml
server:
port: 10100 # 配置启动端口号
mybatis:
config-location: classpath:mybatis.cfg.xml # mybatis主配置文件所在路径
type-aliases-package: com.demo.drools.entity # 定义所有操作类的别名所在包
mapper-locations: # 所有的mapper映射文件
- classpath:mapper/*.xml
spring: #springboot的配置
datasource: #定义数据源
#127.0.0.1为本机测试的ip,3306是mysql的端口号。serverTimezone是定义时区,照抄就好,mysql高版本需要定义这些东西
#useSSL也是某些高版本mysql需要问有没有用SSL连接
url: jdbc:mysql://127.0.0.1:3306/test?serverTimezone=GMT%2B8&useSSL=FALSE
username: root #数据库用户名,root为管理员
password: 123456 #该数据库用户的密码
# 使用druid数据源
type: com.alibaba.druid.pool.DruidDataSource
# mybatis-plus相关配置
mybatis-plus:
# xml扫描,多个目录用逗号或者分号分隔(告诉 Mapper 所对应的 XML 文件位置)
mapper-locations: classpath:mapper/*.xml
# 以下配置均有默认值,可以不设置
global-config:
db-config:
#主键类型 AUTO:"数据库ID自增" INPUT:"用户输入ID",ID_WORKER:"全局唯一ID (数字类型唯一ID)", UUID:"全局唯一ID UUID";
id-type: auto
#字段策略 IGNORED:"忽略判断" NOT_NULL:"非 NULL 判断") NOT_EMPTY:"非空判断"
field-strategy: NOT_EMPTY
#数据库类型
db-type: MYSQL
configuration:
# 是否开启自动驼峰命名规则映射:从数据库列名到Java属性驼峰命名的类似映射
map-underscore-to-camel-case: true
# 如果查询结果中包含空值的列,则 MyBatis 在映射的时候,不会映射这个字段
call-setters-on-nulls: true
# 这个配置会将执行的sql打印出来,在开发或测试的时候可以用
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
5.4.3用户信息实体
java
@Data
@TableName("user_info")//@TableName中的值对应着表名
public class UserInfoEntity {
/**
* 主键
* @TableId中可以决定主键的类型,不写会采取默认值,默认值可以在yml中配置
* AUTO: 数据库ID自增
* INPUT: 用户输入ID
* ID_WORKER: 全局唯一ID,Long类型的主键
* ID_WORKER_STR: 字符串全局唯一ID
* UUID: 全局唯一ID,UUID类型的主键
* NONE: 该类型为未设置主键类型
*/
@TableId(type = IdType.AUTO)
private Long id;
/**
* 姓名
*/
private String name;
/**
* 年龄
*/
private Integer age;
/**
* 技能
*/
private String skill;
/**
* 评价
*/
private String evaluate;
/**
* 分数
*/
private Long fraction;
}
5.4.4配置类
java
public class MybatisPlusConfig {
/**
* mybatis-plus SQL执行效率插件【生产环境可以关闭】
*/
@Bean
public PerformanceInterceptor performanceInterceptor() {
return new PerformanceInterceptor();
}
/**
* 分页插件
*/
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
}
5.4.5启动类
java
@SpringBootApplication
//@MapperScan和dao层添加@Mapper注解意思一样
@MapperScan(basePackages = "com.demo.drools.dao")
public class DroolsApplication {
public static void main(String[] args) {
SpringApplication.run(DroolsApplication.class, args);
}
}
5.4.6Dao层
java
@Mapper
public interface UserInfoDao extends BaseMapper<UserInfoEntity> {
}
5.4.7Service层
java
public interface UserInfoService extends IService<UserInfoEntity> {
}
java
@Service
public class UserInfoSerivceImpl extends ServiceImpl<UserInfoDao, UserInfoEntity> implements UserInfoService {
}
5.4.8扩展:Mybatis Plus的核心
Mybatis Plus的核心为QueryWrapper、UpdateWrapper
- QueryWrapper : Entity 对象封装操作
- UpdateWrapper : Update 条件封装,用于Entity对象更新操作
- 条件构造器使用中的各个方法格式和说明
5.5SpringBoot整合RabbitMQ
6.SpringBoot实现拦截器
6.1拦截器介绍
拦截器(Interceptor)同 Filter 过滤器一样,它俩都是面向切面编程------AOP 的具体实现(AOP切面编程只是一种编程思想而已)。
你可以使用 Interceptor 来执行某些任务,例如在 Controller 处理请求之前编写日志,添加或更新配置...
在 Spring中 ,当请求发送到 Controller 时,在被Controller 处理之前,它必须经过 Interceptors(0或多个)。
6.2拦截器作用
- 日志记录:记录请求信息的日志,以便进行信息监控、信息统计、计算 PV(Page View)等;
- 权限检查:如登录检测,进入处理器检测是否登录;
- 性能监控:通过拦截器在进入处理器之前记录开始时间,在处理完后记录结束时间,从而得到该请求的处理时间。(反向代理,如 Apache 也可以自动记录)
- 通用行为:读取 Cookie 得到用户信息并将用户对象放入请求,从而方便后续流程使用,还有如提取 Locale、Theme 信息等,只要是多个处理器都需要的即可使用拦截器实现。
6.3自定义拦截器
如果你需要自定义 Interceptor 的话必须实现 org.springframework.web.servlet.HandlerInterceptor接口或继承 org.springframework.web.servlet.handler.HandlerInterceptorAdapter类,并且需要重写下面下面 3 个方法:
- preHandler(HttpServletRequest request, HttpServletResponse response, Object handler) 方法在请求处理之前被调用。该方法在 Interceptor 类中最先执行,用来进行一些前置初始化操作或是对当前请求做预处理,也可以进行一些判断来决定请求是否要继续进行下去。该方法的返回至是 Boolean 类型,当它返回 false 时,表示请求结束,后续的 Interceptor 和 Controller 都不会再执行;当它返回为 true 时会继续调用下一个 Interceptor 的 preHandle 方法,如果已经是最后一个 Interceptor 的时候就会调用当前请求的 Controller 方法。
- postHandler(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) 方法在当前请求处理完成之后,也就是 Controller 方法调用之后执行,但是它会在 DispatcherServlet 进行视图返回渲染之前被调用,所以我们可以在这个方法中对 Controller 处理之后的 ModelAndView 对象进行操作。
- afterCompletion( HttpServletRequest request, HttpServletResponse response, Object handle, Exception ex) 方法需要在当前对应的 Interceptor 类的 postHandler 方法返回值为 true 时才会执行。顾名思义,该方法将在整个请求结束之后,也就是在 DispatcherServlet 渲染了对应的视图之后执行。此方法主要用来进行资源清理。
6.3.1LogInterceptor类
java
public class LogInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
long startTime = System.currentTimeMillis();
System.out.println("\n-------- LogInterception.preHandle --- ");
System.out.println("Request URL: " + request.getRequestURL());
System.out.println("Start Time: " + System.currentTimeMillis());
request.setAttribute("startTime", startTime);
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("\n-------- LogInterception.postHandle --- ");
System.out.println("Request URL: " + request.getRequestURL());
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("\n-------- LogInterception.afterCompletion --- ");
long startTime = (Long) request.getAttribute("startTime");
long endTime = System.currentTimeMillis();
System.out.println("Request URL: " + request.getRequestURL());
System.out.println("End Time: " + endTime);
System.out.println("Time Taken: " + (endTime - startTime));
}
}
6.3.2OldLoginInterceptor类
java
public class OldLoginInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("\n-------- OldLoginInterceptor.preHandle --- ");
System.out.println("Request URL: " + request.getRequestURL());
System.out.println("Sorry! This URL is no longer used, Redirect to /admin/login");
response.sendRedirect(request.getContextPath()+ "/admin/login");
return false;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("\n-------- OldLoginInterceptor.postHandle --- ");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("\n-------- OldLoginInterceptor.afterCompletion --- ");
}
}
6.3.3配置拦截器
java
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LogInterceptor());
registry.addInterceptor(new OldLoginInterceptor()).addPathPatterns("/admin/oldLogin");
registry.addInterceptor(new AdminInterceptor()).addPathPatterns("/admin/*").excludePathPatterns("/admin/oldLogin");
}
}
- LogInterceptor 拦截器用于拦截所有请求; OldLoginInterceptor 用来拦截链接 " / admin / oldLogin",它将重定向到新的 " / admin / login"。;AdminInterceptor用来拦截链接 "/admin/*",除了链接 " / admin / oldLogin"。
6.4应用
6.4.1性能监控
如记录一下请求的处理时间,得到一些慢请求(如处理时间超过500毫秒),从而进行性能改进,一般的反向代理服务器如 apache 都具有这个功能,但此处我们演示一下使用拦截器怎么实现。
-
实现分析
- 在进入处理器之前记录开始时间,即在拦截器的 preHandle 记录开始时间;
- 在结束请求处理之后记录结束时间,即在拦截器的 afterCompletion 记录结束实现,并用结束时间-开始时间得到这次请求的处理时间
-
问题:
-
我们的拦截器是单例,因此不管用户请求多少次都只有一个拦截器实现,即 线程不安全,那我们应该怎么记录时间呢?
-
解决方案是使用 ThreadLocal,它是线程绑定的变量,提供线程局部变量(一个线程一个 ThreadLocal,A线程的ThreadLocal 只能看到A线程的 ThreadLocal,不能看到B线程的 ThreadLocal)。
-
代码实现:
javapublic class StopWatchHandlerInterceptor extends HandlerInterceptorAdapter { private NamedThreadLocal<Long> startTimeThreadLocal = new NamedThreadLocal<>("StopWatch-StartTime"); private Logger logger = LoggerFactory.getLogger(StopWatchHandlerInterceptor.class); @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { long beginTime = System.currentTimeMillis();//1、开始时间 startTimeThreadLocal.set(beginTime);//线程绑定变量(该数据只有当前请求的线程可见) return true;//继续流程 } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { long endTime = System.currentTimeMillis();//2、结束时间 long beginTime = startTimeThreadLocal.get();//得到线程绑定的局部变量(开始时间) long consumeTime = endTime - beginTime;//3、消耗的时间 if(consumeTime > 500) {//此处认为处理时间超过500毫秒的请求为慢请求 //TODO 记录到日志文件 logger.info(String.format("%s consume %d millis", request.getRequestURI(), consumeTime)); } //测试的时候由于请求时间未超过500,所以启用该代码 // logger.info(String.format("%s consume %d millis", request.getRequestURI(), consumeTime)); } }
NamedThreadLocal:Spring提供的一个命名的ThreadLocal实现。
在测试时需要把 stopWatchHandlerInterceptor 放在拦截器链的第一个,这样得到的时间才是比较准确的。
-
拦截器配置类
java@Configuration public class WebConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new StopWatchHandlerInterceptor()); registry.addInterceptor(new OldLoginInterceptor()).addPathPatterns("/admin/oldLogin"); } }
-
6.4.2登录检测
在访问某些资源时(如订单页面),需要用户登录后才能查看,因此需要进行登录检测。
-
流程
- 访问需要登录的资源时,由拦截器重定向到登录页面;
- 如果访问的是登录页面,拦截器不应该拦截;
- 用户登录成功后,往 cookie/session 添加登录成功的标识(如用户编号);
- 下次请求时,拦截器通过判断 cookie/session 中是否有该标识来决定继续流程还是到登录页面;
- 在此拦截器还应该允许游客访问的资源。
-
代码实现
javapublic class MyInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { boolean flag = true; String ip = request.getRemoteAddr(); long startTime = System.currentTimeMillis(); request.setAttribute("requestStartTime", startTime); if (handler instanceof ResourceHttpRequestHandler) { System.out.println("preHandle这是一个静态资源方法!"); } else if (handler instanceof HandlerMethod) { HandlerMethod handlerMethod = (HandlerMethod) handler; Method method = handlerMethod.getMethod(); System.out.println("用户:" + ip + ",访问目标:" + method.getDeclaringClass().getName() + "." + method.getName()); } //如果用户未登录 User user = (User) request.getSession().getAttribute("user"); if (null == user) { //重定向到登录页面 response.sendRedirect("toLogin"); flag = false; } return flag; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { if (handler instanceof ResourceHttpRequestHandler) { System.out.println("postHandle这是一个静态资源方法!"); } else if (handler instanceof HandlerMethod) { HandlerMethod handlerMethod = (HandlerMethod) handler; Method method = handlerMethod.getMethod(); long startTime = (long) request.getAttribute("requestStartTime"); long endTime = System.currentTimeMillis(); long executeTime = endTime - startTime; int time = 1000; //打印方法执行时间 if (executeTime > time) { System.out.println("[" + method.getDeclaringClass().getName() + "." + method.getName() + "] 执行耗时 : " + executeTime + "ms"); } else { System.out.println("[" + method.getDeclaringClass().getSimpleName() + "." + method.getName() + "] 执行耗时 : " + executeTime + "ms"); } } } }
7.文件上传/下载
7.1文件上传/下载流程概述
7.1.1文件上传
- 当客户端发送文件上传请求时,Spring Boot会接收到一个包含文件的MultipartHttpServletRequest对象。
- 在控制器方法中,可以通过参数接收这个MultipartHttpServletRequest对象,并从中获取上传的文件。
- Spring Boot会将上传的文件存储到临时目录中,可以通过MultipartFile对象的getOriginalFilename()方法获取文件名,通过getBytes()方法获取文件内容。
- 文件上传后,可以将其保存到服务器的持久化存储中,例如本地磁盘、云存储等。
7.1.2文件下载
- 当客户端发送文件下载请求时,Spring Boot会根据请求的URL找到对应的文件。
- 找到文件后,需要将文件的内容写入到Response的输出流中,为了防止浏览器解析,需要在响应头中设置正确的MIME类型(Content-Type)。
- 如果要实现断点续传功能,需要根据文件的元数据信息判断是否已经下载过该文件,如果已经下载过,则直接返回已存在的文件内容即可。
7.2实现过程
7.2.1添加依赖
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
7.2.2配置文件上传大小限制
yaml
server:
port: 18080
spring:
servlet:
multipart:
max-file-size: 10MB
max-request-size: 10MB
7.2.3上传控制器
- 创建一个控制器类,用于处理文件上传请求。在这个类中,使用
@PostMapping
注解指定处理POST请求的方法,并使用@RequestParam("file") MultipartFile file
参数接收上传的文件。
java
// 创建文件上传控制器
@RestController
@RequestMapping("/api/upload")
public class FileUploadController {
// 处理文件上传请求的POST方法
@PostMapping("/")
public ResponseEntity<String> handleFileUpload(@RequestParam("file") MultipartFile file) {
try {
// 获取上传文件的文件名
String fileName = file.getOriginalFilename();
// 将文件保存到磁盘或执行其他操作,这里只是简单地将文件保存到静态资源目录下
file.transferTo(new File("D:/" + fileName));
return new ResponseEntity<>("文件上传成功!", HttpStatus.OK);
} catch (Exception e) {
return new ResponseEntity<>("文件上传失败:" + e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
}
}
}
7.2.4下载控制器
- 创建一个控制器类,用于处理文件下载请求。在这个类中,我们将使用@GetMapping注解指定处理GET请求的方法,并使用@RequestParam("filename") String fileName参数接收要下载的文件名。然后,我们可以使用文件名来获取要下载的文件,并将其作为响应返回给客户端。
- 为了让Spring Boot能够找到静态资源(如文件),你需要在
src/main/resources
目录下创建一个名为static
的文件夹,并在其中创建一个名为files
的文件夹,用于存放要下载的文件。
java
@RestController
@RequestMapping("/api/download")
public class FileDownloadController {
private static final Logger log = LoggerFactory.getLogger(FileDownloadController.class);
@Autowired
private ResourceLoader resourceLoader;
// 处理文件下载请求的GET方法,通过文件名获取文件并返回给客户端下载
@GetMapping("/{filename:.+}")
public ResponseEntity<Resource> handleFileDownload(@PathVariable String filename) throws IOException {
// 获取要下载的文件的Resource对象,这里假设文件保存在静态资源目录下的files文件夹中
Resource resource = resourceLoader.getResource("classpath:static/files/" + filename);
if (resource == null) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
// 将文件内容包装为响应体,并设置响应头信息,提示浏览器下载文件而不是打开文件
InputStreamResource inputStreamResource = new InputStreamResource(resource.getInputStream());
HttpHeaders headers = new HttpHeaders();
headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + filename);
return ResponseEntity.ok()
.headers(headers)
.contentType(MediaTypeFactory.getMediaType(resource).get())
.body(inputStreamResource);
}
}
8.SpringBoot访问静态资源
8.1何为静态资源?
静态资源,一般是网页端的:HTML文件、JavaScript文件和图片 。尤其是设置图片的静态资源,尤其重要:
8.2设置访问静态资源的两种方法
- Springboot内设置静态资源,或者说静态资源文件夹,主要有两种方法(均为SpringMVC实现):
- 在
application.yml
/application.properties
内配置。 - 设置
Configuration配置类
。
- 在
以上两种方法,均可实现用户访问网址,不走Controller层的拦截,直接进行静态文件访问。
8.2.1application设置方法
(1)配置详讲
-
spring.mvc.static-path-pattern
:根据官网的描述和实际效果,可以理解为**静态文件URL匹配头**,也就是静态文件的URL地址开头。Springboot默认为:/**
。 -
spring.web.resources.static-locations
:根据官网的描述和实际效果,可以理解为==实际静态文件地址 ==,也就是静态文件URL后,匹配的实际静态文件。Springboot默认为: classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/
-
注意:
- spring.web.resources.static-locations是后续配置,旧版Springboot的配置项为:spring-resources-static-locations;在2.2.5版本之后,旧版本配置已经失效。
- spring.web.resources.static-locations有多个配置项,在Springboot编译后,会合并为一个文件。多个配置文件,使用,进行分割。
- spring.web.resources.static-location仅仅允许一个配置,无法使用,进行分割,如果需要多个静态资源文件,可以使用下文的配置类方法。
- spring.web.resources.static-locations可以使用classpath、file进行匹配。如果使用file,这个时候的相对路径为项目地址(打包为.jar后,相对路径就是.jar运行地址)。
(2)实践
-
最终效果:
-
浏览器输入:
http://localhost:8088/SystemData/UserData/Avatar/Mintimate.jpeg
-
可以直接访问项目文件下的:
/SystemData/UserData/Avatar/Mintimate.jpeg
-
-
配置文件:
yaml
spring:
mvc:
# URL响应地址(Springboot默认为/**)
static-path-pattern: /SystemData/**
web:
resources:
# 静态文件地址,保留官方内容后,进行追加
static-locations: classpath:/static,classpath:/public,classpath:/resources,classpath:/META-INF/resources,file:SystemData
其中,file:SystemData
就是映射本地文件了。
(3)优缺点
- 优点:简单粗暴
- 缺点:
- URL响应地址只能为一项,也就是
spring.mvc.static-path-pattern
配置只能写一项 - 上文设置了
/SystemData/**
为URL匹配,就不能设置第二个/resources/**
这样的配置为第二静态目录 - 如果需要设置多个地址为静态资源目录,可以参考下文的
设置配置类方法
方法。
- URL响应地址只能为一项,也就是
8.2.2设置配置类方法
(1)方法介绍
写一个配置类,实现静态资源的文件夹方法很多。比如:
-
继承于
WebMvcConfigurationSupport
父类,并实现addResourceHandlers
方法。java@Override protected void addResourceHandlers(ResourceHandlerRegistry registry) { }
这里的
registry
使用链式编程,方法为:addResourceHandler
:添加URL响应地址目录。addResourceLocations
:添加实际资源目录。
-
引用
WebMvcConfigurer
接口,并实现addInterceptors
方法(常用)
一些文章可能会让你继承于
WebMvcConfigurerAdapter
方法,但是实际上WebMvcConfigurerAdapter
方法在Spring5.0和Springboot2.0之后,已经弃用。
(2)实践
-
最终效果1:
- 浏览器输入:
http://localhost:8088/SystemData/UserData/Avatar/Mintimate.jpeg
- 可以直接访问项目文件下的:
/SystemData/UserData/Avatar/Mintimate.jpeg
,
- 浏览器输入:
-
最终效果2:
- 浏览器输入:
http://localhost:8088/SystemDataTest/UserData/Avatar/Mintimate.jpeg
- 可以直接访问项目文件下的:
/Test/UserData/Avatar/Demo.jpeg
,
- 浏览器输入:
-
添加一个配置类,并继承
WebMvcConfigurationSupport
,实现addResourceHandlers
方法,并打上@Configuration
注解,使其成为配置类:
java
@Configuration
public class WebConfig extends WebMvcConfigurationSupport{
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
//定位到项目文件夹下的SystemData文件夹
static final String IMG_PATH=System.getProperty("user.dir")+"/SystemData/";
static final String IMG_PATH_TWO=System.getProperty("user.dir")+"/Test/";
registry.addResourceHandler("/SystemData/**)")
.addResourceLocations("file:"IMG_PATH);
registry.addResourceHandler("/SystemDataTest/**)")
.addResourceLocations("file:"IMG_PATH_TWO);
super.addResourceHandlers(registry);
}
}
(3)优缺点
- 相比前文,这样的配置更麻烦。
- 相比前文,这样的可塑性更高:可以添加更多的映射、不会对默认配置造成覆盖等。