SpringBoot教程:基本涵盖日常开发所有知识点

定义

spring boot 特性:
1、创建一个独立的spring 应用;
2、内嵌Tomcat
3、提供 starter 依赖
4、提供第三方配置
5、提供生产就绪的特性
6、配置更简单

第一个SpringBoot应用

http://c.biancheng.net/spring_boot/example.html

SpringBoot注解

@RestController

在Spring中@RestController的作用等同于@Controller + @ResponseBody。

@GetMapping/@PostMapping

在Spring中@GetMapping()/@PostMapping等同于传统的@RequestMapping来编写应该是@RequestMapping(value = “”}, method = RequestMethod.GET/POST)

SpringBoot启动器starter

Spring Boot 将日常企业应用研发中的各种场景都抽取出来,做成一个个的 starter(启动器),starter 中整合了该场景下各种可能用到的依赖,用户只需要在 Maven 中引入 starter 依赖,SpringBoot 就能自动扫描到要加载的信息并启动相应的默认配置。starter 提供了大量的自动配置,让用户摆脱了处理各种依赖和配置的困扰。所有这些 starter 都遵循着约定成俗的默认配置,并允许用户调整这些配置,即遵循“约定大于配置”的原则。
可以看看:http://c.biancheng.net/spring_boot/starter.html

SpringBoot的yml配置文件

http://c.biancheng.net/spring_boot/yaml.html

bootstrap.yml配置文件

bootstrap.yml和application.yml差不多
区别:

  • bootstrap.yml的优先级大于application.yml
  • spplication.yml主要用于项目的配置
  • bootstrap.yml用于:
    • SpringCloud
    • 加密解密
    • 固定参数

devtools自动重启实现

springboot通过devtools实现了可以不用重启,自动的执行了更新后的java代码,不包括配置信息。
实现步骤:

  1. 引入依赖:
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-devtools</artifactId>
   <optional>true</optional>
</dependency>
  1. 更改内容后,点击ctrl+F9

propertis配置

在springBoot里面,一般的常量配置都使用properties配置
使用步骤:

  1. 新建一个properties文件放在resource里面MyConfig.properties
user.userName=叼毛
user.age=20
  1. 在需要用的地方写入注解
package org.example.pojo;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;

@Component
@ConfigurationProperties("user")
@PropertySource(value = "classpath:MyConfig.properties", encoding = "utf-8")
public class MyConfig {

    private String userName;
    private Integer age;

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}

3 引入依赖

<dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-configuration-processor</artifactId>
     </dependency>

SpringBoot-整合redis

配置步骤:

1.引入依赖

<!-- 引入 redis 依赖 -->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-redis</artifactId>
	<!--<version>2.1.5.RELEASE</version>-->
</dependency>

<dependency>
	<groupId>com.squareup.okhttp3</groupId>
	<artifactId>okhttp</artifactId>
</dependency>

2.配置yml

spring:
  redis:
    database: 0
    host: 192.168.1.201
    port: 6379
    password: 123456

3.拷贝RedisOperator

4.controller测试redis中set和get

@Autowired
private RedisOperator redis;
    
@GetMapping("/redis")
public Object redis() {
    redis.set("name", "国产凌凌漆");
    return redis.get("name");
}

自定义启动logo

启动logo设置为图片

spring
	banner:
	    image:
	      location: classpath:img/cat.png
	      pixelmode: block

启动logo设置为文本

spring:
  banner:
    location: classpath:banner/banner.txt

web请求静态资源

springboot里面的静态资源默认路径是

classpath:/static
classpath:/public
classpath:/resources
classpath:/META-INF/resources

请求静态资源执行路径默认到static,可以直接访问,例如:http://localhost:8080/cat.png
可以通过yml设置更改默认路径
自定义请求路径配置

spring:
  mvc:
    static-path-pattern: /abc/**   # 示例:http://localhost:8080/abc/cat.png

自定义静态资源配置路径

spring.web.resources.static-locations=classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/,classpath:/webapp/

Restfull接口请求风格

Restfull请求规范
实际指接口请求注解
强规范
get(查询) post(新增) put:修改 Delete:删
弱规范
get(查询) post(新增 ,修改 ,修改 )
示例:

package org.example.controller;

import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("stu")
public class RestfulController {

    @GetMapping("query")
    public String query(){
        return "query";
    }

    @PostMapping("create")
    public String create(){
        return "create";
    }

    @PutMapping("update")
    public String update(){
        return "update";
    }

    @DeleteMapping
    public String delete(){
        return "delete";
    }
}

接口参数常用注解

  • @PathVariable:用于获取路径里面的参数
  • @RequestParam:用于获取URL中的请求参数,如果参数变量名保持一致,该注解可以省略
  • @RequestBody:用于获取body里面的信息
  • @RequestHeader:用于获取请求头里面的数据
  • @CookieValue:用于获取cookie里面的信息
package org.example.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;

import java.util.Map;

@Slf4j
@RestController
@RequestMapping("param")
public class ParamsController {

    @GetMapping("{stuId}/query")
    public String query(@PathVariable("stuId") String stuId,
                        @RequestParam String name,
                        @RequestParam String age){
        /**
         * @PathVariable:用于获取路径里面的参数,根据如上去使用
         * @RequestParam:用于获取URL中的请求参数,如果参数变量名保持一致,该注解可以省略
         */
        log.error("stuId:" + stuId);
        log.info("name:" + name);
        log.info("age:" + age);
        return "query";
    }

    @PostMapping("create")
    public String create(@RequestBody Map<String, Object> map,
                         @RequestHeader("token") String token,
                         @CookieValue("clientId") String clientId){

        /**
         * @RequestBody:用于获取body里面的信息
         * @RequestHeader:用于获取请求头里面的数据
         * @CookieValue:用于获取cookie里面的信息
         */
        log.info(map.toString());
        log.info(token);
        log.info(clientId);
        return "create";
    }
}

返回值

一般封装成一个对象

package com.imooc.utils;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.databind.ObjectMapper;

/**
 * 
 * @Title: JSONResult.java
 * @Package com.imooc.utils
 * @Description: 自定义响应数据结构
 * 				本类可提供给 H5/ios/安卓/公众号/小程序 使用
 * 				前端接受此类数据(json object)后,可自行根据业务去实现相关功能
 * 
 * 				200:表示成功
 * 				500:表示错误,错误信息在msg字段中
 * 				501:bean验证错误,不管多少个错误都以map形式返回
 * 				502:拦截器拦截到用户token出错
 * 				555:异常抛出信息
 * 				556: 用户qq校验异常
 * @Copyright: Copyright (c) 2020
 * @Company: www.imooc.com
 * @author liutianyu 
 * @version V1.0
 */
public class JSONResult {

    // 定义jackson对象
    private static final ObjectMapper MAPPER = new ObjectMapper();

    // 响应业务状态
    private Integer status;

    // 响应消息
    private String msg;

    // 响应中的数据
    private Object data;
    
    @JsonIgnore
    private String ok;	// 不使用

    public static JSONResult build(Integer status, String msg, Object data) {
        return new JSONResult(status, msg, data);
    }

    public static JSONResult build(Integer status, String msg, Object data, String ok) {
        return new JSONResult(status, msg, data, ok);
    }
    
    public static JSONResult ok(Object data) {
        return new JSONResult(data);
    }

    public static JSONResult ok() {
        return new JSONResult(null);
    }
    
    public static JSONResult errorMsg(String msg) {
        return new JSONResult(500, msg, null);
    }
    
    public static JSONResult errorMap(Object data) {
        return new JSONResult(501, "error", data);
    }
    
    public static JSONResult errorTokenMsg(String msg) {
        return new JSONResult(502, msg, null);
    }
    
    public static JSONResult errorException(String msg) {
        return new JSONResult(555, msg, null);
    }
    
    public static JSONResult errorUserQQ(String msg) {
        return new JSONResult(556, msg, null);
    }

    public JSONResult() {

    }

    public JSONResult(Integer status, String msg, Object data) {
        this.status = status;
        this.msg = msg;
        this.data = data;
    }
    
    public JSONResult(Integer status, String msg, Object data, String ok) {
        this.status = status;
        this.msg = msg;
        this.data = data;
        this.ok = ok;
    }

    public JSONResult(Object data) {
        this.status = 200;
        this.msg = "OK";
        this.data = data;
    }

    public Boolean isOK() {
        return this.status == 200;
    }

    public Integer getStatus() {
        return status;
    }

    public void setStatus(Integer status) {
        this.status = status;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }

	public String getOk() {
		return ok;
	}

	public void setOk(String ok) {
		this.ok = ok;
	}

}

文件上传

文件上传大小限制

spring:
  servlet:
    multipart:
      max-file-size: 600KB  
      max-request-size: 2MB

在这里插入图片描述

在这里插入图片描述

自定义异常界面

自定义封装异常

package org.example.exception;

public class MyCustomException extends RuntimeException {
    public MyCustomException(String message) {
        super(message);
    }
}

拦截器

1.创建拦截器

package org.example.interceptor;

import lombok.extern.slf4j.Slf4j;
import org.example.exception.GraceException;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Slf4j
public class UserInfoInterceptor implements HandlerInterceptor {

    /**
     * @Description: 拦截请求,访问controller之前
     * @param: [request, response, handler]
     * @return: boolean
     * @author: liuyong
     * @date: 2022/8/3
     * @time: 16:05
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String userId = request.getHeader("userId");
        String userToken = request.getHeader("userToken");
        if(userId == null || userId.isEmpty() || userToken == null || userToken.isEmpty()) {
           log.error("用户校验不通过,信息不完整");
            GraceException.display("用户校验不通过,信息不完整");
           return false;
        }
        return false;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        log.info("interceptor...");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        log.info("afterCompletion");
    }
}

2.注册拦截器

package org.example.config;

import org.example.interceptor.UserInfoInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {

    @Bean
    public UserInfoInterceptor userInfoInterceptor() {
        return new UserInfoInterceptor();
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(userInfoInterceptor()).addPathPatterns("/upload");
    }
}

定时任务

package com.imooc.config;

import com.imooc.service.OrderService;
import com.imooc.utils.DateUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
public class OrderJob {

    @Autowired
    private OrderService orderService;

    /**
     * 使用定时任务关闭超期未支付订单,会存在的弊端:
     * 1. 会有时间差,程序不严谨
     *      10:39下单,11:00检查不足1小时,12:00检查,超过1小时多余39分钟
     * 2. 不支持集群
     *      单机没毛病,使用集群后,就会有多个定时任务
     *      解决方案:只使用一台计算机节点,单独用来运行所有的定时任务
     * 3. 会对数据库全表搜索,及其影响数据库性能:select * from order where orderStatus = 10;
     * 定时任务,仅仅只适用于小型轻量级项目,传统项目
     *
     */

//    @Scheduled(cron = "0/3 * * * * ?")
    @Scheduled(cron = "0 0 0/1 * * ?")
    public void autoCloseOrder() {
        orderService.closeOrder();
        System.out.println("执行定时任务,当前时间为:"
                + DateUtil.getCurrentDateString(DateUtil.DATETIME_PATTERN));
    }
}

整合高性能HicarICP数据源

1.引入依赖pom

<!--        jdbc启动器-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>

<!--        mysql驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

2.数据源配置

spring:
  datasource:                                              # 数据库的相关配置节点
    type: com.zaxxer.hikari.HikariDataSource               # 指定数据源类型
    driver-class-name: com.mysql.cj.jdbc.Driver            # mysql驱动
    url: jdbc:mysql://localhost:3306/springboot-demo?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true    # 数据库地址
    username: root                                         # 用户名
    password: root                                         # 密码
    hikari:
      connection-timeout: 30000                   # 等待连接池分配的最大时长(毫秒),超过这个时长还没可用的连接则发生SQLException,默认:30秒
      minimum-idle: 5                             # 最小连接数
      maximum-pool-size: 20                       # 最大连接数
      auto-commit: true                           # 自动提交
      idle-timeout: 600000                        # 连接超时的最大时长(毫秒),超时则被释放(retired),默认:10分钟
      pool-name: DataSourceHikariCP               # 连接池名称
      max-lifetime: 1800000                       # 连接的生命时长(毫秒),超时而且没被使用则被释放(retired),默认:30分钟
      connection-test-query: SELECT 1

springboot整合mybatis配置

1.引入依赖
默认第一个依赖就行了,第二个依赖可以调整mapper接口继承通用mapper

<!--        mybatis启动器-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.0</version>
        </dependency>

<!--        mapper启动器:myMapper还封装了常用查询语句-->
        <dependency>
            <groupId>tk.mybatis</groupId>
            <artifactId>mapper-spring-boot-starter</artifactId>
            <version>2.1.5</version>
        </dependency>

2.mybatis配置

# myBatis相关配置
mybatis:
  type-aliases-package: org.example.pojo          # mapper逆向生成工具后实体包存放的位置
  mapper-locations: classpath:mapper/*.xml        # 所有mapper映射的文件所在目录
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl    # 开启mybatis日志实现
# 通用mapper配置
mapper:
  mappers: org.example.my.mapper.MyMapper         # 所有mapper都需要实现的接口
  not-empty: true                                 # 在进行数据库操作的时候,判断一个属性是否为空的时候,是否需要自动追加username != ""
  identity: MYSQL

3.编写自己的通用mapper

package org.example.my.mapper;

import tk.mybatis.mapper.common.Mapper;
import tk.mybatis.mapper.common.MySqlMapper;

/**
 * 继承自己的myMapper
 */
public interface MyMapper<T> extends Mapper<T>, MySqlMapper<T> {

}

4.常用mapper示例

package org.example.mapper;

import org.example.my.mapper.MyMapper;
import org.example.pojo.Teacher;
import org.springframework.stereotype.Repository;

@Repository
public interface TeacherMapper extends MyMapper<Teacher> {
}

接口参数验证框架

1.引入依赖

<!--        引入参数验证框架-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>

2.bean添加验证注解
常用验证注解有:
在这里插入图片描述

示例:

package org.example.pojo.bo;

import lombok.Data;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;

@Data
public class TeacherBO {

    private String id;

    @NotBlank
    private String name;

    @NotNull
    private Integer age;

}

3.controller层代码配置

    @PostMapping("save")
    public JSONResult save(@Valid @RequestBody TeacherBO teacherBO,
                           BindingResult bindingResult){
        String sid = UUID.randomUUID().toString().trim();

        if (bindingResult.hasErrors()) {
            List<FieldError> fieldErrors = bindingResult.getFieldErrors();
            HashMap<String, String> m = new HashMap<>();
            for (FieldError error : fieldErrors) {
                m.put(error.getField(),error.getDefaultMessage());
            }
            return JSONResult.error(m.toString());
        }


        Teacher teacher = new Teacher();
        BeanUtils.copyProperties(teacherBO, teacher);
        teacher.setId(sid);
//        teacher.setName("展示");
//        teacher.setAge(18);

        teacherService.save(teacher);

        return new JSONResult("1");
    }

事务管理

事务的传播属性:

  • required:当存在事务时沿用上一个事务,没有则新建一个事务
  • suppots:如果当前存在事务则沿用,如果没有事务则不用
  • mandatory:当前必须存在一个事务,如果没有则抛出异常
  • required_new:如果当前存在事务则创建一个事务,如果没有同required
  • not_supported:如果当前存在事务,则挂起事务,自己不用事务
  • never:如果当前存在事务,则抛出异常
  • nested:如果当前存在事务,则开启子事务(嵌套事务),嵌套事务是独立提交或回滚;
    如果当前没有事务,则同required,如果主事务提交或回滚,会携带子事务,但子事务异常,主事务可回滚可不回滚

示例:

    @Override
    @Transactional(propagation = Propagation.SUPPORTS)
    public List<Teacher> queryByCondition(String name) {
        Example example = new Example(Teacher.class);
        Example.Criteria criteria = example.createCriteria();
        criteria.andEqualTo("name",name);

        List<Teacher> teachers = teacherMapper.selectByExample(example);
        return teachers;
    }

整合自定义阿里druid数据源

1.引入依赖

<!-- https://mvnrepository.com/artifact/com.alibaba/druid-spring-boot-starter -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.2.6</version>
</dependency>

2.配置数据源类型

spring: 
	datasource: 
		type: com.alibaba.druid.pool.DruidDataSource  # 配置自定义的数据源:阿里druid

3.配置druid连接池

spring: 
	datasource: 
		druid: 
			connection-timeout: 30000

开启mybatis的sql执行日志打印

mybatis:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl    # 开启mybatis日志实现

使用AOP监控service执行时间

package org.example.config;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

@Aspect
@Slf4j
@Component
public class logAdvice {


    /**
     * AOP的通知类型
     * 前置通知
     * 后置通知
     * 环绕通知
     * 异常通知
     * 最终通知
     */

    @Around("execution(* org.example.service.impl..*.*(..))")
    public Object printLogOfService(ProceedingJoinPoint joinPoint) throws Throwable {

        log.info("正在执行{}.{}", joinPoint.getTarget().getClass(), joinPoint.getSignature().getName());

        long start = System.currentTimeMillis();

        Object result = joinPoint.proceed();

        // 3. 记录service的方法执行之后的时间
        long end = System.currentTimeMillis();
        // 4. 得到service的方法执行的时间差
        long takeTime = end - start;

        // 5. 根据时间差来进行判断,并且打印日志的输出到控制台
        if (takeTime > 3000) {
            log.error("当前执行时间耗时太久了,为{}毫秒", takeTime);
        } else if (takeTime > 2000) {
            log.warn("当前执行时间耗时稍微有点长,应该后期考虑优化,为{}毫秒", takeTime);
        } else {
            log.info("当前执行时间耗时为{}毫秒", takeTime);
        }

        return result;
    }
}


版权声明:本文为u013303534原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。