springboot基础(四):swagger+各种任务

单体开发进阶

SpringBoot的Web开发之路

大家平时中用的CRUD和传统的开发,2天时间我们搞定了!(是每个程序员必经之路!)
能够独立开发出一个简单的CRUD的系统即可(OA系统、CRM管理系统、基于表单的CRUD的系统!
80%的应用都是在做昨天的应用工作!)
分布式开发:后端提供接口 + 前端接收信息渲染!
单体开发:后端提供数据 + 前端获取数据渲染!
整体来说,从开发流程,单体和 分布式本质上没有区别,只是用到技术和思想上略有不同!
分布式开发的本质:网络是不可靠的!服务之间的通信,服务崩了怎么办,客户端怎么访问,服务的注
册与发现!

今日内容

1、精通Swagger
2、异步任务(JUC、多线程)
3、定时任务(定时备份日志…)
4、邮件任务(扩展技能!微信的前身,邮件系统!)
5、富文本编辑器(MD前后端联调,开发博客使用!)

精通Swagger

学习目标

在这里插入图片描述

1、了解Swagger的概念和作用
2、在项目中集成并使用Swagger!

什么是Swagger

相信无论是前端还是后端开发,都或多或少地被接口文档折磨过。前端经常抱怨后端给的接口文档与实际情况不一致。后端又觉得编写及维护接口文档会耗费不少精力,经常来不及更新。其实无论是前端调用后端,还是后端调用后端,都期望有一个好的接口文档。但是这个接口文档对于程序员来说,就跟注释一样,经常会抱怨别人写的代码没有写注释,然而自己写起代码起来,最讨厌的,也是写注释。所以仅仅只通过强制来规范大家是不够的,随着时间推移,版本迭代,接口文档往往很容易就跟不上代码了。

发现了痛点就要去找解决方案。解决方案用的人多了,就成了标准的规范,这就是Swagger的由来。通过这套规范,你只需要按照它的规范去定义接口及接口相关的信息。再通过Swagger衍生出来的一系列项目和工具,就可以做到生成各种格式的接口文档,生成多种语言的客户端和服务端的代码,以及在线接口调试页面等等。这样,如果按照新的开发模式,在开发新版本或者迭代版本的时候,只需要更新Swagger描述文件,就可以自动生成接口文档和客户端服务端代码,做到调用端代码、服务端代码以及接口文档的一致性。

但即便如此,对于许多开发来说,编写这个yml或json格式的描述文件,本身也是有一定负担的工作,特别是在后面持续迭代开发的时候,往往会忽略更新这个描述文件,直接更改代码。久而久之,这个描述文件也和实际项目渐行渐远,基于该描述文件生成的接口文档也失去了参考意义。所以作为Java届服务端的大一统框架Spring,迅速将Swagger规范纳入自身的标准,建立了Spring-swagger项目,后面改成了现在的Springfox。通过在项目中引入Springfox,可以扫描相关的代码,生成该描述文件,进而生成与代码一致的接口文档和客户端代码。这种通过代码生成接口文档的形式,在后面需求持续迭代的项目中,显得尤为重要和高效。

前后端分离:
前端 --> 前端的控制层,视图层. (专业的前端团队开发)
后端 --> 后端控制层、服务层、数据访问层.(专业的后端团队开发)
前后端的交互是通过 API 来进行的?关于API的约定该怎么处理呢?
早期:后端编写文档(协同文档),前端根据文档解析接口然后渲染视图!
问题:前后端集成,前端或者后端无法做到:及时协商!最终可能导致问题集中爆发或者项目延时!
现在得开源项目中,都有集成Swagger!
什么是Swagger?
号称世界上最流行的API框架!
Restful api 自动生成文档 ,和代码对应的
直接运行测试接口,不用下载 postman
支持多种语言:(Java、Php等)
官网:https://swagger.io/

集成Swagger

基础集成

1、导入依赖

<!--导入依赖-->
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger2 -->
<dependency>
  <groupId>io.springfox</groupId>
  <artifactId>springfox-swagger2</artifactId>
  <version>2.9.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -->
<dependency>
  <groupId>io.springfox</groupId>
  <artifactId>springfox-swagger-ui</artifactId>
  <version>2.9.2</version>
</dependency>

2、编写配置

@Configuration
public class SwaggerConfig {
  // 注册bean Docket
  @Bean
  public Docket docket(){
    return new Docket(DocumentationType.SWAGGER_2);
 }
}
// 主启动类开启配置!
@SpringBootApplication
// 使Swagger生效,默认是不开启!@EnableSwagger2
@EnableSwagger2
public class SpringbootPlusApplication {
  public static void main(String[] args) {
    SpringApplication.run(SpringbootPlusApplication.class, args);
 }
}

3、启动测试 : http://localhost:8080/swagger-ui.html

在这里插入图片描述

spring boot 加入拦截器后swagger不能访问问题

未加入拦截器时,swagger可以正常访问接口信息,但是加入拦截器之后swagger就不能访问了

原因分析
不能访问的原因的swagger的内置接口被拦截器拦下来了

在这里插入图片描述

图片中可以看到swagger的所有请求的url信息,只要把他们加到拦截器的排除列表中即可

package com.trimps928.config;

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.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;

/**

 * @author liubing

 * @version 2018-06-26

 * 拦截器配置
   **/
   @Configuration
   public class MyWebAppConfig extends WebMvcConfigurationSupport {

   @Bean
   LoginInterceptor localInterceptor() {
       return new LoginInterceptor();
   }
	//将swagger相关的链接排除出去
   @Override
   public void addInterceptors(InterceptorRegistry registry) {
       registry.addInterceptor(localInterceptor())
               .addPathPatterns("/**")
               .excludePathPatterns("/user/login")
               .excludePathPatterns("/swagger-resources/**", "/webjars/**", "/v2/**", "/swagger-ui.html/**");
   }
	//添加静态资源访问   这两者都需要的
   @Override
   protected void addResourceHandlers(ResourceHandlerRegistry registry) {
       registry.addResourceHandler("swagger-ui.html")
               .addResourceLocations("classpath:/META-INF/resources/");
       registry.addResourceHandler("/webjars/**")
               .addResourceLocations("classpath:/META-INF/resources/webjars/");
   }
   }

注意点:写Controller的之后一定要精确到方法! 不能写@RequestMapping 因为这是由下面六个组成的,而不是精确到方法的

在这里插入图片描述

配置Swagger

1、配置文档信息

@Bean
public Docket docket(){
  return
    new Docket(DocumentationType.SWAGGER_2)
   .apiInfo(apiInfo()); // 配置文档信息!
}
// 配置文档信息 apiInfo
private ApiInfo apiInfo(){
  Contact contact = new
Contact("coding","https://www.icodingedu.com/course/52","24736743@qq.com");
  return new ApiInfo(
    "SpringBoot-Plus 接口文档信息",
    "所有的测试请求地址",
    "v1.0",
    "https://www.icodingedu.com/course/52", //组织连接
    contact,
    "Apache 2.0",
    "http://www.apache.org/licenses/LICENSE-2.0",
    new ArrayList<>());
}

2、配置接口扫描,需要哪些被扫描到文档中

@Bean
public Docket docket(){
  return
    new Docket(DocumentationType.SWAGGER_2)
   .apiInfo(apiInfo())
   .select()
   .apis(RequestHandlerSelectors.basePackage("com.coding.controller"))
   .build();
}

在这里插入图片描述

其与的几个方法说明:

any()  # 扫描所有,项目的所有接口都会被扫描的
none()  # 不扫描接口
basePackage()  # 根据包路径扫描
withMethodAnnotation(GetmMapping.class) # 通过方法注解扫描! 比如 GetMapper.class
withClassAnnotation(Controller.class) # 通过类上的注解扫描!

学习方式:上课听(思考自己不会的问题或者有疑问问题,写在记事本上,下课之后去钻研!)
3、设置哪些接口不被扫描!

在这里插入图片描述

@Bean
public Docket docket(){
  return
    new Docket(DocumentationType.SWAGGER_2)
   .apiInfo(apiInfo()) // 配置文档信息!
   .select()
   .apis(RequestHandlerSelectors.basePackage("com.coding.controller"))
    // 配置path过滤请求!只扫描以 /kuang 开头的请求!
   .paths(PathSelectors.ant("/kuang/**"))
   .build();
}

配置Swagger的开关

test、dev 环境下才可以显示 swagger-ui , prod 不显示!

@Bean
public Docket docket(){
  return
    new Docket(DocumentationType.SWAGGER_2)
   .apiInfo(apiInfo())
   .enable(false) // 如果是false就无法在浏览器中访问!
   .select()
   .apis(RequestHandlerSelectors.basePackage("com.coding.controller"))

   .paths(PathSelectors.ant("/kuang/**"))
   .build();
}

在这里插入图片描述

这里的 false 应该是一个变量:技巧点!通过Profiles类来获取限定咋们的开发环境!

@Bean
public Docket docket(Environment environment){
  // 设置要显示的swagger环境
  Profiles of = Profiles.of("dev", "test");
  // 判断是否处于该环境!
  boolean b = environment.acceptsProfiles(of);
  return
    new Docket(DocumentationType.SWAGGER_2)
   .apiInfo(apiInfo()) // 配置文档信息!
   .enable(b) // 如果是false就无法在浏览器中访问!
   .select()
   .apis(RequestHandlerSelectors.basePackage("com.coding.controller"))
   .paths(PathSelectors.ant("/kuang/**"))
   .build();
}

配置API分组

这个大家现在过个眼熟即可,能掌握最好,后面我们使用MP一键生成!

在这里插入图片描述

@Bean
public Docket docket1(){
  return new Docket(DocumentationType.SWAGGER_2).groupName("group1");
}
@Bean
public Docket docket2(){
  return new Docket(DocumentationType.SWAGGER_2).groupName("group2");
}
@Bean
public Docket docket3(){
  return new Docket(DocumentationType.SWAGGER_2).groupName("group3");
}
    @Bean
    public Docket docket(){
        Docket docket = new Docket(DocumentationType.SWAGGER_2)
                .groupName("null")
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.hl.mywebsystem.controller"))
                .build();
        return docket;
    }

公司都是盈利为目的:你算一下你的工作能带来资金能比你工资多吗?

实体配置

1、新建一个实体类

// 和注释差不多,但是会被Swagger识别
@ApiModel("用户实体")
public class User {
  @ApiModelProperty("用户名")
  private String username;
  @ApiModelProperty("密码")
  private String password;
}

2、请求的接口配置!如果能够看到实体类配置!如果这个实体类在请求的返回值或者泛型中,那么就会
被映射!

// 只有返回值用到才会显示
@GetMapping("/getUser")
public User getUser(){
  return new User();
}

在这里插入图片描述

接口上的配置

@Api(tags="AAAAA测试")
@RestController
public class HelloController {
  // 后面我们自己开发项目的时候,名主要是写 方法注释和参数注释!
  @ApiOperation("coding的接口")
  @PostMapping("/coding")
  public String coding(@ApiParam("这个名字会被返回!") String username){
    return username;
 }
  @GetMapping("/kuang/hello")
  public String hello(){
    return "hello,Swagger";
 }
  // 只有返回值用到才会显示
  @GetMapping("/getUser")
  public User getUser(){
    return new User();
 }

扩展:皮肤包!

1、默认的 http://localhost:8081/swagger-ui.html

<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -->
<dependency>
  <groupId>io.springfox</groupId>
  <artifactId>springfox-swagger-ui</artifactId>
  <version>2.9.2</version>
</dependency>

在这里插入图片描述

2、BootStrap-ui http://localhost:8080/doc.html

<!-- https://mvnrepository.com/artifact/com.github.xiaoymin/swagger-bootstrap-ui
-->
<dependency>
  <groupId>com.github.xiaoymin</groupId>
  <artifactId>swagger-bootstrap-ui</artifactId>
  <version>1.9.6</version>
</dependency>

在这里插入图片描述

3、Layui的框架 http://localhost:8080/docs.html

<!-- https://mvnrepository.com/artifact/com.github.caspar-chen/swagger-ui-layer -
->
<dependency>
  <groupId>com.github.caspar-chen</groupId>
  <artifactId>swagger-ui-layer</artifactId>
  <version>1.1.3</version>
</dependency>

在这里插入图片描述

4、mg-ui http://localhost:8080/document.html

<dependency>
  <groupId>com.zyplayer</groupId>
  <artifactId>swagger-mg-ui</artifactId>
  <version>1.0.6</version>
</dependency>

在这里插入图片描述

异步任务

异步处理请求!

无返回值的异步任务

1、在业务方法中,加上@Async注解

// 模拟一个延时的后台方法
@Async // 告诉Spring这是一个异步方法!默认使用了线程池,效率很好!
public void hello(){
try {
  Thread.sleep(3000);
 } catch (InterruptedException e) {
  e.printStackTrace();
 }
System.out.println("数据处理中.....");
}

2、开启注解的支持

@SpringBootApplication
@EnableSwagger2 // 使Swagger生效,默认是不开启!@EnableSwagger2
@EnableAsync // 开启异步注解的支持
public class SpringbootPlusApplication {
  public static void main(String[] args) {
    SpringApplication.run(SpringbootPlusApplication.class, args);
 }
}

测试,秒级刷新,后台OK!

有返回值的异步任务

返回值用Futrue变量封装起来,下面是service层的代码

@Async
public Future doReturn(int i){
try {
// 这个方法需要调用500毫秒
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 消息汇总
return new AsyncResult<>(String.format(“这个是第{%s}个异步调用的证书”, i));
}
读取的时候,记得要批量读取不能单独读取, 下面是controller层的代码

@GetMapping("/hi")
public Map<String, Object> testAsyncReturn() throws ExecutionException, InterruptedException {
long start = System.currentTimeMillis();

    Map<String, Object> map = new HashMap<>();
    List<Future<String>> futures = new ArrayList<>();
    for (int i = 0; i < 10; i++) {
        Future<String> future = asyncService.doReturn(i);
        futures.add(future);
    }
    List<String> response = new ArrayList<>();
    for (Future future : futures) {
        String string = (String) future.get();
        response.add(string);
    }
    map.put("data", response);
    map.put("消耗时间", String.format("任务执行成功,耗时{%s}毫秒", System.currentTimeMillis() - start));
    return map;
}

在浏览器输入地址:http://localhost:8080/hi

结果如下: 耗时500多毫秒的意思代表,springboot自带异步任务线程池是小于10的大小的

{“data”:[“这个是第{0}个异步调用的证书”,“这个是第{1}个异步调用的证书”,“这个是第{2}个异步调用的证书”,“这个是第{3}个异步调用的证书”,“这个是第{4}个异步调用的证书”,“这个是第{5}个异步调用的证书”,“这个是第{6}个异步调用的证书”,“这个是第{7}个异步调用的证书”,“这个是第{8}个异步调用的证书”,“这个是第{9}个异步调用的证书”],“消耗时间”:“任务执行成功,耗时{508}毫秒”}
重要的事情说三遍:
一定要批量读取结果, 否则不能达到异步的效果!!
一定要批量读取结果, 否则不能达到异步的效果!!
一定要批量读取结果, 否则不能达到异步的效果!!
1、异步方法和调用类不要在同一个类中

2、注解扫描时,要注意过滤,避免重复实例化,因为存在覆盖问题,@Async就失效了

否则效果如下:

{“data”:[“这个是第{0}个异步调用的证书”,“这个是第{1}个异步调用的证书”,“这个是第{2}个异步调用的证书”,“这个是第{3}个异步调用的证书”,“这个是第{4}个异步调用的证书”,“这个是第{5}个异步调用的证书”,“这个是第{6}个异步调用的证书”,“这个是第{7}个异步调用的证书”,“这个是第{8}个异步调用的证书”,“这个是第{9}个异步调用的证书”],“消耗时间”:“任务执行成功,耗时{5012}毫秒”}

定时任务

工作中一定会用到定时任务,每天发送一个日志信息,Spring也提供了对应的支持!

Cron 表达式

计划任务,是任务在约定的时间执行已经计划好的工作,这是表面的意思。在Linux中,我们经常用到
cron 服务器来完成这项工作。cron服务器可以根据配置文件约定的时间来执行特定的任务。

在这里插入图片描述

秒 分 小时 日期 月份 星期
除了以上的作用值之外,
还有 * ,任意时间!

- 区间 6-7
L :最后

? :天/星期冲突匹配
W 工作日(朝九晚五、双休,节假日!)

#星期, 4#2 ,第二个星期三

我们平时生成这个表达式,可以去找在线生成的网站!
常用的例子:

(1)0/2 * * * * ?  表示每2秒 执行任务
(1)0 0/2 * * * ?  表示每2分钟 执行任务
(1)0 0 2 1 * ?  表示在每月的1日的凌晨2点调整任务
(2)0 15 10 ? * MON-FRI  表示周一到周五每天上午10:15执行作业
(3)0 15 10 ? 6L 2002-2006  表示2002-2006年的每个月的最后一个星期五上午10:15执行作
(4)0 0 10,14,16 * * ?  每天上午10点,下午2点,4点
(5)0 0/30 9-17 * * ?  朝九晚五工作时间内每半小时
(6)0 0 12 ? * WED  表示每个星期三中午12点
(7)0 0 12 * * ?  每天中午12点触发
(8)0 15 10 ? * *  每天上午10:15触发
(9)0 15 10 * * ?   每天上午10:15触发
(10)0 15 10 * * ?  每天上午10:15触发
(11)0 15 10 * * ? 2005  2005年的每天上午10:15触发
(12)0 * 14 * * ?   在每天下午2点到下午2:59期间的每1分钟触发
(13)0 0/5 14 * * ?  在每天下午2点到下午2:55期间的每5分钟触发
(14)0 0/5 14,18 * * ?   在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发
(15)0 0-5 14 * * ?  在每天下午2点到下午2:05期间的每1分钟触发
(16)0 10,44 14 ? 3 WED  每年三月的星期三的下午2:10和2:44触发
(17)0 15 10 ? * MON-FRI  周一至周五的上午10:15触发
(18)0 15 10 15 * ?  每月15日上午10:15触发
(19)0 15 10 L * ?  每月最后一日的上午10:15触发
(20)0 15 10 ? * 6L  每月的最后一个星期五上午10:15触发
(21)0 15 10 ? * 6L 2002-2005  2002年至2005年的每月的最后一个星期五上午10:15触发
(22)0 15 10 ? * 6#3  每月的第三个星期五上午10:15触发

写点代码测试一下!

自动化定时任务!
1、编写Service

package com.coding.service;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
// 定时任务类
@Service // 放到Spring容器中
public class ScheduledService {
  // 秒  分  时 日 月  周几
  @Scheduled(cron = "0 * * * * 0-7")
  public void hello(){
    System.out.println("Hello。。。。。。。。");
 }
}

2、开启注解的支持!

@SpringBootApplication
@EnableSwagger2 // 使Swagger生效,默认是不开启!@EnableSwagger2
@EnableAsync // 开启异步注解的支持
@EnableScheduling // 开启定时任务支持
public class SpringbootPlusApplication {
  public static void main(String[] args) {
    SpringApplication.run(SpringbootPlusApplication.class, args);
 }
}

邮件任务

所有的网站,差不多都用邮件收发功能!基于SpringBoot!

1.导入启动器

2.配置文件

3.测试使用

邮件的开发者权限获取

QQ 的是比较复杂,因为有安全验证

在这里插入图片描述

在这里插入图片描述

得到授权码:相当于你的密码!可以登录你的邮箱!
在这里插入图片描述

在这里插入图片描述

其与的就直接写代码就好!

测试

1、导入启动器 ,java发送邮件:javax.mail 但是SpringBoot启动器中导入的是 jakarta.mail?

java有两段api,

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-mail</artifactId>
</dependency>

2、配置文件,收件人,发件人,邮件主题,邮件内容!
分析源码: MailSenderAutoConfiguration,通过导入的组件发现配置类:

@Bean
JavaMailSenderImpl mailSender(Session session) {
  JavaMailSenderImpl sender = new JavaMailSenderImpl();
  sender.setDefaultEncoding(this.properties.getDefaultEncoding().name());
  sender.setSession(session);
  return sender;

配置类:MailProperties

# 发件人的信息(一般是公司的账号)
spring.mail.username=24736743@qq.com
spring.mail.password=vtnofwxdkmuqcagf
spring.mail.host=smtp.qq.com
# QQ比较特殊,需要配置ssl 安全连接, 如果是其他的邮箱不用配置!
spring.mail.properties.mail.smtp.ssl.enable=true

3、测试使用!

@SpringBootTest
class SpringbootPlusApplicationTests {
  @Autowired
  JavaMailSenderImpl mailSender;
  @Test
  void contextLoads() {
    // 发送简单的邮件! 万物皆对象,邮件也不例外
    SimpleMailMessage message = new SimpleMailMessage();// 简单的邮箱消息
    message.setSubject("通知,明天不上课"); // 邮件主题
    message.setText("明天要出差!"); // 简单的文本
    message.setTo("24736743@qq.com","785897496@qq.com","2360004365@qq.com");
    message.setFrom("24736743@qq.com");
    mailSender.send(message); // 发送邮件!
 }
  // 复杂的邮件
  @Test
  void test2() throws MessagingException {

    // 复杂的邮件,通过一个辅助类来完成 MimeMessage
    MimeMessage mimeMessage = mailSender.createMimeMessage();
    MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);
    // 基本信息
    helper.setSubject("复杂的邮件测试");
    helper.setText("<h1 style='color:red'>今天有点意思</h1>",true);
    // 发送附件
    helper.addAttachment("1.jpg",new
File("C:\\Users\\Administrator\\Desktop\\Bilibili\\赞赏码.jpg"));
    helper.addAttachment("2.jpg",new
File("C:\\Users\\Administrator\\Desktop\\Bilibili\\赞赏码.jpg"));
    // 发送对象!
    String[] userList =
{"24736743@qq.com","785897496@qq.com","2360004365@qq.com"};
    helper.setTo(userList);
    helper.setFrom("24736743@qq.com");
    mailSender.send(mimeMessage); // 发送邮件!
 }
}

下去一定要自己实现一下,可以自己写一个前端页面,给自己的博客网站,增加一个写邮件的功能!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值