springboot学习笔记-ssm全记录

本文记录我学习springboot的整个流程, 会持续不断更新, 具体的代码开源到了gitee, 点击跳转

1. 新版SpringBoot2.X的常⽤用注解你知多少

  • @Controller 作⽤用:⽤用于标记这个类是⼀一个控制器器,返回⻚页⾯面的时候使⽤用;如果要返回 JSON,则需要在接⼝口上使⽤用 @ResponseBody才可以

  • @RestController 作⽤用:⽤用于标记这个类是⼀一个控制器器,返回 JSON数据的时候使⽤用,如果使⽤用这个注解,则接⼝口返回数据会被序列列化为 JSON

  • 所以:@RestController = @Controller+@ResponseBody

  • @RequestMapping 作⽤用:路路由映射,⽤用于类上做 1级路路径;⽤用于某个⽅方法上做⼦子路路径

  • @SpringBootApplication 作⽤用 : ⽤用于标记是 SringBoot应⽤用,⾥里里⾯面包含多个⼦子注解 ,即

    @SpringBootApplication = 
    @Configuration+@EnableAutoConfiguration+@ComponentScan
    (下⾯面的⽬目前只需简单理理解即可,想深⼊入的同学,后续可以看专⻔门的 Spring原理理课程深⼊入 )
    @Configuration: 主要标注在某个类上,⽤用于 spring扫描注⼊入 ,⼀一般结合 @Bean使⽤用
    @EnableAutoConfiguration: 启⽤用 Spring的⾃自动加载配置 ,⾃自动载⼊入应⽤用程序所需的所有 Bean
    @ComponentScan:告诉spring扫描包的范围,默认是Applocation类所在的全部⼦子包,可以指定
    其他包
    @ComponentScan({"net.xdclass.package1","net.xdclass.package2"})
    

2. 互联⽹网公司中新版 SpringBoot2.X开发规范解读

2.1. 新版SpringBoot2.x⽬目录⽂文件结构讲解和静态资源访问

2.1.1. ⽬目录讲解

  1. src/main/java:存放代码
  2. src/main/resources
  3. static: 存放静态⽂文件,⽐比如 css、js、image, (访问⽅方式 http://localhost:8080/js/main.js)
  4. templates:存放静态⻚页⾯面 jsp,html,tpl
  5. config:存放配置⽂文件 ,application.properties
  6. resources:

2.1.2. 同个文件的加载顺序

静态资源⽂文件 Spring Boot 默认会挨个从

META/resources >resources >static >public

⾥面找是否存在相应的资源,如果有则直接返回,不在默认加载的目录,则找不不到

2.1.3. 默认配置

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

3. 启动类位置常见形式

  1. 当启动类和controller在同⼀一类中时,在该类上添加注解 @Controller即可
  2. 当启动类和controller分开时,启动类要放在根⽬目录下,启动类上只需要注解@SpringBootApplication
  3. 当启动类和controller分开时,如果启动类在⾮非根⽬目录下,需要在启动类中增加注解@ComponentScan,并配置需要扫描的包名,如(basePackages = )
    @ComponentScan(basePackages ={"net.xdclass.controller","net.xdclass.service"})
  4. 推荐使用第二种方式, 否则漏掉配置扫描包, 项目庞大, 出现问题难以排查

4. 启动⽅方式讲解和部署

4.1. IDEA开发中启动

4.2. 外置Tomcat中启动

4.3. Jar方式打包

4.3.1. 新增maven插件

官方推荐

# pom⽂文件新增 maven插件
<build>
      <plugins>
        <plugin>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
      </plugins>
</build>
# 如果没有加,则执⾏行行jar包 ,报错如下
# java -jar spring-boot-demo-0.0.1-SNAPSHOT.jar
# no main manifest attribute, in spring-boot-demo-0.0.1-SNAPSHOT.jar

4.3.2. maven打包启动

构建:mvn install
构建跳过测试类 mvn install -Dmaven.test.skip=true 
target⽬目录下有对应的 jar包就是打包后项⽬目
进到对应的target⽬目录启动  java -jar xxxxx.jar  即可
想后台运⾏行行,就⽤用守护进程  nohup java -jar xxx.jar &


E:\12_ssm_Java_springboot\springboot\demo> mvn install
ls .\target\demo-0.0.1-SNAPSHOT.jar
E:\12_ssm_Java_springboot\springboot\demo> java -jar .\target\demo-0.0.1-SNAPSHOT.jar

4.3.3. 打包后的jar里面的目录结构

unzip *.jar

example.jar
 |
 +-META-INF 
 |  +-MANIFEST.MF # jave 虚拟机入口函数等重要路径
 +-org
 |  +-springframework
 |     +-boot
 |        +-loader
 |           +-<spring boot loader classes>
 +-BOOT-INF 
    +-classes # 自己写的各种文件
    |  +-mycompany
    |     +-project
    |        +-YourClasses.class
    +-lib # 依赖的包
       +-dependency1.jar
       +-dependency2.jar

5. SpringBoot在线教育架构搭建

创建项目, 配置启动类, 建立对应包

controller
service
dao
domain
utils

请添加图片描述

请添加图片描述

请添加图片描述

请添加图片描述

6. 接口返回协议

每个API都需要有一个统一返回, 在定义自己的返回信息

/**
 * @Author: zjq
 * @Date: 2022/9/4 15:25
 * @Description: 用于每次请求返回给前端的统一接口协议 如下:
 * {
 * code: 0  //信息码
 * data:{"1":"hello", "2":"world" ..} // 实际数据
 * msg: "这里是提示信息, 报错信息"
 * }
 **/
@Data
public class JsonData {
    private int code;
    private Object data;
    private String msg;

    public JsonData() {
    }

    public JsonData(int code, Object data) {
        this.code = code;
        this.data = data;
    }

    public JsonData(int code, Object data, String msg) {
        this.code = code;
        this.data = data;
        this.msg = msg;
    }

    // code = 0 表示成功, -1表示失败, 失败需要返回信息
    public static JsonData buildSuccess(Object data) {
        return new JsonData(0, data);
    }

    public static JsonData buildError(String msg) {
        return new JsonData(-1, "", msg);
    }

    public static JsonData buildError(String msg, int code) {
        return new JsonData(code, "", msg);
    }
}

以后每个接口的返回都是这样做返回的

    @GetMapping(value = "list")
    public Object list() {
        List<Video> list = videoService.videoList();

        return JsonData.buildSuccess(list);
    }

7. post请求User登录增加token

	private static Map<String, User> sessionMap = new HashMap<>(); // 模拟内存数据库    
	@Override
    public String login(String username, String pwd) {
        User user = userMapper.login(username, pwd);
        if (user == null) {
            return null;
        } else {
            String token = UUID.randomUUID().toString();
            System.out.println(token);
            sessionMap.put(token, user);
            // 设置token, 记录user缓存
            return token;
        }
    }

请添加图片描述

8. json请求和处理

8.1. requestBody对象json数组提交接口开发

post请求, RequestBody方式, json对象映射, 数组对象提交接口开发

@Data
public class Video {
    private int id;
    private String title;
    private String summary;
    private int price;
    private String coverImg;
    private Date createTime;
    private List<Chapter> chapterList;
    
    
    @PostMapping(value = "save_video_chapter")
    public JsonData saveVideoChapter(@RequestBody Video video) {
        System.out.println(video.toString());
        return JsonData.buildSuccess("");
    }

请添加图片描述

8.2. SpringBoot配置Jackson处理理字段

常⽤用框架 阿⾥里里 fastjson,谷歌 gson

8.2.1. javaBean序列化为Json

  • 性能 jackson > fastJson > Gson > json-lib 同个结构
  • 各有优缺点, 空间换时间或者时间换空间

8.2.2. Jcakson处理相关自动

  • 指定字段不返回 @JsonIgnore 比如请求用户信息时的密码
  • 指定日期格式 @JsonFormat(pattern="yyyy-MM-dd-hh:mm:ss", locale="zh", timezone="GMT+8")
  • 空字段不返回 @JsonInclude(Include.NON_NULL)
  • 指定别名 @JsonProperty @JsonProperty("创建的时间") // 创建的别名

本节: 视频创建时间返回自定义格式, 过滤用户敏感信息

序列化和反序列化操作

//序列列化操作
ObjectMapper objectMapper = new ObjectMapper();
String jsonStr = objectMapper.writeValueAsString(list);
System.out.println(jsonStr);
//反序列列化操作
List<Video> temp = objectMapper.readValue(jsonStr,List.class);

9. Springboot热部署

9.1. 嘛是 热部署

  1. 应用正在运行的时候升级功能不需要重启应用
  2. Java应用程序, 热部署就是在运行更新Java类文件
  3. 好处是 : 不需要手工重启, 提高本地开发效率

9.2. 常见的热部署方式

  1. Jrebel
  2. Springloaded
  3. springboot devtools

9.3. 实现

9.3.1. 设置pom.xml

 <dependency>  
         <groupId>org.springframework.boot</groupId>  
         <artifactId>spring-boot-devtools</artifactId>  
         <optional>true</optional>  
  </dependency>
  
  
  <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <fork>true</fork><!--必须添加这个配置-->
                </configuration>
            </plugin>
        </plugins>
    </build>

9.3.2. IDEA里面设置

  1. 打开 build→Compiler→打开自动编译
  2. 寻找compiler.automake.allow.when
  3. reset IDEA

请添加图片描述

10. 常见配置文件

10.1. 常见文件格式

xml
properties
json
yaml

springboot框架中, resource文件夹可以存放配置的文件有两种

  1. application.properties的用法, 扁平的k/v格式

    server.port=8081
    server.tomcat.accept-count=200
    server.compression.min-response-size=1M
    
  2. application.yml的用法, 树状格式

    server:
      port: 8082
      tomcat:
        accept-count: 200
      compression:
        min-response-size: 1M
    
  3. 同一个目录下, 以yaml为主, properties进行补充

10.2. 外部约定的配置文件加载顺序

  1. classpath 根目录下, resource文件夹编译之后就到该目录, 即 resources根目录 就是classpath目录
  2. resources/config/
  3. 项目根目录
  4. 项目根目录/config
  5. 直接子目录/config
  6. java -jar *.jar --spring.config.location=D:\config/
application: 8082
config:
	application: 8081
src:
	resources:
		application: 8084
		config:
			application: 80803
java -jar *.jar --server.port=8080

# 优先顺序是 8080>8081>8082>8083>8084

10.3. 手动选择不同的配置文件

  1. 第一种: 默认配置文件 spring.profiles.active=prod # 就会加载对应的prod配置文件

    /* 默认配置文件 spring.profiles.active=prod # 就会加载对应的prod配置文件
    resources/application.properties
    resources/application-dev.properties # 开发环境 (乌三)
    resources/application-prod.properties # 生产环境(现网)
    */
    
  2. 第二种: 通过注解方式选择

    @RestController
    @RequestMapping(value = "api/v1/test")
    @PropertySource({"classpath:config/pay.properties"})
    public class TestController {
        @Value("${wx.pay.appid}")
        private String wxPayAppId;
    
        @Value("${wx.pay.secret}")
        private String wxPaySecret;
    }
    

请添加图片描述

 -   设置加载

请添加图片描述

10.4. 随机数

    @Value("${rand_int}")
    private String randInt;

    @Value("${rand_long}")
    private String  randLong;

    @Value("${rand_uuid}")
    private String randUuid;

    @Value("${rand_less_than_ten}")
    private String randLessThanTen;

    @Value("${rand_in_range}")
    private String randInRange;

/*
配置文件
# mock 设置随机值
rand_value="${random.value}"
rand_int="${random.int}"
rand_long="${random.long}"
rand_uuid="${random.uuid}"
rand_less_than_ten="${random.int(10)}"
rand_in_range="${random.int[1024,65536]}"
*/

11. springboot2.X进行单元测试

11.1. 公司软件开发的测试测试流程多少?

  • 需求分析 →设计→开发→测试→上线

  • 测试里面的种类

    1. 单元测试: 完成最小的软件设计单元的验证工作, 目标是确保模块被正确的编码
    • 黑盒测试: 不考虑内部结构, 主要测试功能十分满足需求
    • 白盒测试: 针对代码级别, 测试开发工程师一般具备白盒测试能力, 针对程序内部的逻辑结构进行代码级别的测试
    • 回归测试: 对原先提出的缺陷进行二次验证, 开发人员修复后进行二次的验证
    • 集成测试: 测试模块与模块之间的整合, 且测试主要的业务功能
    • 系统测试: 针对整个产品系统进行测试, 验证系统是否满足产品业务的需求

11.2. 单元测试

11.2.1. 引入依赖

<!--springboot程序测试依赖,如果是⾃自动创建项⽬目默认添加 -->
 <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

<dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
</dependency>

11.2.2. 配置注解

@RunWith(SpringRunner.class)  //底层⽤用 junit  SpringJUnit4ClassRunner
@SpringBootTest(classes={DemoApplicationTests.class})//启动整个springboot⼯工程
public class SpringBootTests { }

11.2.3. 常用的单元注解

@Before // 资源初始化
@Test // 测试逻辑
@After // 资源回收

11.2.4. 断言

判断程序结果是否符合预期

TestCase.assertXXX

11.3. 实战

请添加图片描述

11.3.1. Controller层登录⽅方法测试

@RunWith(SpringRunner.class)
@SpringBootTest
public class UserTest {
    private static final Logger LOOGER = LoggerFactory.getLogger(UserTest.class);

    @Autowired
    private UserController userController;

    @Test
    public void loginTest() {
        User user = new User();
        user.setUsername("jack");
        user.setPwd("134");
        JsonData jsonData = userController.login(user);
        LOOGER.info(jsonData.toString());
        TestCase.assertEquals(jsonData.getCode(), 0);
    }
}

11.3.2. 视频列列表 Service层单元测试

@RunWith(SpringRunner.class) // 底层用Junit, SpringJunit4ClassRunner
@SpringBootTest(classes = {DemoApplication.class}) //
public class VideoTest {
    private static final Logger LOGGER = LoggerFactory.getLogger(VideoTest.class);
    @Autowired
    private VideoService videoService;

    @Test
    public void testVideoList() {
        List<Video> videoList = videoService.videoList();
        TestCase.assertTrue(videoList.size()>0);
    }
}

11.4. mockMvc 模拟浏览器访问url

@Autowired
private MockMvc mockMvc;

@Test
public void testVideoListApi() throws Exception {
    MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.get("/api/v1/pub/video/list"))
            .andExpect(MockMvcResultMatchers.status().isOk())
            .andReturn();
    int status = mvcResult.getResponse().getStatus();
    LOGGER.info("获取状态: {}", status);
    String result = mvcResult.getResponse().getContentAsString();
    LOGGER.info(result);
}

12. 异常处理

12.1. 全局异常处理

  1. 为何配置全局异常: 不配全局服务端报错场景比如1/0, 空指针等会把所有的错误信息传递到前端
  2. 配置好处:
    • 统一的错误页面或者错误码
    • 对用户更加友好

12.1.1. 配置全局异常的流程

    @GetMapping("list")
    public JsonData getList(){
        int a = 1/0; // 这里有异常会报错
        return JsonData.buildSuccess("hello list");
    }

@RestControllerAdvice // 标记这是一个异常处理类
public class CustomExtHandler {

    @ExceptionHandler(value = Exception.class) // 标记所有异常都在这里回收
    JsonData handlerException(Exception e, HttpServletRequest request) {
        return JsonData.buildError("服务器出问题了", -2);
    }
}

13. 过滤器

13.1. 过滤器介绍

  1. 过滤器相当于在请求之前, 将请求内容进行一遍过滤, 比如请求的token为管理员还是用户

  2. 人→管理员(filter)→景区开放

  3. SpringBoot2.X里面的过滤器包含

    ApplicationContextHeaderFilter
    OrderedCharacterEncodingFilter
    OrderedFormContentFilter
    OrderedRequestContextFilter
    
  4. 过滤器的优先级

    Ordered.HIGHEST_PRECEDENCE 
    Ordered.LOWEST_PRECEDENCE
    
  5. 自定义Filter, 避免和默认的Filter优先级一样, 不然会冲突

  6. 注册Filter配置的两种方式

    1. bean FilterRegistrationBean
    2. Servlet3.0 WebFilter

13.2. 具体开发流程

13.2.1. 使用Servlet3.0注解开发自定义过滤器的流程

  1. 启动类里面增加 @ServletComponentScan,进⾏行扫描
  2. 新建一个 Filter类,implements Filter,并实现对应的接口
  3. @WebFilter 标记一个类为 filter,被spring进⾏行行扫描
  4. urlPatterns:拦截规则,支持正则匹配
  5. 控制chain.doFilter的方法的调用,来实现是否通过放行
  6. 不放行, web应⽤用 resp.sendRedirect("/index.html") 或者 返回json字符串串

请添加图片描述

13.2.2. 应用场景

权限控制, 用户登录状态控制

14. 回归本质Servlet3.0的注解原⽣生 Servlet实战

14.1. 使用 Servlet3.0的注解自定义原生

@WebServlet(name = "userServlet", urlPatterns = "/api/v1/test/customs")
public class UserServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        PrintWriter write = resp.getWriter();
        write.write("hello world, this is my custom servlet");
        write.flush();
        write.close();
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doGet(req, resp);
    }
}

14.2. Servlet3.0 注解Listener常用监听器

14.2.1. 作用

ServletContextListener // 应用启动监听器
HttpSessionLisener // 回话监听器
ServletRequestListener //请求监听器
@WebListener
public class RequestListener implements ServletRequestListener {
    @Override
    public void contextDestroyed(ServletContextEvent sce) {
            System.out.println("======contextDestroyed========");
    }
    @Override
    public void contextInitialized(ServletContextEvent sce) {
            System.out.println("======contextInitialized========");
    }
}

15. 拦截器 interceptor

15.1. 拦截器

和过滤器用途基本类似

15.2. 使用步骤

// 第一步: 继承拦截器
@Configuration
public class CustomWebMvcConfigurer implements WebMvcConfigurer{}

// 第二步 自定义 拦截器
preHandle: 调用Controller某个方法 之前
postHandle: 调用Controller某个方法 之后, 视图渲染之前, 如果控制器Controller出现了 异常, 则不会执行该方法
afterHandle: 调用Controller某个方法 不管有没有异常, 都会被调用, 用于清理资源

请添加图片描述

@Configuration
public class CustomWebMvcConfigurer implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 注册配置
        // 配置登录拦截器
        // 先注册, 先被拦截
        registry.addInterceptor(getLoginInterceptor()).addPathPatterns("/api/v1/pri/**");
        registry.addInterceptor(new TwoIntercepter()).addPathPatterns("/api/v1/test/**");
        WebMvcConfigurer.super.addInterceptors(registry);
    }

    @Bean
    public LoginInterceptor getLoginInterceptor() {
        return new LoginInterceptor();
    }
}

15.3. 按照注册顺序拦截, 先注册, 先拦截

15.4. 拦截器不生效常见问题

- 是否有加@Configuration
- 拦截路路径是否有问题  *** 
- 拦截器器最后路路径⼀一定要  /**  如果是⽬目录的话则是  /*/

15.5. 和Filter过滤器的区别

  1. FilterInterceptor二者都是AOP编程思想的体现,功能基本都可以实现
  2. 拦截器功能更强大些,Filter能做的事情它都能做
  3. Filter在只在Servlet前后起作用,而Interceptor够深入到方法前后、异常抛出前后等
  4. filter依赖于Servlet容器即web应用中,而Interceptor不依赖于Servlet容器所以可以运行在多种环境。
  5. 在接口调用的生命周期里,Interceptor可以被多次调用,而Filter只能在容器初始化时调用一次。
  6. FilterInterceptor的执行顺序
  7. 过滤前->拦截前->action执行->拦截后->过滤后

15.6. 不拦截

registry.addInterceptor(new TwoIntercepter()).addPathPatterns("/api/v1/test/**").excludePathPatterns("/**/*.html","/**/*.js");

16. 模板引擎

16.1. 模板引擎第一课: 异常信息返回到error.html页面上

16.1.1. 配置pom

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

16.1.2. 异常出错

    @GetMapping("error")
    public JsonData getList(){
        int a = 1/0; // 这里有异常会报错
        return JsonData.buildSuccess("hello list");
    }

16.1.3. 异常接收并返回error信息

@ControllerAdvice
public class CustomExtHandler {

    @ExceptionHandler(value = Exception.class)
    Object handlerException(Exception e, HttpServletRequest request) {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("error.html"); // 浏览器访问这个页面一样
        modelAndView.addObject("msg", e.getMessage());
        return modelAndView;
    }
}

error.html的路径是 src/main/resources/templates/error.html

16.2. 模板引擎介绍

16.2.1. JSP 后端渲染, 消耗性能

Java Server Pages动态网页技术,由应用服务器中的JsP引擎来编译和执行,再将生成的整个页面返回给客户端
可以写java代码
持表达式语言(e1、jst1)
内建函数
JsP->Servlet(占用JVM内存)permSize
javaweb官方推荐
springboot官方不推荐

16.2.2. Freemarker

FreeMarker Template Language:(FT工) 文件一般保存为xxx.ftl
严格依赖MvC模式,不依赖Servlet容器(不占用JvM内存)
内建函数

16.2.3. Thymeleaf 主推

轻量级的模板引擎(复杂逻辑业务的不推荐,解析DOM或者XML会占用多的内存)
可以直接在浏览器中打开且正确显示模板页面
直接是html结尾,直接编辑xdlcass.net/user/userinfo.html
社会工程学伪装

16.3. 模板引擎 freemarker实战

16.3.1. 依赖

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

16.3.2. freemarker基础配置

spring:
  freemarker:
    allow-request-override: false
    cache: false
    charset: UTF-8
    check-template-location: true
    content-type: text/html # 文件类型
    expose-request-attributes: true
    expose-session-attributes: true
    suffix: .ftl  #     # 文件后缀
    template-loader-path: classpath:/templates/ # 文件路径

16.3.3. Controller

@Controller // 这里需要注意, 不是RestController
@RequestMapping(value = "api/v1/test")
public class FreemarkerController {
    @Autowired
    WXConfig wxConfig;

    @GetMapping(value = "freemarker")
    public String index(ModelMap modelMap) {
        modelMap.addAttribute("setting", wxConfig);
        return "user/fm/index";
        // 不用加后缀, 因为配置文件里面已经指定了后缀
    }
}

16.3.4. index.ftl文件路径和内容

src/main/resources/templates/user/fm/index.ftl

<h1>setting.wxPayAppId : ${setting.wxPayAppId}</h1>

16.4. 模板引擎 temleaf

16.4.1. 依赖

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

16.4.2. 配置

  thymeleaf:
    cache: false
    mode: HTML5
    prefix: classpath:/templates/
    encoding: UTF-8
    servlet:
      content-type: text/html
    suffix: .html

16.4.3. index路径

src/main/resources/templates/tl/index.html

17. 定时器任务

17.1. 定时器任务的定义

  1. 某个时间定时处理某个任务
  2. 发邮件, 短信
  3. 消息提醒
  4. 订单通知
  5. 统计报表

17.2. 常见的定时任务

  1. Java自带的java.util.Timer类配置比较麻烦,时间延后问题
  2. Quartzi框架:配置更简单,xml或者注解适合分布式或者大型调度作业
  3. SpringBoot框架自带

17.3. 操作流程

  1. 启动类里面添加 @EnableScheduling // 打开定时器扫
  2. 定时任务业务类, 加注解 @Component
  3. 定时任务方法上注解 @Scheduled()
@Component
public class VideoOrderTask {

    // 每2s执行一次, 前提是该任务执行完成后, 执行时间点后xx秒再次执行
    @Scheduled(fixedRate = 2000)
    public void fixedRateTest() {

    }

    // https://tool.lu/crontab/
    @Scheduled(cron = "*/9 * * * * *")
    public void cronTest() {
    }

    // 上一次结束时间点往后再过3s后 执行一次
    @Scheduled(fixedDelay = 3000)
    public void fixedDelayTest() {
    }
}

18. 异步任务

请添加图片描述

18.1. 异步任务使用场景

  1. 处理log, 发送邮件, 短信
  2. 下单接口查看库存 1000
  3. 余额校验1500
  4. 风控用户 1000

18.2. 实现

  1. 启动类里面添加 @EnableAsync // 打开异步任务
  2. 异步任务类, 加注解 @Component 和 @Async
  3. Controller直接使用即可
// com/huawei/demo/task/AsyncTask.java
@Async
@Component
public class AsyncTask {
    public void task1() {
        Thread.sleep(4000);
        System.out.println("task 1");
    }

    public void task2() {
        Thread.sleep(4000);
        System.out.println("task 2");
    }
}

// src/main/java/com/huawei/demo/controller/TestController.java
    @Autowired
    AsyncTask asyncTask;

    @GetMapping(value = "async")
    public JsonData getAsync() {
        long start = System.currentTimeMillis();
        asyncTask.task1();
        asyncTask.task2();
        asyncTask.task3();
        long end = System.currentTimeMillis();
        return JsonData.buildSuccess(end - start);
    }

18.3. 想要拿到异步任务的结果

// async
public Future<String> task4(){
    try {
        Thread.sleep(4000L);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println(" task 4 ");

    return new AsyncResult<String>("task4");
}

// Controller
@GetMapping("async_get_result")
public JsonData testAsync(){

    long begin = System.currentTimeMillis();
    Future<String> task4 =  asyncTask.task4();
    Future<String> task5 =  asyncTask.task5();

    for(;;){ // 一直等待异步任务完成
        if(task4.isDone() && task5.isDone()){
            try {
                String task4Result = task4.get();
                System.out.println(task4Result);

                String task5Result = task5.get();
                System.out.println(task5Result);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }finally {
                break;
            }
        }
    }
    long end = System.currentTimeMillis();
    return JsonData.buildSuccess( end - begin);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

落子无悔!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值
>