• springBoot--终


    Web

    定制SpringMVC

    定制某些mvc配置

    • 如果您想保持Spring Boot MVC的默认配置,并在此基础上添加一些自定义配置,比如拦截器、格式化器、视图控制器等,可以创建一个使用@Configuration注解的配置类,并实现WebMvcConfigurer接口。同时不需要标注@EnableWebMvc注解。

      @Configuration
      public class CustomWebMvcConfigurer implements WebMvcConfigurer {
      
          @Override
          public void addInterceptors(InterceptorRegistry registry) {
              // 添加拦截器逻辑
          }
      
          @Override
          public void addFormatters(FormatterRegistry registry) {
              // 添加格式化器逻辑
          }
      
          @Override
          public void addViewControllers(ViewControllerRegistry registry) {
              // 添加视图控制器逻辑
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18

    例子

    • 自定义拦截器: 假设您希望在每个请求前后记录请求日志。首先,创建一个拦截器类来处理请求日志逻辑
    public class LoggingInterceptor implements HandlerInterceptor {
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
            // 在请求之前记录日志
            System.out.println("请求开始:" + request.getRequestURI());
            return true;
        }
    
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
            // 请求处理完成后记录日志
            System.out.println("请求结束:" + request.getRequestURI());
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 自定义格式化器: 假设您需要在URL参数中接收日期,并在处理请求时将其转换为java.util.Date类型。首先,创建一个格式化器类来完成日期的格式化/解析:
    public class DateFormatter implements Formatter<Date> {
    
        @Override
        public Date parse(String text, Locale locale) throws ParseException {
            // 将文本解析为日期对象
            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd", locale);
            return dateFormat.parse(text);
        }
    
        @Override
        public String print(Date date, Locale locale) {
            // 将日期对象格式化为文本
            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd", locale);
            return dateFormat.format(date);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 自定义视图控制器: 假设您希望处理一些简单的URL映射,而无需创建完整的Controller。首先,创建一个视图控制器类来处理URL映射:
    @Controller
    public class ViewController {
    
        @GetMapping("/hello")
        public String hello() {
            return "hello"; // 返回视图模板名称
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 在相关配置类中注册
    @Configuration
    public class CustomWebMvcConfigurer implements WebMvcConfigurer {
    	//拦截器
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(new LoggingInterceptor());
        }
        //格式化器
        @Override
        public void addFormatters(FormatterRegistry registry) {
            registry.addFormatter(new DateFormatter());
        }
        //视图控制器
        @Override
        public void addViewControllers(ViewControllerRegistry registry) {
            registry.addViewController("/hello").setViewName("hello");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    定制mvc核心组件

    1. 如果您只需要自定义一些核心组件实例,比如RequestMappingHandlerMappingRequestMappingHandlerAdapterExceptionHandlerExceptionResolver,可以通过创建一个WebMvcRegistrations组件并将其注册到Spring容器中实现。这样您可以自行创建和配置这些核心组件的实例,而不会影响其他默认配置。

      @Configuration
      public class Testconfig implements WebMvcRegistrations  {
          
          @Override
          public RequestMappingHandlerMapping getRequestMappingHandlerMapping(){
           	//重写自己的逻辑   
              return null;
      
          }
          @Override
          public RequestMappingHandlerAdapter getRequestMappingHandlerAdapter(){
              //重写自己的逻辑
              return null;
          }
          @Override
          public ExceptionHandlerExceptionResolver getExceptionHandlerExceptionResolver() {
              //重写自己的逻辑
              return null;
          }
      
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21

    完全自定义mvc

    需要注意的是,完全自定义Spring MVC可能需要更多的工作和理解框架的内部机制。在大多数情况下,使用Spring MVC的默认配置和特性已经足够满足绝大多数Web应用程序的需要。只有在特殊情况下才需要进行完全自定义。


    用于启用Spring MVC的完整功能,但是使用它可能会覆盖Spring Boot的自动配置,因此请谨慎使用。如果您只需要部分自定义配置,通常不需要使用@EnableWebMvc注解,而是仅实现WebMvcConfigurer接口即可。

    @Configuration
    @EnableWebMvc//这个注解加上的话,会将springBoot的默认配置覆盖掉,全部使用你定义的规则
    public class StaticConfig {
        @Bean
        //将WebMvcConfigurer放在容器中
        public WebMvcConfigurer webMvcConfigurer(){
            return new WebMvcConfigurer() {
                @Override
                public void addResourceHandlers(ResourceHandlerRegistry registry) {
                    WebMvcConfigurer.super.addResourceHandlers(registry);
                    registry.addResourceHandler("/static/**")
                            .addResourceLocations("classpath:/a/")
                            //缓存最大时间 单位秒
                            .setCacheControl(CacheControl.maxAge(7200, TimeUnit.SECONDS));
                }
            };
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    静态资源

    静态资源映射

    • 静态资源映射规则在 WebMvcAutoConfiguration 中进行了定义:
    • /webjars/** 的所有路径 资源都在 classpath:/META-INF/resources/webjars/
    • /** 的所有路径 资源都在 classpath:/META-INF/resources/classpath:/resources/classpath:/static/classpath:/public/

    静态资源缓存

    1. 所有静态资源都定义了缓存规则。【浏览器访问过一次,就会缓存一段时间】,但此功能参数无默认值
      1. period: 缓存间隔。 默认 0S;
      2. cacheControl:缓存控制。 默认无;
      3. useLastModified:是否使用lastModified头。 默认 false;

    示例

    1. 
      #1、spring.web:
      # 1.配置国际化的区域信息
      # 2.静态资源策略(开启、处理链、缓存)
      
      # 开启缓存映射
      spring.web.resources.add-mappings=true
      # 设置缓存间隔时间
      spring.web.resources.cache.period=3600
      # 缓存详细合并项控制,会覆盖.period这一项
      spring.web.resources.cache.cachecontrol.max-age=7200
      # 使用use-last-modified 服务器在接收到请求的时候,会给客户端返回一个时间
      # 这个时间就是静态资源最后一次修改的时间,如果客户端当前时间和这个返回的时间相同的话
      # 就说明这个资源没有被修改,客户端直接从缓存中获取,否则,将在服务器中重新获取这个静态资源
      # 不设置,默认为开启,true
      spring.web.resources.cache.use-last-modified=true 
      
      
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19

    自定义静态资源规则

    配置方式
    # 自定义静态资源文件夹的位置
    spring.web.resources.static-locations=classpath:/a/
    
    # 自定义静态资源规则
    # 自定义webjars访问路径
    spring.mvc.webjars-path-pattern=/wjs/**
    # 自定静态资源访问路径
    spring.mvc.static-path-pattern=/static/**
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    配置了访问路径.static-path-pattern以后,在浏览器请求时,请求路径就要写http://localhost:8080/static/hyp.jpg

    在配置了资源文件夹位置static-locations后 静态资源的存放位置就变了

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    代码方式

    **注意:**只要将WebMvcConfigurer加入到容器中,配置的底层行为就会生效

    ​ 加上@EnableMvc注解就会禁用springBoot的默认配置

    /**
     * 使用代码方式配置静态资源规则,代替使用properties配置
     * @author Han
     * @data 2023/7/17
     * @apiNode
     */
    @Configuration
    public class StaticConfig implements WebMvcConfigurer {
        @Override
        public void addResourceHandlers(ResourceHandlerRegistry registry) {
            //保留默认配置
            WebMvcConfigurer.super.addResourceHandlers(registry);
            //添加自己的配置【访问路径为/static/**】【修改静态资源所在包  为类路径下的a文件夹】
            registry.addResourceHandler("/static/**")
                    //添加自己的配置【访问路径为/static/**】【修改静态资源所在包  为类路径下的a文件夹】
            registry.addResourceHandler("/static/**")
                    .addResourceLocations("classpath:/a/")
                    //缓存最大时间 单位秒
                    .setCacheControl(CacheControl.maxAge(7200, TimeUnit.SECONDS));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    另一种方式,不继承WebMvcConfigurer类,将WebMvcConfigurer类作为一个Bean注册到容器中

    @Configuration
    @EnableWebMvc
    public class StaticConfig {
        @Bean
        //将WebMvcConfigurer放在容器中
        public WebMvcConfigurer webMvcConfigurer(){
            return new WebMvcConfigurer() {
                @Override
                public void addResourceHandlers(ResourceHandlerRegistry registry) {
                    WebMvcConfigurer.super.addResourceHandlers(registry);
                    registry.addResourceHandler("/static/**")
                            .addResourceLocations("classpath:/a/")
                            //缓存最大时间 单位秒
                            .setCacheControl(CacheControl.maxAge(7200, TimeUnit.SECONDS));
                }
            };
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    欢迎页

    欢迎页规则在 WebMvcAutoConfiguration 中进行了定义:

    1. 静态资源目录下找 index.html
    2. 没有就在 templates下找index模板页

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    Favicon(网站图标)

    1. 在静态资源目录下找 favicon.ico
    2. 将网站的图片放在静态资源目录下,并命名为favicon.ico,就可以改变网页的图标

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    路径匹配策略

    • springBoot支持Ant风格的路径
    • 新版springBoot默认支持pathPatternParser风格路径
    @GetMapping("/testPath/a*/b?/{pp}/**")
        public String testPath(@PathVariable("pp") String pp, HttpServletRequest request){
            var requestURL = request.getRequestURL();
            System.out.println(requestURL);
    
            return requestURL.toString()+"参数pp="+pp;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    • 但是如果将**放在路径的中间位置,@GetMapping("/testPath/**/a*/b?/{pp}/**")
    @GetMapping("/testPath/**/a*/b?/{pp}/**")
        public String testPath(@PathVariable("pp") String pp, HttpServletRequest request){
            var requestURL = request.getRequestURL();
            System.out.println(requestURL);
    
            return requestURL.toString()+"参数pp="+pp;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 就会报错【检测到无效的映射模式】

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    • 解决方法就是将路径匹配规则在配置文件中修改为spring.mvc.pathmatch.matching-strategy=ant_path_matcher

    • 这是因为新版springBoot默认支持的路径匹配风格是spring.mvc.pathmatch.matching-strategy=path_pattern_parser这个效率非常高但是不支持将**写在路径的中间位置

    修改配置文件后:

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    内容协商

    一套系统支持返回多种数据类型

    分为:

    • 基于请求头的内容协商
    • 基于请求参数的内容协商

    控制器方法

     /**
         * 测试内容协商
         * 1、返回结果为json
         * 这里遇到了问题,网页无法显示json数据
         * 是因为当时没有给pojo类中提供get方法,提供以后正常了
         * @return
         */
        @GetMapping("/person")
        public Person person(){
            return new Person("小韩", 21);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    制定返回json类型数据

    ​ 因为springBoot化境下默认导入了json类型的内容协商,所以返回一个对象在浏览器默认显示的是Json

    制定返回xml类型数据

    • 导入支持转换成xml格式的依赖

       <dependency>
           <groupId>com.fasterxml.jackson.dataformatgroupId>
           <artifactId>jackson-dataformat-xmlartifactId>
      dependency>
      
      • 1
      • 2
      • 3
      • 4
    • 给要返回的数据的类上标注@注解

      @JacksonXmlRootElement//可以写出为xml文档
      public class Person {
          private String name;
          private Integer age;
      
          public Person(String name, Integer age) {
              this.name = name;
              this.age = age;
          }
      …………
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
    • 配置配置文件

      注意:基于请求头的内容协商是默认开启的,但是基于请求参数的是需要手动开启的

      # 开启基于请求参数的内容协商
      spring.mvc.contentnegotiation.favor-parameter=true
      # 修改请求参数指定内容协商时的使用的参数名 默认为format
      spring.mvc.contentnegotiation.parameter-name=type
      
      
      • 1
      • 2
      • 3
      • 4
      • 5

    制定返回自定义类型数据

    新增其他内容协商

    1、增加媒体类型支持的依赖

    2、编写properties媒体类型配置

    3、编写自己的MessageConverter

    4、编写HttpMessageConverter。也就是在配置类中配置MessageConverter,将编写的MessageConverter添加到底层

    系统提供的MessageConverter

    八个:

      • ByteArrayHttpMessageConverter: 支持字节数据读写
      • StringHttpMessageConverter: 支持字符串读写
      • ResourceHttpMessageConverter:支持资源读写
      • ResourceRegionHttpMessageConverter: 支持分区资源写出
      • AllEncompassingFormHttpMessageConverter:支持表单xml/json读写
      • MappingJackson2HttpMessageConverter: 支持请求响应体Json读写
    • 系统提供默认的MessageConverter 功能有限,仅用于json或者普通返回数据。额外增加新的内容协商功能,必须增加新的HttpMessageConverter

    返回yaml类型数据

    1、添加依赖

    添加支持返回yaml格式的依赖

     
            <dependency>
                <groupId>com.fasterxml.jackson.dataformatgroupId>
                <artifactId>jackson-dataformat-yamlartifactId>
            dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2、编写配置

    # 增加yaml类型
    spring.mvc.contentnegotiation.media-types.yaml=text/yaml
    
    • 1
    • 2

    3、编写组件 自己的MessageConverter

    作用是将对象转换成yaml格式写出【这一步可以将对象自己定义返回类型】

    
    /**
     * 编写组件
     * 将对象可以返回为yaml格式
     * @author Han
     * @data 2023/7/22
     * @apiNode
     */
    //编写自己的类,继承AbstractHttpMessageConverter类
    public class MyYamlHttpConverters extends AbstractHttpMessageConverter<Object> {
    
        private ObjectMapper objectMapper;
    
        public MyYamlHttpConverters() {
            //定义媒体类型,这里的类型要和配置文件中一致
            super(new MediaType("text", "yaml", Charset.forName("utf-8")));
            
            YAMLFactory disableYaml = new YAMLFactory().disable(YAMLGenerator.Feature.WRITE_DOC_START_MARKER);
            //转xml或者Yaml都是ObjectMapper来起作用的
            this.objectMapper = new ObjectMapper(disableYaml);
        }
    
        @Override
        protected boolean supports(Class<?> clazz) {
            //这里可以添加逻辑判断
            // 判断传入的否是一个对象
            //在这里先不判断
            return true;
        }
    
        @Override
        protected Object readInternal(Class<?> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
            return null;
        }
    
        @Override//将对象写出去
        protected void writeInternal(Object methodReturnValue, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
            //获取对应的输出流
            OutputStream body = outputMessage.getBody();
            try {
                this.objectMapper.writeValue(body, methodReturnValue);
            } finally {
                body.close();
            }
    
        }
    }
    
    
    • 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
    • 44
    • 45
    • 46
    • 47
    • 48

    4、在配置类中配置,增加HttpMessageConverter

    在配置类中添加组件

    @Configuration
    public class StaticConfig {
        @Bean
        //将WebMvcConfigurer放在容器中
        public WebMvcConfigurer webMvcConfigurer(){
            return new WebMvcConfigurer() {
                @Override
                public void addResourceHandlers(ResourceHandlerRegistry registry) {
                    WebMvcConfigurer.super.addResourceHandlers(registry);
                    registry.addResourceHandler("/static/**")
                            .addResourceLocations("classpath:/a/")
                            //缓存最大时间 单位秒
                            .setCacheControl(CacheControl.maxAge(7200, TimeUnit.SECONDS));
                }
    
                @Override
                public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
                    converters.add(new MyYamlHttpConverters());
                }
            };
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    效果

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    模板引擎

    Thymeleaf整合

    引入thymeleaf依赖

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

    自动配置原理

    1. 开启了 org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration 自动配置
    2. 属性绑定在 ThymeleafProperties 中,对应配置文件spring.thymeleaf内容
    3. 所有的模板页面默认在 classpath:/templates文件夹下
    4. 默认效果
      1. 所有的模板页面在 classpath:/templates/下面找
      2. 找后缀名为.html的页面
    5. 这部分看语雀文档

    拦截器

    以用户登录为例子

    使用步骤

    1. 编写一个拦截器实现 HandlerInterceptor 接口
    @Slf4j
    public class LoginInterceptor implements HandlerInterceptor {
        /**
         * 目标方法执行之前
         */
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            String requestURI = request.getRequestURI();
            log.info("preHandle拦截的请求路径是{}", requestURI);
            //登录检查逻辑
            HttpSession session = request.getSession();
            Object loginUser = session.getAttribute("loginUser");
    
            if (loginUser != null) {
                //放行
                return true;
            }
            //拦截住。未登录。跳转到登录页
            request.setAttribute("msg", "请先登录");
            // re.sendRedirect("/");
            request.getRequestDispatcher("/").forward(request, response);
            return false;
        }
    
        /**
         * 目标方法执行完成以后
         */
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse
                response, Object handler, ModelAndView modelAndView) throws Exception {
            log.info("postHandle执行{}", modelAndView);
        }
    
        /**
         * 页面渲染以后
         */
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse
                response, Object handler, Exception ex) throws Exception {
            log.info("afterCompletion执行异常{}", ex);
        }
    }
    
    
    • 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

    2.拦截器注册到容器中(实现 WebMvcConfigurer 的 addInterceptors() )

    3.指定拦截规则(注意,如果是拦截所有,静态资源也会被拦截】

    @Configuration
    public class AdminWebConfig implements WebMvcConfigurer{
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
        	registry.addInterceptor(new LoginInterceptor())//拦截器注册到容器中
       				.addPathPatterns("/**") //所有请求都被拦截包括静态资源
                	//放行的请求
                	.excludePathPatterns("/","/login","/css/**","/fonts/**","/images/**", "/js/**","/aa/**"); 
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    国际化

    在springBoot核心配置文件中进行配置时使用的是 spring.messages

    编写国际化配置文件

    默认:

    ​ 配置文件名:message.properties 无区域代码

    login=login
    sign_in=sign_in
    
    • 1
    • 2

    英文版:使用区域代码_en_US

    ​ 配置文件名:message_en_US.properties _en_US前的名称随意取

    login=login
    sign_in=sign_in
    
    • 1
    • 2

    中文版:使用区域代码_zh_CN

    ​ 配置文件名:message_zh_CN.properties _zh_CN前的名称随意取

    login=登录
    sign_in=注册
    
    • 1
    • 2

    使用thymeleaf语法进行国际化内容显示

    使用#{}

    错误处理

    [原理][https://www.yuque.com/leifengyang/springboot3/wp5l9qbu1k64frz1#mMAt4]可查看语雀文档的错误处理章节

    实战

    1.前后端分离项目

    ​ 前后端分离项目可使用@ControllerAdvice+@ExceptionHandler进行统一异常处理

    2.服务端页面渲染【前后端不分离】

    ​ HTTP状态错误

    ​ 给classpath:/templetes/error目录下放精确码html页面【404.html/500.html……】

    ​ 给classpath:/templetes/error目录下放模糊码html页面【4xx.html/5xx.html……】
    ​ 发生业务错误

    ​ 核心业务**,每一种错误,都应该代码控制,跳转到自己定制的错误页

    ​ 通用业务**,classpath:/templates/error.html页面,显示错误信息

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    注意: springBoot自动将错误信息封装到Modal中,如果要打印错误信息只需要在页面中直接使用${}获取

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    前后端不分离方式

    最后出现什么错误就会到对应的错误页面去显示错误信息

    ​ 400.html:

    DOCTYPE html>
    <html lang="en">
        <head>
            <meta charset="UTF-8">
            <title>Titletitle>
        head>
        <body>
            请求参数错误
            <br>
            错误信息:[[${message}]]
        body>
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    ​ 控制器方法

    @Controller
    @Slf4j
    public class HtmlController {
        @GetMapping("/hello")
        public String hello(@RequestParam("name") String name , Model model){
            //log.info(name);
            //使用model向前端页面展示数据
            model.addAttribute("name", name);
            return "hello";
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    前后端分离方式

    ​ 只需要集中处理错误,页面由前端负责

    返回json数据,在页面的错误信息中显示json数据

    /**
     * @ControllerAdvice 这个注解表明这个类集中处理所有controller发生的错误
     * @author Han
     * @data 2023/7/26
     * @apiNode
     */
    @RestControllerAdvice
    public class AllExceptionHandler {
        @ExceptionHandler(Exception.class)//传入发生异常的类型
        @ResponseBody
        /**
         * 写错误信息处理方法  可返回对象
         * 发生什么错误返回什么对象
         */
        public String handlerException(Exception e) {
            return "错误!! 原因:"+e.getMessage();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    数据访问

    ssm整合

    • 首先导入要使用的starter
    <dependencies>
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-webartifactId>
            dependency>
            <dependency>
                <groupId>org.mybatis.spring.bootgroupId>
                <artifactId>mybatis-spring-boot-starterartifactId>
                <version>3.0.2version>
            dependency>
    
            <dependency>
                <groupId>com.mysqlgroupId>
                <artifactId>mysql-connector-jartifactId>
                <scope>runtimescope>
            dependency>
            <dependency>
                <groupId>org.projectlombokgroupId>
                <artifactId>lombokartifactId>
                <optional>trueoptional>
            dependency>
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-testartifactId>
                <scope>testscope>
            dependency>
            <dependency>
                <groupId>org.mybatis.spring.bootgroupId>
                <artifactId>mybatis-spring-boot-starter-testartifactId>
                <version>3.0.2version>
                <scope>testscope>
            dependency>
        dependencies>
    
    • 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
    • 配置基础的连接数据源
    # 配置数据源
    # 驱动
    spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
    # url
    spring.datasource.url=jdbc:mysql://localhost:3306/p_springBoot
    # 数据库用户名及密码
    spring.datasource.username=root
    spring.datasource.password=hyp
    spring.datasource.type=com.zaxxer.hikari.HikariDataSource
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 以查询User对象为例
      • 首先要创建数据库表映射对象User
    @Data
    public class User {
        private Long id;
        private String loginName;
        private String nickName;
        private String passwd;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    编写相关Mapper接口和Mapper.xml映射文件

    @Mapper//标记@Mapper注解表明这个接口是一个Mapper
    public interface UserMapper {
    
        User getUserByID(@Param("id") Long id);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    编写SQL映射文件

    
    DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    
    <mapper namespace="com.hyp.ssm.mapper.UserMapper">
        <select id="getUserByID" resultType="user">
            select * from t_user where id = #{id}
        select>
    mapper>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    编写controller控制器方法

    @RestController
    @Slf4j
    public class UserController {
        // 将Mapper接口注入
        @Autowired
        UserMapper userMapper;
    
        @GetMapping("/getUserById/{id}")
        public User getUserById(@PathVariable("id") Long id) {
            log.info("查询id为【{}】的用户",id);
            return userMapper.getUserByID(id);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    mybatis相关配置

    # 配置mybatis
    # 别名机制 在Sql映射文件UserMapper.xml文件中的resultType属性使用别名
    mybatis.type-aliases-package=com.hyp.ssm.bean
    # 告诉mybatis SQL映射文件在哪里
    mybatis.mapper-locations=classpath:/mapper/*.xml
    # 开启驼峰命名映射
    mybatis.configuration.map-underscore-to-camel-case=true
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    测试:

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    注意:

    在标注Mapper接口时有两种方法

    • 在Mapper接口上添加@Mapper注解,加了这个注解后,明示了这个接口是一个Mapper接口,并且还将它作为一个bean交给ioc容器进行管理
    • 还可以在主启动类上添加注解@MapperScan(basePackages = "填写Mapper接口所在包")
    /**
     *@MapperScan 这个注解是用来扫描Mapper接口的
     *              如果在Mapper接口上添加了@Mapper注解,就不用再加这个注解了
     */
    @SpringBootApplication
    @MapperScan(basePackages = "com.hyp.ssm.mapper")
    public class SsmApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(SsmApplication.class, args);
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 他们只要存在一个就可以,但也可以同时存在

    springBoot基础特性

    profile环境隔离

    • Spring Profiles 提供一种隔离配置的方式,使其仅在特定环境生效;
    • 任何@Component, @Configuration 或 @ConfigurationProperties 可以使用 @Profile 标记,来指定何时被加载。【容器中的组件都可以被 @Profile标记】
    @Profile({"test"})
    @Component
    public class Cat {
        private Integer id;
        private String name;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    @Profile({"prod"})
    @Component
    public class Person {
        private String name;
        private Integer age;
        private User user;
        private Cat cat;
        private Map<String, Dog> dogMap;
        private List<Dog> dogs;
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在配置文件中选择激活的环境进行激活

    • 这时候,ioc容器中只有Person组件,没有Cat组件
    # 激活prod环境
    spring.profiles.active=prod
    
    • 1
    • 2

    环境包含

    • 生效的环境 = 激活的环境/默认环境 + 包含的环境

    • 项目里面这么用

      • 基础的配置mybatislogxxx:写到包含环境中
      • 需要动态切换变化的 dbredis:写到激活的环境中
    # 包含环境
    # 不管是否激活了Dev 和 test 环境 总是要生效的环境
    spring.profiles.include=prod,test
    
    • 1
    • 2
    • 3

    环境分组

    # 环境分组
    spring.profiles.group.must=dev,test
    spring.profiles.group.A = prod,test
    # 可以选择一个组来激活
    spring.profiles.active=must
    
    • 1
    • 2
    • 3
    • 4
    • 5

    组件环境隔离

    • 一:组件环境隔离
      • 1、标识环境
        • 1、给定区分几个环境 : dev(开发) text(测试) prod(生产)
        • 2、指定每个组件在那个环境下生效 【在组件类上添加@profile注解】
          • 通过:@Profile({"prod"})……

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    • 如果组件没有标注@Profile 那么就是所有环境下都能使用这个组件
    • 如果标注了@Profile({"default"}) 在没有激活指定环境时,默认只有这一个组件

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    • 3、只有激活这些指定的环境,这些组件才能生效

    • 2、激活环境

      • 1、通过配置文件激活 spring.profiles.active=prod

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    配置文件环境隔离

    二:配置文件环境隔离

    • 1、application.properties 主配置文件,在任何环境下都生效
    • 2、其他profile环境下配置文件命名规范:

      •  `application-dev.properties`
        
        • 1
      • application-test.properties

        …… …… ……

      •  格式:`application-{profile标识}.properties`
        
        • 1
    •  3、激活指定环境即可  会自动选择对应环境的配置文件进行配置
      
      • 1
    •  4、**项目的所有生效配置=激活的配置文件的所有项+主配置文件和激活文件`不冲突`的所有项**
       *      如果发生了配置冲突 以激活的配置文件的配置为准
       *      如:
              *      在`application.properties`中配置的端口号为`8080`
              *      在`application-dev.properties`中配置的端口号为`9090`
              *      我们激活了`dev环境`,那么会以dev环境中的端口号`9090为准`
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
    •  注意:要激活某个环境,只能在主配置文件中设置
      
      • 1

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    外部化配置

    • application-{profile}.properties可以作为指定环境的配置文件

    • 激活这个环境,配置就会生效。最终生效的所有配置

      • application.properties:主配置文件,任意时候都生效
      • application-{profile}.properties:指定环境配置文件,激活指定环境生效

    profile优先级 > application

    配置生效优先级

    常用的有:

    • 默认属性【SpringApplication.setDefaultProperties指定,在启动类中设置】
    • 配置文件 【application.properties/yml等】
    • 命令行参数

    注意:
    配置文件优先级如下**(返序)**

    1. jar 包内application.properties/yml
    2. jar 包内application-{profile}.properties/yml
    3. jar 包外application.properties/yml
    4. jar 包外application-{profile}.properties/yml

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    导入外配置文件

    # 导入一个配置文件,这配置文件中配置了server.port=9999
    spring.config.import=classpath:aaa.properties
    # 然后这里也设置了端口,但是因为是导入的配置,
    # 所以优先级大于本身所有的,则最终端口是aaa.properties所定义的
    server.port=8080
    
    • 1
    • 2
    • 3
    • 4
    • 5

    属性占位符

    # 属性占位符,可以获取配置文件中的配置项的值
    # 在controller中使用@value注解拿出来
    portValue = 端口号是:${server.port}
    
    • 1
    • 2
    • 3
    @RestController
    public class HelloController {
        
        @Value("${portValue}")
        String portValue;
        
        @GetMapping("/getPort")
        public String getPortValue(){
            return portValue;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    最终页面上显示:端口号是9999

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    单元测试

    springBoot核心原理

    事件和监听器

    生命周期监听

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    package com.example.texing.CoreTeXing.listener;
    
    import org.springframework.boot.ConfigurableBootstrapContext;
    import org.springframework.boot.SpringApplicationRunListener;
    import org.springframework.context.ConfigurableApplicationContext;
    import org.springframework.core.env.ConfigurableEnvironment;
    
    import java.time.Duration;
    
    /**
     *
     *
     *   Listener先要从 META-INF/spring.factories 读到
     *
     *  1、引导: 利用 BootstrapContext 引导整个项目启动
     *        starting:              应用开始,SpringApplication的run方法一调用,只要有了 BootstrapContext 就执行
     *       environmentPrepared:   环境准备好(把启动参数等绑定到环境变量中),但是ioc还没有创建;【调一次】
     *   2、启动:
     *        contextPrepared:       ioc容器创建并准备好,但是sources(主配置类)没加载。并关闭引导上下文;组件都没创建  【调一次】
     *        contextLoaded:         ioc容器加载。主配置类加载进去了。但是ioc容器还没刷新(我们的bean没创建)。
     *        =======截止以前,ioc容器里面还没造bean呢=======
     *        started:               ioc容器刷新了(所有bean造好了),但是 runner 没调用。
     *        ready:                  ioc容器刷新了(所有bean造好了),所有 runner 调用完了。
     *   3、运行
     *       以前步骤都正确执行,代表容器running。
     *   4、运行失败
     *      failed  以上任意一个步骤如果发生了异常,就会启动失败
     *
     */
    public class MyListener implements SpringApplicationRunListener {
        @Override
        public void starting(ConfigurableBootstrapContext bootstrapContext) {
            System.out.println("===starting===  正在启动 ");
        }
    
        @Override
        public void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) {
            System.out.println("===environmentPrepared===  环境准备完成 ");
        }
    
        @Override
        public void contextPrepared(ConfigurableApplicationContext context) {
            System.out.println("===contextPrepared===  ioc容器准备完成 ");
    
        }
    
        @Override
        public void contextLoaded(ConfigurableApplicationContext context) {
            System.out.println("===contextLoaded===  ioc容器加载完成 ");
    
        }
    
        @Override
        public void started(ConfigurableApplicationContext context, Duration timeTaken) {
            System.out.println("===started===  启动完成 ");
        }
    
        @Override
        public void ready(ConfigurableApplicationContext context, Duration timeTaken) {
            System.out.println("===ready===  准备就绪 ");
        }
    
        @Override
        public void failed(ConfigurableApplicationContext context, Throwable exception) {
            System.out.println("===failed===  应用启动失败 ");
        }
    }
    
    
    • 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
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
  • 相关阅读:
    七日杀服务器租用多少钱?七日杀服务器需要什么配置?宿迁BGP服务器43.248.184.x
    EMR 集群时钟同步问题及解决方案An error occurred (InvalidSignatureException)
    IT技术培训班:实用还是虚幻?
    【安卓】在安卓中使用HTTP协议的最佳实践
    12 个免费 GIS 数据源介绍:最佳全球栅格和矢量数据集
    亚马逊美国站车充UL2089测试报告办理
    嵌入式开发:关于嵌入式软件建模的5个经验
    基于springcloud+vue的分布式架构网上商城网站 前后端分离
    自建SQL server 服务无法启动,提示评估期已过
    两个连续变量乘积线性化——McCormick envelope近似
  • 原文地址:https://blog.csdn.net/TCDHanyongpeng/article/details/132580469