SpringBoot

SpringBoot

什么是Spring?

Spring是一个轻量级的Java开发框架,于2003年兴起,作者:Rod Johnson

Spring为应用开发提供平台。它是为了解决企业应用开发的复杂性而创建的。Spring框架的主要优势之一是分层架构,分层架构允许使用者选择使用哪一个组件,同时为J2EE应用程序开发提供继承的框架

Spring的核心是控制反转(IoC)和面向切面编程(AOP)

Spring是如何简化Java开发的?

  1. 基于POJO的轻量级和最小侵入性编程
  2. 通过IOC,依赖注入(DI)和面向接口实现松耦合
  3. 基于切面(AOP)和惯例进行声明式编程
  4. 通过切面和模板减少样式代码,RedisTemplate,xxxTemplate

存在的缺点:

  • 依赖繁琐(有时还会出现依赖冲突问题)
  • 配置繁琐

1、什么是SpringBoot?


Spring Boot提供了一种快速使用并简化Spring的应用开发,基于约定大于配置的思想,去繁从简,可以让开发不必在配置与业务逻辑之间进行思维的切换,全身心的投入到业务逻辑的编码中,大大提高开发效率!

SpringBoot并不是对Spring功能上的增强,而是提供了一种快速使用Spring的方式,它默认配置了很多框架的使用方式,就像Maven整合了所有的jar包,SpringBoot整合了许多的框架

  • Spring全家桶
  • SpringBoot(构建一切):J2EE一站式解决方案
  • SpringCloud(协调一切):分布式整体解决方案

SpringBoot主要优点:

  • 自动配置(简化常用工程相关配置)
  • 起步依赖(简化依赖配置),与主流框架集成
  • 开箱即用,提供各种默认配置来简化项目配置
  • 辅助功能**(内嵌式容器简化Web项目)**
  • 没有冗余代码生成和XML配置的要求

SpringBoot是由Pivotal团队提供的全新框架,其设计目的是用来简化Spring应用的初始搭建以及开发过程

2、快速上手


创建SpringBoot入门程序

  1. 创建新模块,选择Spring Initializr,并配置模块相关基础信息

    SpringBoot_第1张图片

  2. 选择当前模块需要使用的技术集

    SpringBoot_第2张图片

  3. 开发控制器类

    // Rest模式
    @RestController
    @RequestMapping("/hello")
    public class BookController {
        @GetMapping
        public String hello() {
            System.out.println("Springboot is running...");
            return "Hello Springboot";
        }
    }
    
  4. 运行自动生成的Application类

    SpringBoot_第3张图片

注:基于idea开发SpringBoot程序需要确保联网且能够加载到程序框架结构

starter

  • SpringBoot中常见项目名称,定义了当前项目使用的所有依赖坐标,以达到减少依赖配置的目的

parent

  • 所有SpringBoot项目要继承的项目,定义了若干个坐标版本号(依赖管理,而非依赖),以达到减少依赖冲突的目的
  • spring-boot-starter-parent各版本间存在着诸多坐标版本不同

在实际开发中,使用任意坐标时,仅书写GAV中的G和A,V由SpringBoot提供,除非SpringBoot未提供对应版本V

主启动类(xxxApplication)

  • SpringBoot的引导类是Boot工程的执行入口,运行main方法就可以启动项目
  • SpringBoot工程运行后初始化Spring容器扫描引导类所在包加载bean

Boot内置了三款服务器分别是

tomcat(默认):apache出品,粉丝多,应用面广,负载了若干较重的组件

jetty:更轻量级,负载性能远不及tomcat

undertow:负载性能勉强跑赢tomcat

内嵌Tomcat工作原理是将Tomcat服务器作为对象运行,并将该对象交给Spring容器管理

2.1、配置文件

SpringBoot默认配置文件application.properties,通过键值对配置对应属性

属性配置

# 服务器端口配置
server.port=80

# 关闭运行日志图标(banner)
spring.main.banner-mode=off

# 设置日志相关
logging.level.root=error

SpringBoot内置属性查询

配置文档

配置文件格式

SpringBoot提供了3种配置文件的格式

  • properties (传统格式/默认格式)
  • yml (主流格式)
  • yaml
# application.properties
server.port=80

# application.yml
server:
    port: 81
    
# application.yaml
server:
    port: 82

SpringBoot配置文件加载优先级

  • application.properties(最高)
  • application.yml
  • application.yaml(最低)

不同配置文件中相同配置按照加载优先级相互覆盖,不同配置文件中不同配置全部保留

在SpringBoot项目当中除了以上3种配置文件外,SpringBoot为了增强程序的扩展性,除了支持配置文件的配置方式以外,还支持另外两种常见的配置方式:

JVM系统属性配置 (格式: -Dkey=value)

-Dserver.port=10010

命令行参数 (格式:–key=value)

--server.port=10011

优先级: 命令行参数 > 系统属性参数 > properties参数 > yml参数 > yaml参数

YAML

  • YAML (YAML Ain’t Markup Language) ,以数据为中心,比json、xml等更适合做配置文件
  • 优点
    • 容易阅读
    • 容易与脚本语言交互
    • 以数据为核心,重数据轻格式

YAML文件扩展名

  • .yml(主流)
  • .yaml

核心规则:数据前面要加空格与冒号隔开

yml数据读取

定义数据

country: China
user:
  my-name: xiaoZhang
  hobbies:
    - music
    - girl
    - swim
# 对象数组缩略格式
userList: [ { name: liming,age: 22 },{ name: xiaolin,age: 26 } ]

使用@Value注解读取单个数据,属性名引用方式: ${一级属性名.二级属性名....}

// 读取yml数据中的单一数据        使用SPEL表达式取值
@Value(value = "${country}")
private String country;

// 烤肉串模式获取属性
@Value(value = "${user.my-name}")
private String name;

@Value(value = "${user.hobbies[1]}")
private String hobbyTwo;

// 驼峰方式获取属性
@Value("${userList[1].age}")
private Integer age;

SpringBoot把全部数据封装到Environment对象中,可以直接进行获取

// 使用自动装配将所有的数据封装到一个对象的Environment中
@Autowired
private Environment environment;

@Test
void contextLoads() {
    System.out.println(environment.getProperty("user.hobbies[2]"));    // swim
    System.out.println(environment.getProperty("userList[0].name"));    // liming
}

缺陷:把全部数据都封装了,有时只需要一部分数据

自定义对象封装数据

person:
  name: "xiaozhao\nxiaobai"
  age: 22
  is-marry: false
  birth: 2003-05-21
  hobby: [ boy,music,housework ]
  map: { k1: v1,k2: v2 }
  love-fruit:
    - "苹果"
    - "香蕉"
    - "甜橙"
// 1.定义数据模型封装yml文件中对应的数据  yml和实体类属性一一对应
// 2.提供get/set方法 并定义为Spring管控的bean
@Data
@Component
@ConfigurationProperties(prefix = "person")     // 将本类中的所有属性和配置文件中的相关配置进行绑定
@Validated      // 开启JDR303校验
public class Person {
    private String name;
    @Max(value = 130,message = "年龄不能大于130岁")
    @Min(value = 0,message = "年龄不能小于0岁")
    private Integer age;
    private boolean isMarry;
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date birth;
    private String[] hobby;
    private Map<String, Object> map;
    private List<String> loveFruit;
}

添加spring-boot-configuration-processor坐标,解决警告问题!

注:

  • 封装类必须定义为Spring管理的bean且要提供对应的get/set方法,否则无法进行属性注入
  • 使用""引起来的字符会转义,单引号则不会转义,原样输出
@Value获取值与@ConfigurationProperties获取值区别
特性 @ConfigurationProperties @Value
批量注入配置文件中的属性 只能单个注入
松散语法绑定 支持 不支持
JSR303数据校验 支持 不支持
复杂数据类型封装 支持 不支持(无法进行获取)
SPEL表达式 不支持 支持

2.2、静态资源

resources文件夹中目录结构

  • static:保存所有的静态资源;css、js、image、video…
  • templates:保存所有的页面模块;(SpringBoot默认打包方式为jar包,使用嵌入式的Tomcat,不支持JSP页面)。可以使用模板引擎(freemarker、thymeleaf)
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = new String[]{"classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/"};

2.3、配置文件占位符

RandomValuePropertySource:配置文件中可以使用随机数,占位符获取配置的值,如果没有的话可以使用:之后指定默认值!

animal:
  name: 哈士奇${random.uuid}
  age: ${random.int(0,10)}
  master: ${user.uname:小昭}_dog
@Data
@Component
@ConfigurationProperties(prefix = "animal")
public class Animal {
    private String name;
    private Integer age;
    private String master;
}

2.4、多环境支持

Profile是Spring对不同环境提供不同配置功能的支持,可以通过激活、指定参数等方式快速切换指定环境

在编写主配置文件的时候,文件名可以是:application-{profile}.properties/yml

默认使用application.properties的配置,此文件优先级最高

开发环境:application-dev.yml

server:
  port: 80

生产环境:application-pro.yml

server:
  port: 90

测试环境:application-test.yml

server:
  port: 100

激活方式

  • 配置文件

    • spring:
        profiles:
          active: pro
      
  • 命令行:java -jar springboot_quickstart-0.0.1-SNAPSHOT.jar --spring.profiles.active=test

  • jvm系统属性:java -jar -Dspring.profiles.active=dev springboot_quickstart-0.0.1-SNAPSHOT.jar

2.5、配置文件加载位置

SpringBoot启动会扫描以下位置的application.properties或者application.yml文件作为当前项目的默认配置文件

  • file:./config
  • file:./
  • classpath:/config
  • classpath:/

以上是按照优先级从高到低的顺序,所有位置的文件都会被加载(互补配置),高优先级配置内容会覆盖低优先级配置的内容

也可通过配置spring.config.location来改变其默认的配置

2.6、Bean的管理

获取Bean

默认情况下,SpringBoot项目在启动的时候会自动的创建IOC容器(也称为Spring容器),并且在启动的过程当中会自动的将bean对象都创建好,存放在IOC容器当中。应用程序在运行时需要依赖什么bean对象,直接进行依赖注入就可以了

3种常用方式获取Bean:

  • 根据name获取bean
  • 根据类型获取bean
  • 根据name获取bean(带类型转换)

调用getBean()的重载方法获取即可

@Autowired
private ApplicationContext applicationContext;      // 注入IOC容器对象

@Test
public void testGetBean() {
    // 根据名称获取
    Person p1 = (Person) applicationContext.getBean("person");
    // 根据类型获取
    Person p2 = applicationContext.getBean(Person.class);
    // 根据bean的名称和类型获取
    Person p3 = applicationContext.getBean("person", Person.class);

    System.out.println("p1 = " + p1);
    System.out.println("p2 = " + p2);
    System.out.println("p3 = " + p3);
    System.out.println(p1 == p2 && p2 == p3);       // true
}

注:默认情况下,IOC容器中的bean对象是单例的!

Bean的作用域

默认bean对象是单例模式(只有一个实例对象),那么如何设置bean 对象为非单例呢?需要设置bean的作用域

在Spring中支持五种作用域,后三种在web环境才生效:

作用域 说明
singleton 容器内同名称的bean只有一个实例(单例)(默认)
prototype 每次使用该bean时会创建新的实例(非单例)
request 每个请求范围内会创建新的实例(web环境中)
session 同上
application 同上

可以借助Spring中的@Scope注解来进行配置作用域

  • @Lazy:延迟加载(第一次使用bean对象时,才会创建bean对象并交给ioc容器管理)

注:实际开发当中,绝大部分的Bean是单例的,也就是说绝大部分Bean是不需要配置scope属性的!

第三方Bean

之前所配置的bean,像controller、service,dao三层体系下编写的类,这些类都是我们在项目当中自己定义的类(自定义类),当要声明这些bean,也非常简单,只需要在类上加上 @Component以及它的这三个衍生注解(@Controller、@Service、@Repository)

但是开发中有时会使用大量的第三方Bean,这些第三方提供的Bean是只读的,我们无法在上面进行修改或添加@Component注解或衍生注解

如果要管理的bean对象来自于第三方(不是自定义的),是无法用@Component 及衍生注解声明 bean的,那么此时就需要用到@Bean注解

如果需要定义第三方Bean时, 通常会单独定义一个配置类

@Configuration      // 配置类 (在配置类当中对第三方bean进行集中的配置管理)
public class OtherBeanConfig {
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource dataSource() {
        return new DruidDataSource();
    }
}

配置文件

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/boot
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: root
    type: com.alibaba.druid.pool.DruidDataSource
    # Spring Boot 默认是不注入这些属性值的,需要自己绑定
    # druid 数据源专有配置
    # 初始化时建立物理连接的个数
    initialSize: 5
    # 最小连接池数量
    minIdle: 5
    # 最大连接池数量 maxIdle已经不再使用
    maxActive: 20
    # 获取连接时最大等待时间,单位毫秒
    maxWait: 60000
    # 申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效
    timeBetweenEvictionRunsMillis: 60000
    # 设置连接空闲时间的阈值
    minEvictableIdleTimeMillis: 300000
    # 用来检测连接是否有效的sql 必须是一个查询语句
    # mysql中为 select 'x'
    # oracle中为 select 1 from dual
    validationQuery: select 'x'
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false
    poolPreparedStatements: true

测试

@Test
public void testOtherBean() {
    System.out.println(dataSource.getClass());
}

注:

  • 通过@Bean注解的name或value属性可以声明bean的名称,如果不指定,默认bean的名称就是方法名
  • 如果第三方bean需要依赖其它bean对象,直接在bean定义方法中设置形参即可,容器会根据类型自动装配

3、整合第三方技术


对于数据访问层,无论是SQL还是NOSQL,Spring Boot默认采用整合Spring Data的方式进行统一处理,添加大量自动配置,屏蔽很多设置,引入各种xxxTemplate、xxxRepository来简化对数据访问层的操作

3.1、整合Mybatis

  1. 创建新模块,选择Spring初始化,并配置模块相关基础信息

  2. 选择当前模块需要使用的技术集(MyBatis、MySQL)

  3. 配置数据源参数

    # 配置数据库相关信息
    spring:
      datasource:
        username: root
        password: root
        url: jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
        driver-class-name: com.mysql.cj.jdbc.Driver
    # mybatis相关配置
    mybatis:
      # 指定 mapper.xml 的位置
      mapper-locations: classpath:mapper/*.xml
      # 扫描实体类的位置,在此处指明扫描实体类的包,在 mapper.xml 中就可以不写实体类的全路径名
      type-aliases-package: com.zhang.pojo
      configuration:
      	# 标准日志输出
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
        # 开启驼峰映射
        map-underscore-to-camel-case: true
    
  4. 编写实体类

  5. 定义数据层接口与映射配置

    @Mapper		// 自动生成该接口的实现类对象(代理对象),并把该对象交由IOC容器进行管理,如果mapper文件较多,可在主启动类上添加 @MapperScan 注解扫描指定包
    public interface BookMapper {
        List<Book> queryAllBook();
    
        Book queryBookById(@Param("id") Integer id);
    
        Integer addBook(Book book);
    
        Integer delBook(@Param("id") Integer id);
    
        Integer modifyBook(Book book);
    }
    
  6. 编写mapper.xml文件

    
    DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    
    <mapper namespace="com.zhang.mapper.BookMapper">
        <select id="queryAllBook" resultType="Book">
            select id, type, name, description
            from mybatis.tbl_book
        select>
    
        <select id="queryBookById" resultType="Book">
            select id, type, name, description
            from mybatis.tbl_book
            <where>
                <if test="id!=null">id=#{id}if>
            where>
        select>
    
        <insert id="addBook" parameterType="Book">
            insert into mybatis.tbl_book(id, type, name, description)
            values (#{id}, #{type}, #{name}, #{description})
        insert>
    
        <delete id="delBook" parameterType="integer">
            delete from mybatis.tbl_book
            <where>
                <if test="id!=null">id=#{id}if>
            where>
        delete>
    
        <update id="modifyBook" parameterType="Book">
            update mybatis.tbl_book set type=#{type},name=#{name},description=#{description}
            <where>
                <if test="id!=null">id=#{id}if>
            where>
        update>
    mapper>
    
  7. 测试类中注入mapper接口,测试功能

    @SpringBootTest
    class Springboot05MybatisApplicationTests {
        @Autowired
        private BookMapper bookMapper;
    
        @Test
        void contextLoads() {
            List<Book> books = bookMapper.queryAllBook();
            books.forEach(System.out::println);
        }
    }
    

3.2、整合Mybatis-plus

1.手动添加SpringBoot整合Mybatis-Plus的坐标,可通过maven仓库获取


<dependency>
    <groupId>com.baomidougroupId>
    <artifactId>mybatis-plus-boot-starterartifactId>
    <version>3.4.2version>
dependency>

编写实体类并定义数据层接口,并继承BaseMapper

@Mapper
public interface UserMapper extends BaseMapper<User> {
	// 编写业务扩展方法
}

继承完BaseMapper接口之后,无需编写mapper.xml文件,就拥有大部分操作数据库的方法了

测试类中注入mapper接口,测试功能

@SpringBootTest
class MybatisPlusApplicationTests {
    @Autowired
    private UserMapper userMapper;

    @Test
    void contextLoads() {
        System.out.println(userMapper.selectById(1L));
    }
}

定义业务层接口,继承IService

public interface UserService extends IService<User> {
	// 编写业务扩展方法
}

定义业务层实现类,并继承ServiceImpl,实现其自定义的业务层接口

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
	
}

注:如果mapper接口较多时,可以在启动类上添加@MapperScan(value = "com.zhang.mapper")

3.3、整合Druid

SpringBoot 2.x 默认使用HikariCP作为数据源,数据源的相关配置都在DataSourceProperties里面

可参考DataSourceConfiguration,根据配置创建数据源,可以使用spring.datasource.type指定自定义的数据源类型

HikariCP 是目前市面上性能最好的数据源产品,但在实际的开发过程中,企业往往更青睐于另一款数据源产品:Druid,它是目前国内使用范围最广的数据源产品

Druid 是阿里巴巴推出的一款开源的高性能数据源产品,Druid 支持所有 JDBC 兼容的数据库,包括 Oracle、MySQL、SQL Server 和 H2 等等。Druid 不仅结合了 C3P0、DBCP 和 PROXOOL 等数据源产品的优点,同时还加入了强大的监控功能。通过 Druid 的监控功能,可以实时观察数据库连接池和 SQL 的运行情况,帮助用户及时排查出系统中存在的问题

1.导入Druid对应的starter


<dependency>
    <groupId>com.alibabagroupId>
    <artifactId>druid-spring-boot-starterartifactId>
    <version>1.2.16version>
dependency>

2.在 Spring Boot 配置文件中配置以下内容

  • JDBC 通用配置
  • Druid 数据源连接池配置
  • Druid 监控配置
  • Druid 内置 Filter 配置
###### JDBC通用配置 ######
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/boot?useSSL=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
    username: root
    password: root
    type: com.alibaba.druid.pool.DruidDataSource
    druid:
      ###### Druid连接池的配置 ######
      initial-size: 5                                                                 # 初始化连接大小
      min-idle: 5                                                                     # 最小连接池数量
      max-active: 20                                                                  # 最大连接池数量
      max-wait: 60000                                                                 # 获取连接时最大等待时间,单位毫秒
      time-between-eviction-runs-millis: 60000                                        # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
      min-evictable-idle-time-millis: 300000                                          # 配置一个连接在池中最小生存的时间,单位是毫秒
      validation-query: select 'x'                                                    # 测试连接
      test-while-idle: true                                                           # 申请连接的时候检测,建议配置为true,不影响性能,并且保证安全性
      test-on-borrow: false                                                           # 获取连接时执行检测,建议关闭,影响性能
      test-on-return: false                                                           # 归还连接时执行检测,建议关闭,影响性能
      pool-prepared-statements: false                                                 # 是否开启PSCache,PSCache对支持游标的数据库性能提升巨大,oracle建议开启,mysql下建议关闭
      max-pool-prepared-statement-per-connection-size: 20                             # 开启poolPreparedStatements后生效
      filters: stat,wall                                                              # 配置扩展插件,常用的插件有=>stat:监控统计  wall:防御sql注入
      connection-properties: 'druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000' # 通过connectProperties属性来打开mergeSql功能;慢SQL记录

      ###### Druid监控配置信息 ######
      # StatViewServlet配置
      stat-view-servlet:
        enabled: true                                                                 # 是否开启内置监控页面,默认值为 false
        url-pattern: '/druid/*'                                                       # StatViewServlet 的映射路径,即内置监控页面的访问地址
        reset-enable: true                                                            # 是否启用重置按钮
        login-username: admin                                                         # 内置监控页面的登录页用户名 username
        login-password: admin                                                         # 内置监控页面的登录页密码 password
      # WebStatFilter配置
      web-stat-filter:
        enabled: true                                                                 # 是否开启内置监控中的 Web-jdbc 关联监控的数据
        url-pattern: '/*'                                                             # 匹配路径
        exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*'                     # 排除路径
        session-stat-enable: true                                                     # 是否监控session
      filter:
        # 配置StatFilter (SQL监控配置)
        stat:
          enabled: true                                                               # 开启 SQL 监控
          slow-sql-millis: 1000                                                       # 慢查询
          log-slow-sql: true                                                          # 记录慢查询 SQL
        # 配置WallFilter (防火墙配置)
        wall:
          enabled: true                                                               # 开启防火墙
          config:
            update-allow: true                                                        # 允许更新操作
            drop-table-allow: false                                                   # 禁止删表操作
            insert-allow: true                                                        # 允许插入操作
            delete-allow: true                                                        # 允许删除操作

通过整合以上三种技术,得出经验,在整合任何第三方技术时需

  • 导入对应的starter
  • 配置对应的设置或采用默认配置

3.4、整合日志框架

市面上存在很多的日志框架,比如:

  • JUL(java.util.logging)
  • JCL(Apache Commons Logging)
  • Log4j、Log4j2
  • Logback
  • SLF4j
  • jboss-logging
  • ……

SpringBoot在框架内部使用JCL,spring-boot-starter-logging采用了slf4j+logback的形式

SpringBoot也能自动适配(JUL、log4j2、logback)并简化配置

日志门面(接口) 日志实现
JCL(jakarta Commons Logging)SLF4j(Simple Logging Facade for Java)、jboss-logging Log4j、Log4j2、Logback

SLF4J官方文档

SpringBoot能自动适配所有的日志,而且底层使用slf4j+logback的方式记录日志,引入其他框架的时候,只需要把这个框架依赖的日志框架排除掉即可!

默认配置

SpringBoot默认帮我们配置好了日志,日志级别,由低到高为:

  • TRACE:运行堆栈信息,使用率低
  • DEBUG:程序员调试代码使用
  • INFO:记录运维过程数据
  • WARN:记录运维过程报警数据
  • ERROR:记录错误堆栈信息
  • FATAL:灾难信息,合并计入ERROR
@SpringBootTest
public class LoggingApplicationTests {
    // 日志记录器
    private Logger log = LoggerFactory.getLogger(getClass());

    @Test
    void contextLoads() {
        // 日志的级别,由低到高:trace < debug < info < warn < error
        // 可以调整输出的日志级别;日志就只会在这个级别以后的高级别生效
        // 不要导错包:org.slf4j.LoggerFactory
        // SpringBoot默认级别为:info,可通过配置进行调整
        log.trace("trace");
        log.debug("debug");
        log.info("info");
        log.warn("warn");
        log.error("error");
    }
}

修改日志的默认配置

logging:
  level:
    # 为对应包设置日志级别
    com.zhang: debug
  file:
    # 指定日志生成的目录名称,日志文件名默认为:spring.log
    path: E:/log
    # 当前项目下生成 myBoot.log 日志文件
    # name: myBoot.log
    # 文件大小限制
    max-size: 6MB
  pattern:
    # 指定文件中日志输出的格式
    file: "%d{yyyy-MM-dd} --- [%thread] --- %-5level --- %logger{50} ---- %msg%n"
    # 在控制台输出的日志格式
    console: "%d{yyyy-MM-dd} [%thread] %-5level %logger{50} - %msg%n"
指定配置

参考官方文档

给类路径下放上每个日志框架自己的配置文件即可;SpringBoot就不再使用它默认的配置

  • logback.xml:直接会被日志框架识别
  • logback-spring.xml:日志框架就不直接加载日志的配置项,由SpringBoot解析日志配置,可以使用SpringBoot的高级Profile功能
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
    
    <layout class="ch.qos.logback.classic.PatternLayout">
        <springProfile name="dev">
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} ===> [%thread] ===> %-5level %logger{50} - %msg%npattern>
        springProfile>
        <springProfile name="!dev">
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} ==== [%thread] ==== %-5level %logger{50} - %msg%npattern>
        springProfile>
    layout>
appender>

配置原理总结:导入场景starter—>xxxAutoConfiguration—>导入xxx组件—>绑定xxxProperties—>绑定配置文件项

4、基于SpringBoot整合SSMP


4.1、环境搭建

  1. 创建新模块,选择Spring初始化,并配置模块相关基础信息

  2. 选择当前模块需要使用的技术集(SpringWeb、MySQL)

  3. 手动导入SpringBoot中不包含的坐标

    
    <dependency>
        <groupId>com.baomidougroupId>
        <artifactId>mybatis-plus-boot-starterartifactId>
        <version>3.4.2version>
    dependency>
    
    <dependency>
        <groupId>com.alibabagroupId>
        <artifactId>druid-spring-boot-starterartifactId>
        <version>1.2.8version>
    dependency>
    
  4. 修改配置文件为yml格式

  5. 设置端口为80方便访问

    # 配置端口
    server:
      port: 80
    
  6. 创建数据库表并添加数据

    create database if not exists `ssMp`;
    use ssMp;
    create table if not exists `tb_book`
    (
        `id`   INT(10) auto_increment NOT NULL COMMENT '图书id',
        `type` VARCHAR(20)            NOT NULL COMMENT '图书分类',
        `name` VARCHAR(50)            NOT NULL COMMENT '图书名称',
        `description` VARCHAR(100)    NOT NULL COMMENT '图书描述',
        PRIMARY KEY (`id`)
    ) ENGINE = INNODB
    DEFAULT CHARSET = utf8
    
    # --------------------
    INSERT INTO `tb_book` VALUES (1, '市场营销', '直播带货:淘宝、天猫直播从新手到高手', '一本教你如何玩转直播的书');
    INSERT INTO `tb_book` VALUES (2, '企业管理', '领导力', '高绩效团队管理法则不懂带团队你就自己累');
    INSERT INTO `tb_book` VALUES (3, '企业管理', '高情商管理', '三分管人 七分做人');
    INSERT INTO `tb_book` VALUES (4, '企业管理', '管理三要', '能识人 会用人 懂管人');
    INSERT INTO `tb_book` VALUES (5, '经济学', '每个人的经济学', '做复杂世界的明白人');
    INSERT INTO `tb_book` VALUES (6, '政治经济学', '政治经济学的任务', '揭示经济规律经济规律');
    

4.2、编写Mapper相关代码(数据层)

  1. 编写实体类

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class Book implements Serializable{
        private Integer id;
        private String type;
        private String name;
        private String description;
    }
    
  2. 配置yml文件

    server:
      port: 80
    # 配置druid数据源(druid 专用配置)
    spring:
      datasource:
        druid:
          username: root
          password: root
          url: jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
          driver-class-name: com.mysql.cj.jdbc.Driver
    # 表名前缀
    mybatis-plus:
      global-config:
        db-config:
          table-prefix: tb_
          # 设置主键生成策略为数据库自增策略
          id-type: auto
          # 开启MP日志(标准日志输出)
      configuration:
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    

    注:使用数据库自增策略必须给id设置为自动增长,否则报错

  3. 定义数据层接口与映射配置,继承BaseMapper并指定泛型

    @Mapper
    public interface BookMapper extends BaseMapper<Book> {
        //编写扩展方法
    }
    
  4. 创建测试类并注入mapper接口,测试结果

    @SpringBootTest
    public class BookMapperTestCase {
        @Autowired
        private BookMapper bookMapper;
    
        @Test
        public void testBookById() {
            Book book = bookMapper.selectById(6L);
            System.out.println(book);
        }
    }
    
  5. 添加分页功能

    分页需要设定分页对象Ipage,其中封装了分页操作中的所有数据

    @Test
    void testPageBook() {
        //查询第二页,查询五条数据
        IPage<Book> page = new Page<>(2, 5);
        bookMapper.selectPage(page, null);
        System.out.println("当前页:" + page.getCurrent());
        System.out.println("每页数据总量:" + page.getSize());
        System.out.println("总记录数:" + page.getTotal());
        // 多少页 = 总记录数 除以 每页数据总量
        System.out.println("共多少页:" + page.getPages());
        // 查询得到的数据
        List<Book> records = page.getRecords();
        records.forEach(System.out::println);
    }
    

    分页操作是在MP的常规操作基础上增强得到(依赖MP分页拦截器),**其内部是动态的拼写SQL语句,**因此需要增强对应的功能,需要添加MP拦截器实现

    @Configuration
    public class MPConfig {
        // 配置分页插件
        @Bean
        public MybatisPlusInterceptor mybatisPlusInterceptor() {
            // 1.定义MP拦截器
            MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
            // 2.添加具体的拦截器
            interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
            return interceptor;
        }
    }
    
  6. 按照条件查询

    使用QueryWrapper对象封装查询条件,**推荐使用LambdaQueryWrapper对象,**所有查询操作封装成方法调用

@Test
void queryGetByCondition() {
    String name = "经济";
    LambdaQueryWrapper<Book> lqw = new LambdaQueryWrapper<>();
    // 查询包含spring的数据
    //if (name != null) lqw.like(Book::getName, name);
    // 动态条件拼接
    lqw.like(!Strings.isNullOrEmpty(name), Book::getName, name);
    bookMapper.selectList(lqw);
}

4.3、编写Service相关代码(业务层)

  1. 定义Service接口

    public interface BookService {
        // 添加图书
        Boolean save(Book book);
    
        // 修改图书
        Boolean modify(Book book);
    
        // 删除图书
        Boolean delete(Integer id);
    
        // 根据id查询图书
        Book queryById(Integer id);
    
        // 查询全部图书
        List<Book> queryAll();
    
        // 分页查询
        IPage<Book> queryPage(int currentPage, int pageSize);
    }
    
  2. 定义service对应实现类

    // 定义为业务层的bean
    @Service
    public class BookServiceImpl implements BookService {
        @Autowired
        private BookMapper bookMapper;
    
        @Override
        public Boolean save(Book book) {
            return bookMapper.insert(book) > 0;
        }
    
        @Override
        public Boolean modify(Book book) {
            return bookMapper.updateById(book) > 0;
        }
    
        @Override
        public Boolean delete(Integer id) {
            return bookMapper.deleteById(id) > 0;
        }
    
        @Override
        public Book queryById(Integer id) {
            return bookMapper.selectById(id);
        }
    
        @Override
        public List<Book> queryAll() {
            return bookMapper.selectList(null);
        }
    
        @Override
        public IPage<Book> queryPage(int currentPage, int pageSize) {
            IPage<Book> page = new Page<>(currentPage, pageSize);
            return bookMapper.selectPage(page, null);
        }
    }
    
  3. 定义测试类测试相关功能

    @SpringBootTest
    public class BookServiceTestCase {
        @Autowired
        private BookService bookService;
    
        @Test
        void testQueryById() {
            System.out.println(bookService.queryById(6));
        }
    
        @Test
        void testPageBook() {
            //查询第一页,查询五条数据
            IPage<Book> page = bookService.queryPage(1, 5);
            // 查询得到的数据
            List<Book> records = page.getRecords();
            records.forEach(System.out::println);
        }
    }
    

    注:**Service接口名称尽量定义成业务名称,**并与Mapper接口名称进行区分

  4. 简化通用业务层代码开发

    MP提供有业务层通用接口ISerivce与业务层通用实现类 ServiceImpl

    如果其中提供的方法不满足需要时,可以在通用类的基础上做功能重载或者功能追加(自定义需要的功能)

    定义service接口

    public interface IBookService extends IService<Book> {
        
    }
    

    定义service对应实现类

    @Service
    public class BookServiceImpl extends ServiceImpl<BookMapper, Book> implements IBookService {
        @Autowired
        private BookMapper bookMapper;
    
        @Override
        public boolean addBook(Book book) {
            return bookMapper.insert(book) > 0;
        }
    
        @Override
        public boolean modifyBook(Book book) {
            return bookMapper.updateById(book) > 0;
        }
    
        @Override
        public IPage<Book> queryPage(int currentPage, int pageSize) {
            IPage<Book> page = new Page<>(currentPage, pageSize);
            bookMapper.selectPage(page, null);
            return page;
        }
    }
    

    定义测试类测试相关功能

    @SpringBootTest
    public class BookServiceTest {
        @Autowired
        private IBookService bookService;
    
        @Test
        void testQueryAllBook() {
            List<Book> books = bookService.list();
            books.forEach(System.out::println);
        }
    }
    

注:重载时不要覆盖原始操作,避免原始提供的功能丢失

4.4、编写controller相关代码(表现层)

表现层数据一致性处理

设计表现层返回结果的模型类,用于后端与前端进行数据格式统一,也称为前后端数据协议

@Data
@NoArgsConstructor
public class R {
    private Boolean flag;
    private Object data;

    public R(Boolean flag) {
        this.flag = flag;
    }

    public R(Boolean flag, Object data) {
        this.flag = flag;
        this.data = data;
    }
}

表现层接口统一返回值类型结果

@RestController
@RequestMapping("/books")
public class BookControllerTwo {
    @Autowired
    private IBookService bookService;

    @GetMapping
    public R getAll() {
        return new R(true, bookService.list());
    }

    // @RequestBody 接收JSON格式的实体数据
    @PostMapping
    public R save(@RequestBody Book book) {
        //R r = new R();
        //boolean data = bookService.save(book);
        //r.setData(data);

        return new R(bookService.addBook(book));
    }

    @PutMapping
    public R modify(@RequestBody Book book) {
        return new R(bookService.modifyBook(book));
    }

    // http:localhost/books/5
    @DeleteMapping("/{id}")
    public R delete(@PathVariable Integer id) {
        return new R(bookService.removeById(id));
    }

    // @PathVariable 路径变量
    @GetMapping("/{id}")
    public R bookById(@PathVariable Integer id) {
        return new R(true, bookService.getById(id));
    }

    @GetMapping("/{currentPage}/{pageSize}")
    public R queryPage(@PathVariable int currentPage, @PathVariable int pageSize) {
        return new R(true, bookService.queryPage(currentPage, pageSize));
    }
}

目的:设计统一的返回值结果类型便于前端开发读取数据

4.5、前后端协议联调

在前后端分离结构设计中页面归属于前端服务器

单体工程中页面应放置在resources目录下的static目录中

  1. 前端发送异步请求,调用后端接口(Retrieve)

    // 列表
    getAll() {
        // 使用axios发送异步请求
        axios.get("/books").then((res) => {
            // console.log(res.data);
            this.dataList = res.data.data;
        });
    }
    
  2. 添加操作(Add)

    //弹出添加窗口
    handleCreate() {
        this.dialogFormVisible = true;
        this.resetForm();
    },
    //重置表单
    resetForm() {
        this.formData = {};
    },
    
    //添加
    handleAdd() {
    axios.post("/books", this.formData).then((res) => {
    // 判断当前操作是否成功
    if (res.data.flag) {
        //1、关闭弹层
        this.dialogFormVisible = false;
        this.$message.success("添加成功");
    } else {
        this.$message.error("添加失败");
    }
    }).finally(() => {
        //2、重新加载数据
        this.getAll();
    })
    },
    
  3. 删除操作(Delete)

    // 删除
    handleDelete(row) {
        // console.log(row);
        //1、弹出提示框
        this.$confirm("此操作将永久删除当前信息,是否删除?", "温馨提示", {type: "info"}).then(() => {
        //2、删除操作    
        axios.delete("/books/" + row.id).then((res) => {
        if (res.data.flag) {
        	this.$message.success("删除成功");
        } else {
        	this.$message.error("删除失败");
        }
        }).finally(() => {
        //重新加载数据
        	this.getAll();
        });
        }).catch(() => {
            //3、取消删除
        	this.$message.info("取消删除操作");
        });
    },
    
  4. 修改操作(Modify)

    //弹出编辑窗口
    handleUpdate(row) {
        axios.get("/books/" + row.id).then((res) => {
            if (res.data.flag && res.data.data != null) {
                this.dialogFormVisible4Edit = true;
                this.formData = res.data.data;
            } else {
                this.$message.error("数据同步失败,自动刷新");
            }
        }).finally(() => {
            // 重新加载数据
            this.getAll();
        });
    },
    
  5. 消息一致性处理

    // 定义SpringMvc异常处理器处理异常
    @RestControllerAdvice
    public class ProjectExceptionAdvice {
        // 拦截所有的异常信息
        @ExceptionHandler(Exception.class)
        public R doException(Exception ex) {
            // 记录日志
            // 通知运维
            // 通知开发
            ex.printStackTrace();
            return new R("服务器故障,请稍后再试!");
        }
    }
    

    修改表现层返回结果的模型类,封装出现异常后对应的信息

    private String msg;		// 要显示的消息
    
    public R(String msg) {
        this.flag = false;
        this.msg = msg;
    }
    
  6. 分页功能(Page)

    替换查询全部功能为分页功能

    // 分页查询
    getPage() {
        // 发送异步请求
        axios.get("/books/" + this.pagination.currentPage + "/" + this.pagination.pageSize).then((res) => {
            // 加载分页数据
            let data = res.data.data;
            this.pagination.currentPage = data.current;
            this.pagination.pageSize = data.size;
            this.pagination.total = data.total;
            this.dataList = data.records;
        });
    },
    

    分页页码值切换

    // 切换页码
    handleCurrentChange(currentPage) {
        // 修改页码值为当前的页码值
        this.pagination.currentPage = currentPage;
        // 执行查询
        this.getPage();
    },
    
  7. 条件查询(Condition)

    查询条件数据封装

    pagination: {// 分页相关模型数据
        currentPage: 1,// 当前页码
        pageSize: 10,// 每页显示的记录数
        total: 0,// 总记录数
        type: "",
        name: "",
        description: ""
    }
    

    页面数据模型绑定(v-model进行绑定)

    组织数据成为get请求发送的数据

    // 分页查询
    getAll() {
        // 组织参数,拼接url请求地址
        // console.log(this.pagination.type)
        let params = "?type=" + this.pagination.type + "&name=" + this.pagination.name + "&description=" + this.pagination.description;
        console.log(params);
        // /books/1/10?type=???&name=???&description=???
        // 发送异步请求
        axios.get("/books/" + this.pagination.currentPage + "/" + this.pagination.pageSize + params).then((res) => {
            let data = res.data.data;
            this.dataList = data.records;
        });
    },
    

    controller接收参数

    @GetMapping("{currentPage}/{pageSize}")
    public R queryPage(@PathVariable int currentPage, @PathVariable int pageSize, Book book) {
        System.out.println("参数==>" + book);
        IPage<Book> page = bookService.queryPage(currentPage, pageSize,book);
        return new R(true, page);
    }
    

​ 业务层接口功能开发

IPage<Book> queryPage(int currentPage, int pageSize, Book book);

@Service
public class BookServiceImpl extends ServiceImpl<BookMapper, Book> implements IBookService {
    @Override
    public IPage<Book> queryPage(int currentPage, int pageSize, Book book) {
        IPage<Book> page = new Page<>(currentPage, pageSize);
        LambdaQueryWrapper<Book> lqw = new LambdaQueryWrapper<>();
        lqw.like(Strings.isNotEmpty(book.getType()), Book::getType, book.getType());
        lqw.like(Strings.isNotEmpty(book.getName()), Book::getName, book.getName());
        lqw.like(Strings.isNotEmpty(book.getDescription()), Book::getDescription, book.getDescription());
        bookMapper.selectPage(page, lqw);
        return page;
    }
}

至此,整合完毕,收工!

你可能感兴趣的:(Java框架,JavaEE,后端,java,spring,boot)