likes
comments
collection
share

SpringBoot的使用

作者站长头像
站长
· 阅读数 23

在使用SpringBoot之前,需要学习SpringSpringMVC,这两个框架的使用,需要编写大量的配置文件,文件越多使用就越麻烦,以及需要集成第三方框架时也非常繁琐。Spring-boot就解决了这个问题,提供了一套默认的配置,极少的编写配置文件,让开发者专心编写业务。比如使用Mybatis,直接集成依赖即可使用,可以理解为一个更快速的SpringSpringMVC,开发效率更高,有如下特点:

  1. 可创建Spring应用
  2. 内嵌Tomcat等服务器,不需要额外安装
  3. 提供了starter起步依赖,简化应用配置,相当于有默认的配置,比如使用Mybatis,需要在Spring中配置相关对象,在Springboot中,只需要在pom.xml中添加mybatis-spring-boot-starter,相关的依赖就自动配置好了
  4. 尽可能配置Spring和第三方库

如何创建一个Springboot项目

创建Springboot的两种方式

  1. Idea提供了Spring的初始化容器创建Springboot

需要联网,默认的地址是https://start.spring.io,网络遇到问题可以改成https://start.aliyun.com

SpringBoot的使用

创建完成后就是一个标准可以运行的springboot项目

  1. 创建一个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

作用是可以在当前配置类中导入其他配置类

  1. 先定义一个配置类
package cn.mgl.demo.config;

import org.springframework.context.annotation.Bean;

public class AConfig {
    @Bean
    public String testStr() {
        return "testStr";
    }
}
  1. 使用@Import进行导入将目标类的bean成员复制到当前配置类中
@Configuration
@Import(AConfig.class)
public class WebConfig {
}

此时查看容器中所有的bean,可以看到成功配置

SpringBoot的使用

配置文件

核心配置文件必须要用application命名

propertiesyml两种格式的配置文件,本质上没有区别,如application.properteisapplication.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 启用自动配置,例如安装了mybatisstarter,有这个配置就会把mybatis的对象创建好放入容器中
  • @ComponentScan 作用是包扫描器,默认配置就是当前启动类所在包即其所有子包中有相关注解的类都会被扫描并注册到容器当中,所以当启动类位置不对时,就会出现自动装配失败问题,可以手动进行包扫描的路径修改

如果遇到,如何解决响应体内容乱码问题

  1. 通过添加一个自定义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

二者效果等价

  1. 第一种方式的处理逻辑是不采用框架自带的字符编码过滤器,第二种方式更为简便,只需要通过配置修改掉字符编码为UTF-8
server:
    servlet:
  	encoding:
            enabled: true
            force: true
            charset: utf-8

Springboot集成MyBatis

主流的框架一般都有提供启动器,可以找相关的starter

  1. 添加依赖,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>
  1. 在使用@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();
}

为接口编写对应的sqlnamespaceid分别对应类名和方法名

<?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
  • springdatasource配置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());
    }

}

SpringBoot的使用

使用@MapperScan

如果mapper接口很多,在每个接口上都加Mapper注解比较麻烦,此时可以使用包扫描器,在任意配置类上(如启动类)添加@MapperScan,如@Mapper("包名写在这里"),下面代码示例演示了@MapperScans的使用和@MapperScanbasePackages属性使用,怎么用因人而异,最终实现的效果等同于@Mapper("包名写在这里")

@MapperScans(@MapperScan(basePackages = {"com.example.jinwandalaohu.dao"}))
public class XApplication{
    // 省略
}

分开管理Java代码和xml文件

一般习惯于将代码和配置进行分离,配置全部放置resources目录当中,这种写法就不需要在pom.xml中进行资源目录配置(使用默认配置即可)。但由于mapper接口和xml配置不在同一个包中,所以需要配置mapper文件的映射路径

  1. SpringBoot的使用将原本放置在mapper接口同包下的配置文件挪到自定义的资源目录下
  2. 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

喜欢用Mybatisxml进行配置的可以用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注解标记的方法,就会产生事务传播

传播行为一共有七种

  1. REQUIRED 支持当前事务,如果不存在就新建一个事务(默认就是这个行为)
  2. SUPPORTS 支持当前事务,如果当前没有事务,方法调用以非事务方法执行,简单来说就是有事务就有,没有就算了
  3. MANDATORY 必须运行在一个事务中,如果当前没有事务发生,则抛出异常,测试的话很简单,只需要在方方上标记(强制必须有事务)
    @Transactional(propagation = Propagation.MANDATORY)

SpringBoot的使用

  1. Never不能运行在事务当中,否则抛出异常,经测试,同一个类下的方法调用时无法抛出异常,需要A类的开启事务方法中调用B类中被标记Never的方法
    @Transactional(propagation = Propagation.Never)

SpringBoot的使用

  1. NOT_SUPPORTED 方法不该运行在事务当中,如果存在事务则将当前事务挂起,经测试,同一个类下的方法调用时无法生效,需要A类的开启事务方法中调用B类中被标记NOT_SUPPOTRED的方法

A类中开启事务,过程中调用BNOT_SUPPORTED的方法,在调用方法后A类事务方法故意抛出错误出发回滚,最后的表现就是事务回滚了,但是B类的方法不受影响

SpringBoot的使用

  1. REUQIRED_NEWS 当前方法必须运行在它自己,会启动一个新事务,如果存在事务则会将事务挂起

可以理解为各自有各自的事务,当方法执行后事务会被提交,此时即使调用方抛出异常也不会影响此方法,但反过来如果在方法中抛出异常,则调用方也会被回滚事务,经测试,同一个类中的方法不起作用

SpringBoot的使用

  1. NESTED 嵌套事务,如果当前没有事务,则与默认的REQUIRED表现一致,相当于自己启了一个新事务,如果有事务,则嵌套事务内执行(加入到存在的事务当中)

返回格式化的日期信息

默认情况下,springboot返回Date数据类型时,采用的格式化方式时国际标准ISO 8601的格式,但这种格式并不直观和适合方便前端交互,由于内置了jackson,可以通过application.ymljson序列化的格式做时间配置

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的内容非常庞大,目前就介绍这么多

完:)