SpringBoot使用教程

文章目录

一、SpringBoot介绍


1.1 SpringBoot简介

SpringBoot是由Pivotal团队研发的,SpringBoot并不是一门新技术,只是将之前常用的Spring,SpringMVC,data-jpa等常用的框架封装到了一起,帮助你隐藏这些框架的整合细节,实现敏捷开发。

Spring Boot是基于约定优于配置的,主要作用就是用来简化Spring应用的初始搭建以及开发过程!

后期要学习的微服务框架SpringCloud需要建立在SpringBoot的基础上。

1.2 SpringBoot的特点

1.基于Spring的开发提供更快的入门体验。

2.开箱即用,没有代码生成,也无需XML配置,同时也可以修改默认值来满足特定的需求。

3.提供了一些大型项目中常见的非功能性特性,外部配置等。

4.SpringBoot不是对Spring功能上的增强,而是提供了一种快速使用Spring的方式。

1.3 SpringBoot的核心功能

1.起步依赖

起步依赖本质上是一个Maven项目对象模型(Project Object Model,POM),定义了对其他库的传递依赖,这些东西加在一起即支持某项功能。

简单的说,起步依赖就是将具备某种功能的坐标打包到一起,并提供一些默认的功能。

2.自动配置

Spring Boot的自动配置是一个运行时(更准确地说,是应用程序启动时)的过程,考虑了众多因素,才决定Spring配置应该用哪个,不该用哪个。该过程是Spring自动完成的。

二、SpringBoot介绍


SpringBoot是由Pivotal团队研发的,SpringBoot并不是一门新技术,只是将之前常用的Spring,SpringMVC,data-jpa等常用的框架封装到了一起,帮助你隐藏这些框架的整合细节,实现敏捷开发。

SpringBoot就是一个工具集。

SpringBoot特点:

  • SpringBoot项目不需要模板化的配置。
  • SpringBoot中整合第三方框架时,只需要导入相应的starter依赖包,就自动整合了。
  • SpringBoot默认只有一个.properties的配置文件,不推荐使用xml,后期会采用.java的文件去编写配置信息。
  • SpringBoot工程在部署时,采用的是jar包的方式,内部自动依赖Tomcat容器,提供了多环境的配置。
  • 后期要学习的微服务框架SpringCloud需要建立在SpringBoot的基础上。

三、SpringBoot快速入门

创建springboot有三种方式:

​ 1,使用网站https://start.spring.io/

​ 2.常规的创建方式(使用idea)最常规的

​ 3.使用maven(好多东西需要自己写)


网站创建地址:https://start.spring.io/

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uBvp7ulv-1601205810089)(Pictures\1.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2j7YqdWK-1601205810093)(Pictures\2.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LywEmy4j-1601205810094)(Pictures\3.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cgtLYkop-1601205810096)(Pictures\4.png)]

运行配置类,看到如下页面,表示启动成功!

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jufE6mpw-1601205810098)(Pictures\5.png)]

配置自动编译

手动编写Controller进行进一步测试****(注意:需要将controller类,放在启动类的子包中或者同级包下)***

package com.qf.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class UserController {

    @RequestMapping("/login")
    public String login(){

        System.out.println("登录");

        return "success";
    }
}

重新启动配置类,访问:http://localhost:8080/login

四、SpringBoot热部署配置


为了方便开发,可以在创建项目时手动勾选热部署,或导入该依赖,就不需要每次重启配置类

<!--热部署配置-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <optional>true</optional>
</dependency>

配置自动编译

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9LbAIC1m-1601205810099)(Pictures\7.png)]

最后Shift+Ctrl+Alt+/,选择Registry(选完之后再次查看一下是否勾选上)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Yx9STuuk-1601205810101)(Pictures\8.png)]

再次重新运行一次配置类即可!

五、SpringBoot中的默认配置


可以从jar包中找到SpringBoot的默认配置文件位置

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v0Vn4d6q-1601205810102)(Pictures\9.png)]

SpringBoot是基于约定的,所以很多配置都有默认值,但如果想使用自己的配置替换默认配置的话,就可以使用application.properties或者application.yml(application.yaml)进行配置,SpringBoot默认会从Resources目录下加载application.properties或application.yml(application.yaml)文件。

其中,application.properties文件是键值对类型的文件,除此之外,SpringBoot还可以使用yml文件进行配置,YML文件格式是YAML (YAML Aint Markup Language)编写的文件格式,YAML是一种直观的能够被电脑识别的的数据数据序列化格式,并且容易被人类阅读,容易和脚本语言交互的,可以被支持YAML库的不同的编程语言程序导入,比如: C/C++, Ruby, Python, Java, Perl, C#, PHP等。YML文件是以数据为核心的,比传统的xml方式更加简洁,YML文件的扩展名可以使用.yml或者.yaml。

application.properties方式修改默认配置

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dep78VDl-1601205810104)(Pictures\10.png)]

application.yml方式修改默认配置:(注意:yml文件中空格表示层级关系)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SU7ySAyP-1601205810105)(Pictures\11.png)]

yml文件支持的配置

#普通数据的配置
name: jack

#对象的配置
user:
  username: rose
  password: 123

#配置数组
array:
    beijing,
    tianjin,
    shanghai

#配置集合
yangl:
  test:
    name: tom
    arr: 1,jack,2,tom  #这种对象形式的,只能单独写一个对象去接收,所以无法使用@value注解获取
    list1:
      - zhangsan
      - lisi
    list2:
      - driver: mysql
        port: 3306
      - driver: oracle
        port: 1521
    map:
      key1: value1
      key2: value2

#端口配置
server:
  port: 8081

把yml文件中配置的内容注入到成员变量中,

第一种方式,创建UserController,使用@Value注解方式注入

package com.qf.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;

@RestController
public class UserController {

    @Value("${name}")
    private String name;

    @Value("${user.username}")
    private String username;

    @Value("${user.password}")
    private String password;

    @Value("${array}")
    private String [] array;

    @RequestMapping("/test")
    public String[] test(){

        System.out.println(name);
        System.out.println(username);
        System.out.println(password);
        System.out.println(array[0]);
        
        return array;
    }
}

第二种方式,使用@ConfigurationProperties注解方式,提供GET/SET方法

创建YmlController

package com.qf.controller;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Arrays;
import java.util.List;
import java.util.Map;

@RestController
@ConfigurationProperties(prefix = "yangl.test")
public class YmlController {

    private String name;

    private String[] arr;

    private List<String> list1;

    private List<Map<String,String>> list2;

    private Map<String,String> map;


    @RequestMapping("/yml")
    public void  yml(){

        System.out.println(name);
        System.out.println(Arrays.toString(arr));
        System.out.println(list1);
        System.out.println(list2);
        System.out.println(map);

    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String[] getArr() {
        return arr;
    }

    public void setArr(String[] arr) {
        this.arr = arr;
    }

    public List<String> getList1() {
        return list1;
    }

    public void setList1(List<String> list1) {
        this.list1 = list1;
    }

    public List<Map<String, String>> getList2() {
        return list2;
    }

    public void setList2(List<Map<String, String>> list2) {
        this.list2 = list2;
    }

    public Map<String, String> getMap() {
        return map;
    }


    public void setMap(Map<String, String> map) {
        this.map = map;
    }
}

如果使用@ConfigurationProperties注解时提示以下信息

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ds0OUQ5C-1601205810107)(Pictures\12.png)]

导入以下依赖即可(也可以不导入)

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

六、SpringBoot中的异常处理


6.1 创建ExceptionController测试类
package com.qf.controller;

import com.qf.exception.MyException;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ExceptionController {

    @RequestMapping("/exception")
    public String exception(){

        int i = 1/0;

        return "exception";
    }

    @RequestMapping("/myexception")
    public String myexception()throws MyException{

        throw new MyException("自定义异常");

    }
}

6.2 创建自定义异常类
package com.qf.exception;

public class MyException extends Exception{

    public MyException(String msg){
        super(msg);
    }
}

6.3 创建MyExceptionHandler全局异常处理类
package com.qf.exception;

import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;

//拦截异常
@RestControllerAdvice
public class MyExceptionHandler {

    //处理指定异常
    @ExceptionHandler(value = Exception.class)
    public Object Handler1(Exception e, HttpServletRequest request){

        System.out.println("Handler1");

        HashMap<String, Object> map = new HashMap<>();

        map.put("msg",e.getMessage());
        map.put("url",request.getRequestURL());

        return map;
    }

    @ExceptionHandler(value = MyException.class)
    public Object Handler2(MyException e, HttpServletRequest request){

        System.out.println("Handler2");

        HashMap<String, Object> map = new HashMap<>();

        map.put("msg",e.getMessage());
        map.put("url",request.getRequestURL());

        return map;
    }
}


访问:http://localhost:8080/exception 以及 http://localhost:8080/myexception测试

七、SpringBoot中的过滤器(Listener操作同理)


7.1 创建过滤器
package com.qf.filter;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebFilter(urlPatterns = "/filter/*")//指定拦截路径
public class LoginFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("LoginFilter ");

        HttpServletRequest req = (HttpServletRequest)request;
        HttpServletResponse resp = (HttpServletResponse)response;

        chain.doFilter(req,resp);
    }
}


7.2 创建Controller测试类
package com.qf.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/filter")
public class FilterController {

    @RequestMapping("/login")
    public String login() {

       System.out.println("登录");

        return "login";
    }
}


7.3 在启动类添加@ServletComponentScan注解
package com.qf;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;

@SpringBootApplication
@ServletComponentScan//Servlet、Filter、Listener可以直接通过@WebServlet、@WebFilter、@WebListener注解自动注册
public class Springboot02Application {

    public static void main(String[] args) {
        SpringApplication.run(Springboot02Application.class, args);
    }

}

访问controller测试即可

八、SpringBoot中的拦截器


8.1.创建自定义拦截器
package com.qf.interceptor;

import org.omg.PortableInterceptor.Interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

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


public class MyInterceptor implements HandlerInterceptor {

    
    //进入controller方法之前调用
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle");
        return true;//true表示放行,false表示不放行
    }

    //调用完controller之后,视图渲染之前
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle");
    }

    //页面跳转之后,整个流程执行之后,一般用于资源清理操作
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion");
    }
}


8.2创建拦截器配置类
package com.qf.interceptor;

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 MyInterceptorConfig implements WebMvcConfigurer {


    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //设置拦截器并指定拦截路径
        registry.addInterceptor(new MyInterceptor()).addPathPatterns("/interceptor/*");
        //registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**");//拦截所有
        //registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**").excludePathPatterns("/test");//指定不拦截
        //添加自定义拦截器
        WebMvcConfigurer.super.addInterceptors(registry);
    }
}


8.3在static目录下创建index.html以及controller测试类
package com.qf.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class InterceptorController {

        @RequestMapping("/interceptor/myinterceptor")
        public String myinterceptor(){

            System.out.println("myinterceptor");

            return "/index.html";
        }
}


访问controller测试即可

九、SpringBoot整合MyBatis


9.1导入依赖
<!--  引入mybatis相关依赖,必须写版本号 -->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.1</version>
</dependency>

<!--  引入mysql相关依赖,如果不写版本号,引入的8.0以上版本
 可以设置为其他版本
 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>

9.2在application.properties中配置数据库信息
# 数据库配置
# 默认使用mysql的驱动是8.x的版本,注意driver-class-name,url中增加时区的配置
spring.datasource.url=jdbc:mysql://localhost:3306/java1909?serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

9.3使用之前的Account表,创建实体类
package com.qf.pojo;

public class Account {
    
    private Integer id;
    private String name;
    private Double money;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Double getMoney() {
        return money;
    }

    public void setMoney(Double money) {
        this.money = money;
    }

    @Override
    public String toString() {
        return "Account{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", money=" + money +
                '}';
    }
}


9.4创建mapper
package com.qf.mapper;

import com.qf.pojo.Account;
import org.springframework.stereotype.Repository;
import java.util.List;

@Repository
public interface AccountMapper {

    public List<Account> findAll();
}


9.5在src\main\resources\mapper路径下创建对应的AccountMapper.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.qf.mapper.AccountMapper">

    <select id="findAll" resultType="com.qf.pojo.Account">
        select * from account
    </select>

</mapper>


9.6在application.properties中添加mybatis的信息
# mybatis配置
# 配置别名需要扫描的包
mybatis.type-aliases-package=com.qf.pojo
# 引入映射文件
mybatis.mapper-locations=classpath:mapper/*.xml
# 配置日志在控制台显示sql语句
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

9.7创建service
package com.qf.service;

import com.qf.pojo.Account;

import java.util.List;

public interface AccountService {

    public List<Account> findAll();
}


9.8创建serviceImpl
package com.qf.service.impl;

import com.qf.mapper.AccountMapper;
import com.qf.pojo.Account;
import com.qf.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class AccountServiceImpl implements AccountService {
    
    @Autowired
    private AccountMapper accountMapper;
    
    @Override
    public List<Account> findAll() {
        return accountMapper.findAll();
    }
}


9.9创建controller
package com.qf.controller;

import com.qf.pojo.Account;
import com.qf.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;

@RestController
@RequestMapping("/account")
public class AccountController {

    @Autowired
    private AccountService accountService;

    @RequestMapping("/findAll")
    public List<Account> findAll(){

        List<Account> accounts = accountService.findAll();

        return accounts;
    }
}


9.10启动配置类,进行测试(注意:需要在启动类上添加@MapperScan扫描Mapper)
package com.qf;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@MapperScan("com.qf.mapper")
public class Springboot02Application {

    public static void main(String[] args) {
        SpringApplication.run(Springboot02Application.class, args);
    }

}

9.11分页插件
9.11.1导入依赖
   <dependency>
        <groupId>com.github.pagehelper</groupId>
        <artifactId>pagehelper-spring-boot-starter</artifactId>
        <version>1.2.13</version>
   </dependency>

9.11.2添加方法
   @RequestMapping("/findByPage")
    public PageInfo findByPage(@RequestParam(defaultValue = "1") Integer pageNum,
                               @RequestParam(defaultValue = "2") Integer pageSize){

        PageHelper.startPage(pageNum,pageSize);

        List<Account> accounts = accountService.findAll();

        PageInfo pageInfo = new PageInfo(accounts);

        return pageInfo;
    }

启动工程,进行测试

十、使用Druid连接池进行测试


10.1导入依赖
<dependency>
   <groupId>com.alibaba</groupId>
   <artifactId>druid-spring-boot-starter</artifactId>
   <version>1.1.10</version>
</dependency>

10.2在application.properties文件中添加配置
#使用阿里巴巴druid数据源,默认使用自带的
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource

10.3使用yml方式配置,需要创建application.yml
mybatis:
  type-aliases-package: com.qf.pojo
  mapper-locations: classpath:mapper/*.xml
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    url: jdbc:mysql://localhost:3306/java2001?serverTimezone=Asia/Shanghai
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver


启动工程,进行测试

整合银行项目

我们都知道需要通过控制器跳转才能访问到templates下的html文件
因为springboot默认访问static,resources,public这些文件夹下的文件,而没有默认访问templates下的
我们需要在配置加上

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

就可以直接访问了

十一、SpringBoot整合FreeMarker(了解,自己练习)


11.1导入依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>

11.2在application.properties文件添加配置
########## 配置freemarker ##########
#是否开启缓存
spring.freemarker.cache=false
#路径
spring.freemarker.template-loader-path=classpath:/templates
#文件后缀
spring.freemarker.suffix=.ftl

spring.freemarker.charset=UTF-8
spring.freemarker.content-type=text/html

11.3创建实体类
package com.qf.pojo;

public class Student {

    private Integer id;
    private String name;
    private Integer age;

    private String address;

    public Student(Integer id, String name, Integer age, String address) {

        this.id = id;
        this.name = name;
        this.age = age;
        this.address = address;
    }

    public Student() {
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {

        return name;
    }

    public void setName(String name) {
        this.name = name;
    }


    public Integer getAge() {
        return age;
    }

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

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}


11.4在templates目录下创建student.ftl文件(然后把该ftl文件拷贝到D:/ftl目录下,一会用)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <table border="1" width=600>
        <tr>
            <th>index</th>
            <th>id</th>
            <th>name</th>
            <th>age</th>
            <th>address</th>
        </tr>
        <#list students as student>
            <#if student_index % 2 == 0>
            <tr bgcolor="red">
                <#else>
            <tr bgcolor="yellow">
            </#if>
            <td>${student_index}</td>
            <td>${student.id}</td>
            <td>${student.name}</td>
            <td>${student.age}</td>
            <td>${student.address}</td>
            </tr>
        </#list>
    </table>
</body>
</html>

11.5创建controller
package com.qf.controller;

import com.qf.pojo.Student;
import freemarker.template.Configuration;
import freemarker.template.Template;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.FileWriter;
import java.util.ArrayList;
import java.util.HashMap;

@Controller
@RequestMapping("/freemarker")
public class FreeMarkerController {

    @RequestMapping("/student")
    public String hello(Model model) throws Exception {

        //创建List集合获取多个元素
        ArrayList<Student> students = new ArrayList<>();
        students.add(new Student(1, "jack", 18, "郑州二七"));
        students.add(new Student(2, "rose", 19, "郑州中原"));
        students.add(new Student(3, "tom", 20, "郑州金水"));

        model.addAttribute("students",students);

        return "/student";
    }

    //生成静态页面的方法
    @RequestMapping("createHtml")
    @ResponseBody
    public String createHtml()throws Exception{
        //获取配置对象
        Configuration configuration = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
        //设置字符集
        configuration.setDefaultEncoding("utf-8");

        //设置加载的模版目录
        configuration.setDirectoryForTemplateLoading(new File("D:/ftl"));
        //创建List集合获取多个元素
        ArrayList<Student> students = new ArrayList<>();
        students.add(new Student(1,"张三",18,"北京"));
        students.add(new Student(2,"李四",19,"上海"));
        students.add(new Student(3,"王五",20,"广州"));

        //使用map集合加载数据
        HashMap<String,ArrayList> map = new HashMap<>();
        map.put("students",students);

        //创建输出流对象
        FileWriter fileWriter = new FileWriter(new
                File("D:/ftl_html/student.html"));

        //获取加载的模板
        Template template = configuration.getTemplate("student.ftl");
        //生成html文件
        template.process(map,fileWriter);
        //关流
        fileWriter.close();

        return "success";
    }

}


启动工程,进行测试

十二、SpringBoot整合Thymeleaf(不重要,自己练习)


12.1导入依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

12.2在application.properties文件添加配置
########## 配置thymeleaf ##########
spring.thymeleaf.cache=false
spring.thymeleaf.encoding=utf-8

spring.thymeleaf.prefix=classpath:/templates

spring.thymeleaf.suffix=.html

spring.thymeleaf.mode=HTML5
spring.thymeleaf.servlet.content-type=text/html

12.3创建实体类
package com.qf.pojo;

import java.util.Date;

public class User {

    private Integer id;
    private String username;
    private String password;

    private Date birthday;

    public User(Integer id, String username, String password, Date birthday) {
        this.id = id;
        this.username = username;
        this.password = password;
        this.birthday = birthday;
    }
    
    public User() {
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", birthday=" + birthday +
                '}';
    }
}


12.4创建Controller
package com.qf.controller;

import com.qf.pojo.User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.ArrayList;
import java.util.Date;

@Controller
@RequestMapping("user")
public class UserController {


    @RequestMapping("findAll")
    public String findAll(Model model) {

        ArrayList<User> users = new ArrayList<>();

        users.add(new User(1001, "张三", "123", new Date()));
        users.add(new User(1002, "李四", "456", new Date()));
        users.add(new User(1003, "王五", "789", new Date()));

        model.addAttribute("users", users);

        return "/list";
    }

    @RequestMapping("findById")
    public String findById(Model model, String uid) {
        System.out.println(uid);

        if (uid.equals("1001")) {
            User user = new User(1001, "张三", "123", new Date());
            model.addAttribute("user", user);
        }

        return "/queryOne";

    }

}

12.5在templates目录下创建list.html以及queryOne.html

list.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>


<div th:if="${users!=null}">

    <table border="1" width="600">
        <tr th:each="user,state : ${users}">
            <td th:text="${state.count}"></td>
            <td th:text="${user.id}"></td>
            <td th:text="${user.username}"></td>

            <td th:text="${#dates.format(user.birthday, 'yyyy-MM-dd HH:mm:ss')}"></td>
        </tr>
    </table>

    <hr>
    <table border="1" width="600">
        <!-- 第二个变量,可以获取遍历的元素的状态-->
        <tr th:each="user,state : ${users}">
            <td th:text="${state.index}"></td>
            <td th:text="${user.id}"></td>
            <td th:text="${user.username}"></td>
            <td th:text="${#dates.format(user.birthday, 'yyyy-MM-dd HH:mm:ss')}"></td>
        </tr>
    </table>
    <hr>

    <table border="1" width="600">
        <!--如果不设置表示状态的变量,默认遍历的元素的变量名+Stat
表示状态的变量-->
        <tr th:each="user : ${users}">
            <td th:text="${userStat.count}"></td>
            <td th:text="${user.id}"></td>
            <td th:text="${user.username}"></td>
            <td th:text="${#dates.format(user.birthday, 'yyyy-MM-
dd HH:mm:ss')}"></td>

        </tr>
    </table>

</div>

<hr color="red">

<table border="1" width="600">
    <tr>
        <th>序号</th>
        <th>姓名</th>
        <th>生日</th>
        <th>详情</th>
    </tr>
    <tr th:each="user : ${users}">
        <td th:text="${userStat.count}"></td>
        <td th:text="${user.username}"></td>
        <td th:text="${#dates.format(user.birthday, 'yyyy-MM-dd HH:mm:ss')}"></td>
        <td><a th:href="@{/user/findById(uid=${user.id})}">查询</a></td>
    </tr>
</table>
</body>
</html>

queryOne.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form th:object="${user}">
    <!-- *{...}选择表达式一般跟在th:object后,直接选择object中的属性-->
    <input type="hidden" th:id="*{id}" name="id">
    用户名:<input type="text" th:value="*{username}" name="username" /><br /><br />
    密码:<input type="text" th:value="*{password}" name="password"/>
</form>

</body>
</html>

十三、SpringBoot整合Redis(重要)


13.1导入依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

13.2在application.properties文件添加配置
#配置redis数据库索引(默认0号库)
spring.redis.database=0
#配置redis服务器ip地址
spring.redis.host=192.168.228.135
#配置redis服务器的端口号
spring.redis.port=6379

13.3创建controller
package com.qf.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/redis")
public class RedisController {

    @Autowired
    private RedisTemplate redisTemplate ;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @RequestMapping("/testString")
    public String test(){

        //redisTemplate.opsForValue();//操作字符串
        //redisTemplate.opsForList();//操作List
        //redisTemplate.opsForSet();//操作Set
        //redisTemplate.opsForZSet();//操作ZSet
        //redisTemplate.opsForHash();//操作Map

        redisTemplate.opsForValue().set("username","jack");
        String username = (String)redisTemplate.opsForValue().get("username");
        System.out.println(username);

        stringRedisTemplate.opsForValue().set("password","123");
        String password = stringRedisTemplate.opsForValue().get("password");

        System.out.println(password);

        return "success";
    }
}

启动工程,进行测试

十四、SpringBoot整合Quartz(定时任务)

Quartz是一个任务调度框架。比如你遇到这样的问题

想每月25号,信用卡自动还款
想每年4月1日自己给当年暗恋女神发一封匿名贺卡
想每隔1小时,备份一下自己的爱情动作片 学习笔记到云盘
这些问题总结起来就是:在某一个有规律的时间点干某件事。并且时间的触发的条件可以非常复杂(比如每月最后一个工作日的17:50),复杂到需要一个专门的框架来干这个事。 Quartz就是来干这样的事,你给它一个触发条件的定义,它负责到了时间点,触发相应的Job起来干活。


14.1导入依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-quartz</artifactId>
</dependency>

14.2创建任务类
package com.qf.quartz;

import org.springframework.scheduling.annotation.Scheduled;
        import org.springframework.stereotype.Component;

        import java.util.Date;

@Component
public class MyQuartz {

    @Scheduled(cron="*/2 * * * * ? ")
    public void testQuartz(){
        System.out.println("testQuartz:"+new Date().toLocaleString());
    }
}


14.3在启动类添加@EnableScheduling配置,然后直接启动测试
package com.qf;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication
@EnableScheduling
public class Springboot02Application {

    public static void main(String[] args) {
        SpringApplication.run(Springboot02Application.class, args);
    }

}

详情见https://blog.csdn.net/jack_bob/article/details/78786740文章

十五、SpringBoot日志


SpringBoot默认使用的日志是Logback,官方建议日志文件命名为:logback-spring.xml

15.1在resources目录下创建logback-spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true -->
<!-- scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。
	 默认的时间间隔为1分钟。 -->
<!-- debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 -->
<configuration  scan="true" scanPeriod="60 seconds" debug="true">

    <!-- 定义变量,可通过 ${log.path}和${CONSOLE_LOG_PATTERN} 得到变量值 -->
    <property name="log.path" value="D:/log" />
    <property name="CONSOLE_LOG_PATTERN"
              value="%d{yyyy-MM-dd HH:mm:ss.SSS} |-[%-5p] in %logger.%M[line-%L] -%m%n"/>

    <!-- 输出到控制台 -->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <!-- Threshold=即最低日志级别,此appender输出大于等于对应级别的日志
             (当然还要满足root中定义的最低级别)
        -->
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>debug</level>
        </filter>
        <encoder>
            <!-- 日志格式(引用变量) -->
            <Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
            <!-- 设置字符集 -->
            <charset>UTF-8</charset>
        </encoder>
    </appender>

    <!-- 追加到文件中 -->
    <appender name="file1" class="ch.qos.logback.core.FileAppender">
        <file>${log.path}/mylog1.log</file>
        <encoder>
            <pattern>${CONSOLE_LOG_PATTERN}</pattern>
        </encoder>
    </appender>

    <!-- 滚动追加到文件中 -->
    <appender name="file2" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 正在记录的日志文件的路径及文件名 -->
        <file>${log.path}/mylog2.log</file>
        <!--日志文件输出格式-->
        <encoder>
            <pattern>${CONSOLE_LOG_PATTERN}</pattern>
            <charset>UTF-8</charset> <!-- 设置字符集 -->
        </encoder>
        <!-- 日志记录器的滚动策略,按日期,按大小记录
             文件超过最大尺寸后,会新建文件,然后新的日志文件中继续写入
             如果日期变更,也会新建文件,然后在新的日志文件中写入当天日志
        -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 新建文件后,原日志改名为如下  %i=文件序号,从0开始 -->
            <fileNamePattern>${log.path}/newlog-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <!-- 每个日志文件的最大体量 -->
            <timeBasedFileNamingAndTriggeringPolicy
                    class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>1kb</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
            <!-- 日志文件保留天数,1=则只保留昨天的归档日志文件 ,不设置则保留所有日志-->
            <maxHistory>1</maxHistory>
        </rollingPolicy>
    </appender>

    <root level="INFO">
        <appender-ref ref="CONSOLE"/>
        <appender-ref ref="file1"/>
        <appender-ref ref="file2"/>
    </root>

</configuration>

启动工程,进行测试

十六、SpringBoot整合EasyExcel


EasyExcel官网:https://alibaba-easyexcel.github.io/index.html

16.1导入依赖
<dependency>
     <groupId>com.alibaba</groupId>
     <artifactId>easyexcel</artifactId>
     <version>2.2.3</version>
</dependency>

16.2写入操作
16.2.1创建pojo
package com.qf.pojo;

import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;

import java.util.Date;

@Data
public class DemoData {
    @ExcelProperty("字符串标题")
    private String string;
    @ExcelProperty("日期标题")
    private Date date;
    @ExcelProperty("数字标题")
    private Double doubleData;
    /**
     * 忽略这个字段
     */
    @ExcelIgnore
    private String ignore;
}

16.2.2创建测试类
package com.qf.esayexcel;

import com.alibaba.excel.EasyExcel;
import com.qf.pojo.DemoData;
import org.junit.Test;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class TestEasyExcel {

    private List<DemoData> data() {
        List<DemoData> list = new ArrayList<DemoData>();
        for (int i = 0; i < 10; i++) {
            DemoData data = new DemoData();
            data.setString("字符串" + i);
            data.setDate(new Date());
            data.setDoubleData(0.56);
            list.add(data);
        }
        return list;
    }

    @Test
    public void simpleWrite() {
        // 写法1
        String fileName = "D:/TestEasyExcel.xlsx";
        // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
        EasyExcel.write(fileName, DemoData.class).sheet("模板").doWrite(data());
    }
}

运行测试类即可

16.3读取操作
16.3.1创建监听器
package com.qf.esayexcel;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.fastjson.JSON;
import com.qf.pojo.DemoData;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;

// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
public class DemoDataListener extends AnalysisEventListener<DemoData> {
    private static final Logger LOGGER = LoggerFactory.getLogger(DemoDataListener.class);
    /**
     * 每隔5条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收
     */
    private static final int BATCH_COUNT = 5;
    List<DemoData> list = new ArrayList<DemoData>();
    /**
     * 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。
     */
    private DemoDAO demoDAO;
    public DemoDataListener() {
        // 这里是demo,所以随便new一个。实际使用如果到了spring,请使用下面的有参构造函数
        demoDAO = new DemoDAO();
    }
    /**
     * 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来
     *
     * @param demoDAO
     */
    public DemoDataListener(DemoDAO demoDAO) {
        this.demoDAO = demoDAO;
    }
    /**
     * 这个每一条数据解析都会来调用
     *
     * @param data
     *            one row value. Is is same as {@link AnalysisContext#readRowHolder()}
     * @param context
     */
    @Override
    public void invoke(DemoData data, AnalysisContext context) {
        LOGGER.info("解析到一条数据:{}", JSON.toJSONString(data));
        list.add(data);
        // 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
        if (list.size() >= BATCH_COUNT) {
            saveData();
            // 存储完成清理 list
            list.clear();
        }
    }
    /**
     * 所有数据解析完成了 都会来调用
     *
     * @param context
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        // 这里也要保存数据,确保最后遗留的数据也存储到数据库
        saveData();
        LOGGER.info("所有数据解析完成!");
    }
    /**
     * 加上存储数据库
     */
    private void saveData() {
        LOGGER.info("{}条数据,开始存储数据库!", list.size());
        demoDAO.save(list);
        LOGGER.info("存储数据库成功!");
    }
}

16.3.2创建Dao
package com.qf.esayexcel;

import com.qf.pojo.DemoData;

import java.util.List;

/**
 * 假设这个是你的DAO存储。当然还要这个类让spring管理,当然你不用需要存储,也不需要这个类。
 **/
public class DemoDAO {
    public void save(List<DemoData> list) {
        // 如果是mybatis,尽量别直接调用多次insert,自己写一个mapper里面新增一个方法batchInsert,所有数据一次性插入
    }
}

16.3.3创建测试类
 @Test
    public void simpleRead() {
        // 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
        // 写法1:
        String fileName =  "D:/TestEasyExcel.xlsx";
        // 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭
        EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).sheet().doRead();

    }

十七、SpringBoot整合Swagger


17.1swagger介绍

现在开发,很多采用前后端分离的模式,前端只负责调用接口,进行渲染,前端和后端的唯一联系,变成了API接口。因此,API文档变得越来越重要。swagger是一个方便我们更好的编写API文档的框架,而且swagger可以模拟http请求调用。

大部分采取的方式:Vue + SpringBoot,Vue通过js渲染页面,后端把数据传递给js,早期前端只负责写页面,然后把写好的HTML页面给后端,后端使用模板引擎(Jsp,Thymeleaf、 freemarker)进行开发。

前后端分离的好处:各自开发,相对独立,松耦合,前后端通过API进行交互,后端提供接口给前端,前端去调用该接口,但可能会导致前后端团队人员不能做到及时协商,出现一些问题。解决方式:早期使用实时更新文档,但非常繁琐,后来又使用postman来进行一些测试。

swagger是目前最流行的Api框架,官网:https://swagger.io/

17.2导入依赖
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger-ui</artifactId>
    <version>2.9.2</version>
</dependency>

<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
    <version>2.9.2</version>
</dependency>

17.3创建配置类
@Configuration
@EnableSwagger2//开启Swagger2
public class SwaggerConfig {

}

然后启动测试运行,访问:http://localhost:8080/swagger-ui.html,看到如下页面:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6z4S7zrk-1601205810110)(Pictures\13.png)]

17.4手动配置实例,修改SwaggerConfig配置类
package com.qf.swagger.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;


@Configuration
@EnableSwagger2//开启Swagger2
public class SwaggerConfig {

    //配置Swagger的Bean实例
    @Bean
    public Docket createDocket() {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo());
    }


    //配置API的基本信息(会在http://项目实际地址/swagger-ui.html页面显示)
    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("测试API文档标题")
                .description("测试api接口文档描述")
                .termsOfServiceUrl("http://www.baidu.com")
                .version("1.0")
                .build();
    }
}

再次启动测试运行,访问:http://localhost:8080/swagger-ui.html,看到如下页面:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ulve3Ek7-1601205810112)(Pictures\14.png)]

17.5创建实体类
package com.qf.entity;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;

@ApiModel("用户")
public class User {

    @ApiModelProperty("编号")
    private String uid;
    
    @ApiModelProperty("用户名")
    private String username;
    
    @ApiModelProperty("密码")
    private String password;


    public String getUid() {
        return uid;
    }

    public void setUid(String uid) {
        this.uid = uid;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}


17.6创建controller
package com.qf.controller;


import com.qf.entity.User;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.List;

//@Api(description = "用户接口")
@Api(tags = "用户接口")
@RestController
@RequestMapping("/user")
public class UserController {


    @ApiOperation("查询单个用户")
    @RequestMapping("/findById")
    public User findById(@RequestParam @ApiParam("用户ID") String uid){

        User user = new User();
        user.setUid(uid);
        user.setUsername("张三");
        user.setPassword("123");

        return  user;
    }

    @ApiOperation("删除单个用户")
    @PostMapping("/delete")
    public User delete(String uid){

        User user = new User();
        user.setUid(uid);
        user.setUsername("李四");
        user.setPassword("456");

        return  user;
    }

    @ApiOperation("查询所有用户")
    @GetMapping("/findAll")
    public List<User> findAll(){

        User user1 = new User();
        user1.setUid("1001");
        user1.setUsername("张三");
        user1.setPassword("123");

        User user2 = new User();
        user2.setUid("1002");
        user2.setUsername("李四");
        user2.setPassword("456");

        ArrayList<User> users = new ArrayList<>();
        users.add(user1);
        users.add(user2);

        return  users;
    }
}


17.7修改SwaggerConfig配置类
package com.qf.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

@Configuration
@EnableSwagger2//开启Swagger2
public class SwaggerConfig {

    //配置Swagger的Bean实例
    @Bean
    public Docket createDocket(){

        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(createApiInfo())
                .groupName("yangl")分组名称(可以创建多个Docket就有多个组名)
                .enable(true)//enable表示是否开启Swagger
                .select()
                //RequestHandlerSelectors指定扫描的包
                .apis(RequestHandlerSelectors.basePackage("com.qf.controller"))
                .build();
    }

    //配置API的基本信息(会在http://项目实际地址/swagger-ui.html页面显示)
    public ApiInfo createApiInfo(){
        return new ApiInfoBuilder()
                .title("测试标题")
                .description("测试描述")
                .termsOfServiceUrl("http://www.baidu.com")
                .build();

        //return ApiInfo.DEFAULT;
    }

}


Swagger通过注解表明该接口会生成文档,包括接口名、请求方法、参数、返回信息

@Api:修饰整个类,描述Controller的作用

@ApiOperation:描述一个类的一个方法,或者说一个接口

@ApiModel:用对象来接收参数 ,修饰类

@ApiModelProperty:用对象接收参数时,描述对象的一个字段

@ApiResponse:HTTP响应其中1个描述

@ApiResponses:HTTP响应整体描述,一般描述错误的响应

@ApiIgnore:使用该注解忽略这个API

@ApiError :发生错误返回的信息

@ApiParam:单个参数描述

@ApiImplicitParam:一个请求参数,用在方法上

@ApiImplicitParams:多个请求参数

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值