• 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"})
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8

    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插件
    >
          >
            >
              >org.springframework.boot>
              >spring-boot-maven-plugin>
            >
          >
    >
    # 如果没有加,则执⾏行行jar包 ,报错如下
    # java -jar spring-boot-demo-0.0.1-SNAPSHOT.jar
    # no main manifest attribute, in spring-boot-demo-0.0.1-SNAPSHOT.jar
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    5. SpringBoot在线教育架构搭建

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

    controller
    service
    dao
    domain
    utils
    
    • 1
    • 2
    • 3
    • 4
    • 5

    请添加图片描述

    请添加图片描述

    请添加图片描述

    请添加图片描述

    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);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

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

        @GetMapping(value = "list")
        public Object list() {
            List
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    7. post请求User登录增加token

    	private static Map 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;
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    请添加图片描述

    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 chapterList;
        
        
        @PostMapping(value = "save_video_chapter")
        public JsonData saveVideoChapter(@RequestBody Video video) {
            System.out.println(video.toString());
            return JsonData.buildSuccess("");
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    请添加图片描述

    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
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    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.bootgroupId>  
             <artifactId>spring-boot-devtoolsartifactId>  
             <optional>trueoptional>  
      dependency>
      
      
      <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.bootgroupId>
                    <artifactId>spring-boot-maven-pluginartifactId>
                    <configuration>
                        <fork>truefork>
                    configuration>
                plugin>
            plugins>
        build>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    9.3.2. IDEA里面设置

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

    请添加图片描述

    10. 常见配置文件

    10.1. 常见文件格式

    xml
    properties
    json
    yaml
    
    • 1
    • 2
    • 3
    • 4

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

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

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

      server:
        port: 8082
        tomcat:
          accept-count: 200
        compression:
          min-response-size: 1M
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

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

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

      /* 默认配置文件 spring.profiles.active=prod # 就会加载对应的prod配置文件
      resources/application.properties
      resources/application-dev.properties # 开发环境 (乌三)
      resources/application-prod.properties # 生产环境(现网)
      */
      
      • 1
      • 2
      • 3
      • 4
      • 5
    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;
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10

    请添加图片描述

     -   设置加载
    
    • 1

    请添加图片描述

    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]}"
    */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    11. springboot2.X进行单元测试

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

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

    • 测试里面的种类

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

    11.2. 单元测试

    11.2.1. 引入依赖

    
     <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-testartifactId>
        <scope>testscope>
    dependency>
    
    <dependency>
          <groupId>junitgroupId>
          <artifactId>junitartifactId>
          <version>4.12version>
          <scope>testscope>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    11.2.2. 配置注解

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

    11.2.3. 常用的单元注解

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

    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);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    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);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    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);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    13. 过滤器

    13.1. 过滤器介绍

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

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

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

      ApplicationContextHeaderFilter
      OrderedCharacterEncodingFilter
      OrderedFormContentFilter
      OrderedRequestContextFilter
      
      • 1
      • 2
      • 3
      • 4
    4. 过滤器的优先级

      Ordered.HIGHEST_PRECEDENCE 
      Ordered.LOWEST_PRECEDENCE
      
      • 1
      • 2
    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);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

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

    14.2.1. 作用

    ServletContextListener // 应用启动监听器
    HttpSessionLisener // 回话监听器
    ServletRequestListener //请求监听器
    
    • 1
    • 2
    • 3
    @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========");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    15. 拦截器 interceptor

    15.1. 拦截器

    和过滤器用途基本类似

    15.2. 使用步骤

    // 第一步: 继承拦截器
    @Configuration
    public class CustomWebMvcConfigurer implements WebMvcConfigurer{}
    
    // 第二步 自定义 拦截器
    preHandle: 调用Controller某个方法 之前
    postHandle: 调用Controller某个方法 之后, 视图渲染之前, 如果控制器Controller出现了 异常, 则不会执行该方法
    afterHandle: 调用Controller某个方法 不管有没有异常, 都会被调用, 用于清理资源
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    请添加图片描述

    @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();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

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

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

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

    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");
    
    • 1

    16. 模板引擎

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

    16.1.1. 配置pom

    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-thymeleafartifactId>
    dependency>
    
    • 1
    • 2
    • 3
    • 4

    16.1.2. 异常出错

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

    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;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    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官方不推荐
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    16.2.2. Freemarker

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

    16.2.3. Thymeleaf 主推

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

    16.3. 模板引擎 freemarker实战

    16.3.1. 依赖

    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-freemarkerartifactId>
    dependency>
    
    • 1
    • 2
    • 3
    • 4

    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/ # 文件路径
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    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";
            // 不用加后缀, 因为配置文件里面已经指定了后缀
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

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

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

    <h1>setting.wxPayAppId : ${setting.wxPayAppId}h1>
    
    • 1

    16.4. 模板引擎 temleaf

    16.4.1. 依赖

            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-thymeleafartifactId>
            dependency>
    
    • 1
    • 2
    • 3
    • 4

    16.4.2. 配置

      thymeleaf:
        cache: false
        mode: HTML5
        prefix: classpath:/templates/
        encoding: UTF-8
        servlet:
          content-type: text/html
        suffix: .html
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    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() {
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    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);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

  • 相关阅读:
    【C语言】指针的入门详细介绍
    在Mac上安装和配置Node.js
    QML粒子系统
    叉树的逆向有序遍历(C/C++实现)
    Python开源项目周排行 2023年第38周
    Restful Web Service
    【Odoo】Odoo16-性能优化提升
    第二证券|12月A股投资方向来了!这些板块已先涨为敬
    数据挖掘实战(2)——糖尿病数据集(回归问题)
    python中的各种打断方式、终止代码
  • 原文地址:https://blog.csdn.net/qq_32460819/article/details/126902488