SpringBoot的使用
在使用SpringBoot
之前,需要学习Spring
和SpringMVC
,这两个框架的使用,需要编写大量的配置文件,文件越多使用就越麻烦,以及需要集成第三方框架时也非常繁琐。Spring-boot
就解决了这个问题,提供了一套默认的配置,极少的编写配置文件,让开发者专心编写业务。比如使用Mybatis
,直接集成依赖即可使用,可以理解为一个更快速的Spring
和SpringMVC
,开发效率更高,有如下特点:
- 可创建
Spring
应用 - 内嵌
Tomcat
等服务器,不需要额外安装 - 提供了
starter
起步依赖,简化应用配置,相当于有默认的配置,比如使用Mybatis
,需要在Spring
中配置相关对象,在Springboot
中,只需要在pom.xml
中添加mybatis-spring-boot-starter
,相关的依赖就自动配置好了 - 尽可能配置
Spring
和第三方库
如何创建一个Springboot
项目
创建Springboot的两种方式
Idea
提供了Spring
的初始化容器创建Springboot
需要联网,默认的地址是https://start.spring.io
,网络遇到问题可以改成https://start.aliyun.com
创建完成后就是一个标准可以运行的springboot
项目
- 创建一个
Maven
工程,手动添加Springboot
依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.11</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
parent
声明这是一个父子结构的项目,父pom
中声明了很多配置,如下面的依赖和插件无需写版本号的原因就是因为这个父pom
管理了spring-boot-starter-web
是官方提供的web
启动器,里面包含很多常规依赖包build
标签中的插件spring-boot-maven-plugin
提供了打包成可执行的springboot
项目的能力
package cn.mgl;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
@SpringBootApplication
public class MainApplication {
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class);
// 获取容器中所有bean的名称并输出
for (int i = 0; i < run.getBeanDefinitionNames().length; i++) {
System.out.println(run.getBeanDefinitionNames()[i]);
}
}
}
定义一个接口
package cn.mgl.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
@GetMapping("/getSomething")
public String getSomething(){
return "来自Controller";
}
}
此时执行项目 访问http://localhost:8080/getSomething
即可访问到接口
执行mvn package
即可打成一个可执行的jar
包,通过java -jar xx.jar
运行
如果要修改被管理的依赖版本
- 可以进入父
pom
查找对应的版本配置Properties
,找到目标依赖的版本配置项,在开发项目(当前项目)的pom
中更改掉目标版本号,类似于Java
中的重写 - 在声明依赖时不要忽略版本号,把
version
也写上
JavaConfig--@Confiuration
Spring
提供的使用Java
类来配置容器,优点是避免的繁琐的xml
配置文件,以及可以用面向对象的方式编写,最终将对象注入到容器当中。
使用两个注解
@Configuration
:用于类上,表示这个类作为配置文件使用@Bean
:声明对象,把对象注入到容器当中
package cn.mgl.config;
import cn.mgl.vo.Student;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Configuration:表示当前类是作为配置类,荣来配置容器的,相当于beans.xml
*/
@Configuration
public class SpringConfig {
/**
* 创建Bean的方法,在方法上加上Bean注解,返回值会注入到容器当中,不指定名称时方法名字就是bean的名字
* @return Student
*/
@Bean
public Student student() {
Student student = new Student();
student.setAge(1);
student.setName("就上课了绝对是李开复");
student.setId(1000L);
return student;
}
}
使用这个Bean
@Test
public void TestA() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
Student student = (Student) ctx.getBean("student");
System.out.println(student);
}
默认情况下,Bean
注册到容器中是单例的
规则:
-
Springboot
会先加载所有自动配置类XXAutoConfiguration
-
每个自动配置类按照条件进行生效,默认会绑定配置文件指定的值,
xxProperties
中取值,xxProperties
和配置文件进行了绑定 -
生效的配置类就会给容器中装配很多组件,让开发者可以方便的使用
-
如果不满足,可以自己定制化一些配置
- 通过
@Configuration
标记的类中,手动的声明@Bean
替换掉容器自己装配的类,如@Bean
方法有参数,则会从容器当中取出并传入 - 开发者查看获取配置的文件相关值并手动的修改,如修改
Springboot
默认的启动端口号,在项目的配置文件中编写如下配置
- 通过
server:
port: 7777
此时再次启动项目,服务端口就改成了7777
@Import
作用是可以在当前配置类中导入其他配置类
- 先定义一个配置类
package cn.mgl.demo.config;
import org.springframework.context.annotation.Bean;
public class AConfig {
@Bean
public String testStr() {
return "testStr";
}
}
- 使用
@Import
进行导入将目标类的bean
成员复制到当前配置类中
@Configuration
@Import(AConfig.class)
public class WebConfig {
}
此时查看容器中所有的bean
,可以看到成功配置
配置文件
核心配置文件必须要用application
命名
有properties
和yml
两种格式的配置文件,本质上没有区别,如application.properteis
,application.yml
当同时存在这两种格式的配置文件,默认会使用properties
的,但现在主流是yml
多环境配置
环境一般有3种
- 开发环境:本机开发
- 测试环境:测试服务
- 发布环境:线上环境
他们之间一般会有配置的不同,比如数据库的链接地址,账号密码等等不同的配置信息。只需要创建多个配置文件,在不同场景读取不同的配置即可轻松完成切换
application-[环境名称].yml
application-dev.yml
application-pro.yml
application-test.yml
在主配置文件application.yml
中,配置active
的值,可选值为dev
| pro
| test
spring:
profiles:
active: test
此时就会读取application-test.yml
中的配置,相当于用配置文件配置配置文件,同时主配置文件中可以配置一些默认通用的配置,这样不需要在每个指定环境中进行重复的配置项
不同文件格式可以同时使用,但还是建议统一使用某一种
@Value
注解可以在Java
类中读取配置文件中的属性
name=lisi
使用上面示例的类来获取配置的值,其实使用${}
来包括配置的key
,后面接:
可以给默认值,当配置值找不到时就会使用默认值
public class AConfig {
@Value("${name:wangwu}")
private String name;
@Bean
public String testStr() {
return name + ":testStr";
}
}
简单理解自动装配
看看最核心的一个注解@SpringBootApplication
,实际他是一个复合注解
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
//...省略
}
这个注解有3
个核心注解
@SpringBootConfiguration
这个类表示当前类也是一个配置类,配置类怎么用的,那么在这个启动类上就可以怎么用
@Configuration
@Indexed
public @interface SpringBootConfiguration {
//...省略
}
@EnableAutoConfguration
启用自动配置,例如安装了mybatis
的starter
,有这个配置就会把mybatis
的对象创建好放入容器中@ComponentScan
作用是包扫描器,默认配置就是当前启动类所在包即其所有子包中有相关注解的类都会被扫描并注册到容器当中,所以当启动类位置不对时,就会出现自动装配失败问题,可以手动进行包扫描的路径修改
如果遇到,如何解决响应体内容乱码问题
- 通过添加一个自定义
Bean
且类型为FilterRegistrationBean
,并关掉Springboot
自带的CharacterEncodingFilter
@Configuration
public class WebConfig {
@Bean
public FilterRegistrationBean filterRegistrationBean() {
FilterRegistrationBean frb = new FilterRegistrationBean<>();
CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
characterEncodingFilter.setEncoding(StandardCharsets.UTF_8.toString());
characterEncodingFilter.setForceEncoding(true);
frb.setFilter(characterEncodingFilter);
frb.addUrlPatterns("/*");
return frb;
}
}
接着在application.[yml/properties]
中进行取消启用默认配置好的CharacterEncodingFilter
,乱码的原因是因为这个过滤器中的字符编码设置是ISO-8859-1
properties
写法
server.servlet.encoding.enabled=false
yml
写法
server:
servlet:
encoding:
enabled: false
二者效果等价
- 第一种方式的处理逻辑是不采用框架自带的字符编码过滤器,第二种方式更为简便,只需要通过配置修改掉字符编码为
UTF-8
server:
servlet:
encoding:
enabled: true
force: true
charset: utf-8
Springboot集成MyBatis
主流的框架一般都有提供启动器,可以找相关的starter
- 添加依赖,
mybatis
会自动配置如SessionFactory
等类对象。因为要链接数据库,所以Mysql
驱动类也要引入
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
- 在使用
@Mapper
相关注解时,需要在相同路径下,添加一个相同文件名的xml
文件进行SQL
的编写,此处不考虑@Select
等注解使用情况。需要将放java
代码的目录中的xml
文件添加到类路径当中,默认是不会添加的。
mapper
代码如下,需要添加一个@Mapper
注解告知Mybatis
需要为这个接口创建代理对象供后续调用,(User类自行创建)
package cn.mgl.demo.mapper;
import cn.mgl.demo.pojo.User;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface UserMapper {
public List<User> getAll();
}
为接口编写对应的sql
,namespace
和id
分别对应类名和方法名
<?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="cn.mgl.demo.mapper.UserMapper">
<select id="getAll" resultType="cn.mgl.demo.pojo.User">
select *
from t_user;
</select>
</mapper>
下面是在pom.xml
中进行配置。第一个resource
标签的作用是讲src/main/java
目录下所有xml
文件类型添加到类路径当中,默认是不处理的。第二个resource
标签是由于手动添加资源配置后,原本默认的就会失效,需要将原本存放资源文件的resources
添加到资源依赖当中(一般不配置的情况下,会将src/main/resource
目录添加到类路径
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.yml</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
最后配置一下链接信息及mybatis
的配置,在application.yml
中可以直接配置曾经引入mybatis
时需要单独编写的mybatis-config.xml
,框架会根据配置项达到相同效果
server:
servlet:
encoding:
charset: utf-8
force: true
spring:
profiles:
active: dev
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/db?characterEncoding=UTF-8&useSSL=false&allowPublicKeyRetrieval=true&ServerTimezone=UTC
username: root
password: 12345678
mybatis:
configuration:
map-underscore-to-camel-case: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
type-aliases-package: com.example.jinwandalaohu.pojo
spring
的datasource
配置jdbc
的老四样,不赘述了mybatis
中配置了下划线与小驼峰对象属性映射开关,日志的实现及pojo
类的包别名目录
若报错绑定失败,需要查看是否没有将xml
输出到target
目录,可以执行mvn clean
后再进行编译
获取Mapper
对应的bean
,执行接口方法后,输出如下,集成Mybatis
成功
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
ConfigurableApplicationContext ctx = SpringApplication.run(DemoApplication.class, args);
UserMapper bean = ctx.getBean(UserMapper.class);
System.out.println(bean.getAll());
}
}
使用@MapperScan
如果mapper
接口很多,在每个接口上都加Mapper
注解比较麻烦,此时可以使用包扫描器,在任意配置类上(如启动类)添加@MapperScan
,如@Mapper("包名写在这里")
,下面代码示例演示了@MapperScans
的使用和@MapperScan
的basePackages
属性使用,怎么用因人而异,最终实现的效果等同于@Mapper("包名写在这里")
@MapperScans(@MapperScan(basePackages = {"com.example.jinwandalaohu.dao"}))
public class XApplication{
// 省略
}
分开管理Java
代码和xml
文件
一般习惯于将代码和配置进行分离,配置全部放置resources
目录当中,这种写法就不需要在pom.xml
中进行资源目录配置(使用默认配置即可)。但由于mapper
接口和xml
配置不在同一个包中,所以需要配置mapper
文件的映射路径
- 将原本放置在
mapper
接口同包下的配置文件挪到自定义的资源目录下 - 在
mybatis
的配置,添加一个mapper-locations
指定最终类路径下自定义目录中的所有xml
mybatis:
configuration:
map-underscore-to-camel-case: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
type-aliases-package: com.example.jinwandalaohu.pojo
mapper-locations: classpath:mybatis/mapper/**/*.xml
喜欢用Mybatis
的xml
进行配置的可以用mybatis.config-location
指定配置文件
至此,Mybatis
的集成已经可以使用了,只需要在对应的Service
层去注入XXDao
接口对象使用接口完成链接数据库操作
Springboot事务使用
其实就是使用的Spring
框架的注解,只需要在配置类上添加一个注解@EnabledTransaction
表示开启事务
由于已集成了Mybatis
,所以已经有提供好的DataSourceTransactionManager
对象,开发者可以开箱即用
事务可以控制:隔离级别,传播行为,超时时间等
事务处理
不配置@Transactional
就是使用REQUIRED
的隔离级别,超时时间为-1
,抛出运行时异常,回滚事务
按照一个MVC
结构设计的项目,通常会在Service
层做主要的业务逻辑,一般会将@Transactional(rollbackFor=Exception.class)
注解放到Service
的方法当中,如果放到类上则表示当前Service
类的全部方法都开启了事务
编程式事务
声明式事务(包括注解、xml
方式),可以配置以下信息
isolation
事务之间的隔离级别timeout
事务超时时间 默认-1,等于不限时readOnly
表示当前事务为只读事务propagation 传播行为
rollbackFor
遇到异常时回滚事务noRollbackFor
遇到异常不回滚事务
什么叫事务传播行为:A方法事务执行过程中,调用了第二个有Transactional
注解标记的方法,就会产生事务传播
传播行为一共有七种
REQUIRED
支持当前事务,如果不存在就新建一个事务(默认就是这个行为)SUPPORTS
支持当前事务,如果当前没有事务,方法调用以非事务方法执行,简单来说就是有事务就有,没有就算了MANDATORY
必须运行在一个事务中,如果当前没有事务发生,则抛出异常,测试的话很简单,只需要在方方上标记(强制必须有事务)
@Transactional(propagation = Propagation.MANDATORY)
Never
不能运行在事务当中,否则抛出异常,经测试,同一个类下的方法调用时无法抛出异常,需要A
类的开启事务方法中调用B
类中被标记Never
的方法
@Transactional(propagation = Propagation.Never)
NOT_SUPPORTED
方法不该运行在事务当中,如果存在事务则将当前事务挂起,经测试,同一个类下的方法调用时无法生效,需要A
类的开启事务方法中调用B
类中被标记NOT_SUPPOTRED
的方法
在A
类中开启事务,过程中调用B
类NOT_SUPPORTED
的方法,在调用方法后A
类事务方法故意抛出错误出发回滚,最后的表现就是事务回滚了,但是B
类的方法不受影响
REUQIRED_NEWS
当前方法必须运行在它自己,会启动一个新事务,如果存在事务则会将事务挂起
可以理解为各自有各自的事务,当方法执行后事务会被提交,此时即使调用方抛出异常也不会影响此方法,但反过来如果在方法中抛出异常,则调用方也会被回滚事务,经测试,同一个类中的方法不起作用
NESTED
嵌套事务,如果当前没有事务,则与默认的REQUIRED
表现一致,相当于自己启了一个新事务,如果有事务,则嵌套事务内执行(加入到存在的事务当中)
返回格式化的日期信息
默认情况下,springboot
返回Date
数据类型时,采用的格式化方式时国际标准ISO 8601
的格式,但这种格式并不直观和适合方便前端交互,由于内置了jackson
,可以通过application.yml
对json
序列化的格式做时间配置
spring:
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
Springboot集成reids
首先添加redis
的启动器
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
接着在application.yml
中设置端口
spring:
redis:
port: 6379
引入后启动器后,会像Spring
容器注入一个RedisTemplate
对象,Java
程序中通过这个模板类操作数据
@GetMapping("getFromCacheOrDb")
public Object getName() {
String name = (String) redisTemplate.opsForValue().get("name");
if (name == null || name.isEmpty()) {
System.out.println("数据库没有,需要从db拿并且设置到redis当中");
redisTemplate.opsForValue().set("name", "李四");
return "李四";
} else {
System.out.println("redis有,直接返回");
return "redis有,直接返回" + name;
}
}
Springboot导入老项目的xml
需要在配置类上,加上@ImportResource
注解,属性填入类路径中的xml
,启动项目后,便会从xml
当中解析并注入bean
到容器中
@SpringBootApplication
@ImportResource("classpath:beans.xml")
public class MainApplication {
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<beans>
<bean class="cn.mgl.model.User" name="user"></bean>
</beans>
</beans>
Springboot实现文件上传
处理文件请求上传大小限制,放置文件超大小超出默认配置导致上传失败
spring:
redis:
port: 6379
servlet:
multipart:
max-file-size: 10MB
max-request-size: 100MB
java
代码编写
@RestController
public class FileController {
@PostMapping("upload")
public Object upload(@RequestParam("file") MultipartFile uploadFile) throws IOException {
String originalFilename = uploadFile.getOriginalFilename();
int i = originalFilename.lastIndexOf(".");
String suffix = originalFilename.substring(i);
String prefix = originalFilename.substring(0, i);
File file = new File("你的目标存储路径" + prefix + UUID.randomUUID() + suffix);
uploadFile.transferTo(file);
return "上传成功";
}
}
如果是单独一个api
提供给前后端分离的项目使用,应该配置一个允许跨域的设置,否则预检请求或响应体会被浏览器给拦截,前端代码中会获取不到,在配置中实现WebMvcConfigurer
,并重写其addCorsMappings
方法
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOriginPatterns("*").
allowCredentials(true)
.maxAge(3600)
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS");
}
}
提供一段使用atnd
的前端React
代码,表单对象设置的key
就是后端接口中,@RequestParam
设置的key
,需要一一对应
const Upload: FC = () => {
const customUpload = async (file: RcFile, FileList: RcFile[]) => {
const data = new FormData();
data.set('file', file);
const res = await axios.post("http://localhost:8080/upload", data);
// 拿到接口响应,如果接口调用成功,res拿到的就是"上传成功"字符串
return Promise.reject();
}
return (
<Upload name={'file'} beforeUpload={customUpload}>
<Button>点击上传文件</Button>
</Upload>
);
};
Spring Boot
的内容非常庞大,目前就介绍这么多
完:)
转载自:https://juejin.cn/post/7197222701219774519