Springboot MVC

发布于:2024-12-08 ⋅ 阅读:(116) ⋅ 点赞:(0)

1. Springboot为MVC提供的自动配置

Spring Boot 为 Spring MVC 提供了自动配置,这在大多数应用程序中都能很好地工作。除了已经实现了 Spring MVC 的默认功能外,自动配置还提供了以下特性:

  • 包括 ContentNegotiatingViewResolver 和 BeanNameViewResolver 的 Bean。
    • ContentNegotiatingViewResolver 自动根据HTTP请求头中Accept字段来选择合适的视图技术渲染响应。
    • BeanNameViewResolver的作用是根据视图名称找到视图View对象。​
  • 支持提供静态资源,包括对 WebJars的支持。
    • 静态资源路径默认已经配置好了。默认会去static目录下找。
  • 自动注册 Converter、GenericConverter 和 Formatter 的 Bean。
    • Converter:转换器,做类型转换的,例如表单提交了用户数据,将表单数据转换成User对象。
    • Formatter:格式化器,做数据格式化的,例如将Java中的日期类型对象格式化为特定格式的日期字符串。或者将用户提交的日期字符串,转换为Java中的日期对象。
  • 支持 HttpMessageConverters。
    • 内置了很多的HTTP消息转换器。例如:MappingJackson2HttpMessageConverter可以将json转换成java对象,也可以将java对象转换为json字符串。
  • 自动注册 MessageCodesResolver。
    • SpringBoot会自动注册一个默认的消息代码解析器
    • 帮助你在表单验证出错时生成一些特殊的代码。这些代码让你能够更精确地定位问题,并提供更友好的错误提示。
  • 静态 index.html 文件支持。
    • Spring Boot 会自动处理位于项目静态资源目录下的 index.html 文件,使其成为应用程序的默认主页
  • 自动使用 ConfigurableWebBindingInitializer Bean。
    • 用它来指定默认使用哪个转换器,默认使用哪个格式化器。在这个类当中都已经配好了。
  • 如果您不想使用自动配置并希望完全控制 Spring MVC,可以添加您自己的带有 @EnableWebMvc注解的 @Configuration
  • 如果您希望保留这些 Spring Boot MVC 定制化设置并进行更多的 MVC 定制化(如拦截器、格式化程序、视图控制器等其他功能),可以添加您自己的类型为 WebMvcConfigurer 的 @Configuration 类。但不能使用​@EnableWebMvc注解​。

2. 关键的自动配置类:WebMvcAutoConfiguration

public class WebMvcAutoConfiguration {

  @Bean
  @ConditionalOnMissingBean({HiddenHttpMethodFilter.class})
  @ConditionalOnProperty(
    prefix = "spring.mvc.hiddenmethod.filter",
    name = {"enabled"}
  )
  // 这个过滤器是专门处理Rest请求的。GET POST PUT DELETE请求。
  public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {
    return new OrderedHiddenHttpMethodFilter();
  }

  @Bean
  @ConditionalOnMissingBean({FormContentFilter.class})
  @ConditionalOnProperty(
    prefix = "spring.mvc.formcontent.filter",
    name = {"enabled"},
    matchIfMissing = true
  )
  //OrderedFormContentFilter 是 Spring Boot 中用于处理 HTTP 请求的一个过滤器,特别是针对 PUT 和 DELETE 请求。
  //这个过滤器的主要作用是在处理 PUT 和 DELETE 请求时,确保如果请求体中有表单格式的数据,这些数据会被正确解析并可用。
  public OrderedFormContentFilter formContentFilter() {
    return new OrderedFormContentFilter();
  }
    @Configuration(
    proxyBeanMethods = false
  )
  @Import({EnableWebMvcConfiguration.class})
  @EnableConfigurationProperties({WebMvcProperties.class, WebProperties.class})
  @Order(0)
  // 关键方法: 进行了一系列的Spring MVC相关配置
  public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer, ServletContextAware {}
  
   @Configuration(
    proxyBeanMethods = false
  )
  @EnableConfigurationProperties({WebProperties.class})
  // 启动配置
  public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {}
}

3. WebMvcAutoConfigurationAdapter :修改配置

1. 类实现的 WebMvcConfigurer接口

public interface WebMvcConfigurer {
    // 用于定制 Spring MVC 如何匹配请求路径到控制器
    default void configurePathMatch(PathMatchConfigurer configurer) {}
    // 用于定制 Spring MVC 的内容协商策略,以确定如何根据请求的内容类型来选择合适的处理方法或返回数据格式
    default void configureContentNegotiation(ContentNegotiationConfigurer configurer) {}
    // 用于定制 Spring MVC 处理异步请求的方式
    default void configureAsyncSupport(AsyncSupportConfigurer configurer) {}
    // 用于定制是否将某些静态资源请求转发WEB容器默认的Servlet处理
    default void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {}
    // 用于定制 Spring MVC 解析视图的方式,以确定如何将控制器返回的视图名称转换为实际的视图资源。
    default void configureViewResolvers(ViewResolverRegistry registry) {}
    // 用于定制 Spring MVC 如何处理 HTTP 请求和响应的数据格式,包括 JSON、XML 等内容类型的转换
    default void configureMessageConverters(List<HttpMessageConverter<?>> converters) {}
    // 用于定制 Spring MVC 如何处理控制器方法中发生的异常,并提供相应的错误处理逻辑。
    default void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {}

    // 用于定制 Spring MVC 如何处理数据的格式化和解析,例如日期、数值等类型的对象的输入和输出格式。
    default void addFormatters(FormatterRegistry registry) {}
    // 用于定制 Spring MVC 如何使用拦截器来处理请求和响应,包括在请求进入控制器之前和之后执行特定的操作。
    default void addInterceptors(InterceptorRegistry registry) {}
    // 用于定制 Spring MVC 如何处理静态资源(如 CSS、JavaScript、图片等文件)的请求。
    default void addResourceHandlers(ResourceHandlerRegistry registry) {}
    // 用于定制 Spring MVC 如何处理跨域请求,确保应用程序可以正确地响应来自不同域名的 AJAX 请求或其他跨域请求。
    default void addCorsMappings(CorsRegistry registry) {}
    // 用于快速定义简单的 URL 到视图的映射,而无需编写完整的控制器类和方法。
    default void addViewControllers(ViewControllerRegistry registry) {}
    // 用于定制 Spring MVC 如何解析控制器方法中的参数,包括如何从请求中获取并转换参数值。
    default void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {}
    // 用于定制 Spring MVC 如何处理控制器方法的返回值,包括如何将返回值转换为实际的 HTTP 响应。
    default void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> handlers) {}

    // 用于定制 Spring MVC 如何处理 HTTP 请求和响应的数据格式,允许你添加或调整默认的消息转换器,以支持特定的数据格式。
    default void extendMessageConverters(List<HttpMessageConverter<?>> converters) {}
    // 用于定制 Spring MVC 如何处理控制器方法中抛出的异常,允许你添加额外的异常处理逻辑。
    default void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {}
}

2. 配置类的配置属性:WebMvcProperties.class

  • 主要用于配置 Spring MVC 的相关行为,例如路径匹配、视图解析、静态资源处理等
@ConfigurationProperties(
  prefix = "spring.mvc"
)
public class WebMvcProperties {}

3. 配置类的配置属性:WebProperties.class

  • 通常用于配置一些通用的 Web 层设置,如资源处理、安全性配置等。
@ConfigurationProperties("spring.web")
public class WebProperties {

4. 静态资源配置:addResourceHandlers

  • addResourceHandlers方法:使用了方法重载,有3个addResourceHandlers方法

第一个addResourceHandlers方法:静态资源的目录

public void addResourceHandlers(ResourceHandlerRegistry registry) {}

此方法指定了静态资源路径:

  1. 当请求路径是/webjars/**,则会去classpath:/META-INF/resources/webjars/找。
  2. 当请求路径是/,则会去classpath:/META-INF/resources/;classpath:/resources/;classpath:/static/;classpath:/public/ 找。

第三个addResourceHandlers方法:静态资源的缓存配置

private void addResourceHandler(ResourceHandlerRegistry registry, String pattern, Consumer<ResourceHandlerRegistration> customizer) { 
	// 设置缓存的过期时间(如果没有指定单位,默认单位是秒)
    registration.setCachePeriod(this.getSeconds(this.resourceProperties.getCache().getPeriod()));
    //设置静态资源的 Cache-Control HTTP 响应头,告诉浏览器如何去缓存这些资源。
    registration.setCacheControl(this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl());
    // 设置静态资源在响应时,是否在响应头中添加资源的最后一次修改时间。
    registration.setUseLastModified(this.resourceProperties.getCache().isUseLastModified());
    this.customizeResourceHandlerRegistration(registration);
  }
}

1. 配置文件修改静态资源

  • 静态资源缓存设置

// 1. 缓存有效期
spring.web.resources.cache.period=100
// 2. 缓存控制(cachecontrol配置的话,period会失效)
spring.web.resources.cache.cachecontrol.max-age=20
// 3. 是否使用缓存的最后修改时间(默认是:使用)
spring.web.resources.cache.use-last-modified=true
// 4. 是否开启静态资源默认处理方式(默认是:开启)
spring.web.resources.add-mappings=true
  • 静态资源目录配置:
// 1. 设置webjars静态资源的请求路径的前缀
spring.mvc.webjars-path-pattern=/wjs/**
// 2. 设置普通静态资源的请求路径的前缀
spring.mvc.static-path-pattern=/static/**
// 3. 修改静态资源存放位置
// 但是 classpath:/META-INF/resources/ 静态资源不受此配置的影响
spring.web.resources.static-locations=classpath:/static1/,classpath:/static2/

2. 实现接口方式修改静态资源配置

@Configuration
public class WebConfig implements WebMvcConfigurer {
  @Override
  public void addResourceHandlers(ResourceHandlerRegistry registry){
    registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
  }
}

5. 拦截器配置

@Configuration
public class WebConfig implements WebMvcConfigurer {
  @Override
  public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(new HandlerInterceptor() {
      @Override
      public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("拦截器:preHandle");
        return true;
      }

      @Override
      public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("拦截器:postHandle");
      }

      @Override
      public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("拦截器:afterCompletion");
      }
    });
  }
}

4. EnableWebMvcConfiguration :启用配置

 @Configuration(
    proxyBeanMethods = false
  )
  @EnableConfigurationProperties({WebProperties.class})
  public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {}

1. 首页映射

    @Bean
    public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext, FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) {
      return (WelcomePageHandlerMapping)this.createWelcomePageHandlerMapping(applicationContext, mvcConversionService, mvcResourceUrlProvider, WelcomePageHandlerMapping::new);
    }
  • 会在4个静态资源目录中查找index.html文件作为欢迎页

5. 异常处理

  • 以下代码适合前后端分离的项目,只要给前端返回错误信息,如何展示由前端决定

1. 局部异常处理 @ExceptionHandler

  • 只针对当前controller的异常进行处理
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class UserController {

    @GetMapping("/resource/{id}")
    public String getResource(@PathVariable Long id){
        if(id == 1){
            throw new IllegalArgumentException("无效ID:" + id);
        }
        return "ID = " + id;
    }

    @ExceptionHandler(IllegalArgumentException.class)
    public String handler(IllegalArgumentException e){
        return "错误信息:" + e.getMessage();
    }
}

2. 全局异常处理:@ControllerAdvice + @ExceptionHandler

@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(IllegalArgumentException.class)
    @ResponseBody
    public String handler(IllegalArgumentException e){
        return "错误信息:" + e.getMessage();
    }
}

6. web服务器的配置类

  • web服务器的自动配置类:ServletWebServerFactoryAutoConfiguration
  • web服务器默认配置:ServerProperties.class
@EnableConfigurationProperties({ServerProperties.class})
public class ServletWebServerFactoryAutoConfiguration {}

1. tomcat服务器常见配置

# 这个参数决定了 Tomcat 在接收请求时,如果在指定的时间内没有收到完整的请求数据,将会关闭连接。这个超时时间是从客户端发送请求开始计算的。
# 防止长时间占用资源:如果客户端发送请求后,长时间没有发送完所有数据,Tomcat 会在这个超时时间内关闭连接,从而释放资源,避免资源被长时间占用。
server.tomcat.connection-timeout=20000

# 设置 Tomcat 服务器处理请求的最大线程数为 200。
# 如果超过这个数量的请求同时到达,Tomcat 会将多余的请求放入一个等待队列中。
# 如果等待队列也满了(由 server.tomcat.accept-count 配置),新的请求将被拒绝,通常会返回一个“503 Service Unavailable”错误。
server.tomcat.max-threads=200

# 用来设置等待队列的最大容量
server.tomcat.accept-count=100

# 设置 Tomcat 服务器在空闲时至少保持 10 个线程处于活动状态,以便快速响应新的请求。
server.tomcat.min-spare-threads=10

# 允许 Tomcat 服务器在关闭后立即重新绑定相同的地址和端口,即使该端口还在 TIME_WAIT 状态
# 当一个网络连接关闭时,操作系统会将该连接的端口保持在 TIME_WAIT 状态一段时间(通常是 2-4 分钟),以确保所有未完成的数据包都能被正确处理。在这段时间内,该端口不能被其他进程绑定。
server.tomcat.address-reuse-enabled=true

# 设置 Tomcat 服务器绑定到所有可用的网络接口,使其可以从任何网络地址访问。
server.tomcat.bind-address=0.0.0.0

# 设置 Tomcat 服务器使用 HTTP/1.1 协议处理请求。
server.tomcat.protocol=HTTP/1.1

# 设置 Tomcat 服务器的会话(session)超时时间为 30 分钟。具体来说,如果用户在 30 分钟内没有与应用进行任何交互,其会话将被自动注销。
server.tomcat.session-timeout=30

# 设置 Tomcat 服务器的静态资源缓存时间为 3600 秒(即 1 小时),这意味着浏览器会在 1 小时内缓存这些静态资源,减少重复请求。
server.tomcat.resource-cache-period=3600

# 解决get请求乱码。对请求行url进行编码。
server.tomcat.uri-encoding=UTF-8

# 设置 Tomcat 服务器的基础目录为当前工作目录(. 表示当前目录)。这个配置指定了 Tomcat 服务器的工作目录,包括日志文件、临时文件和其他运行时生成的文件的存放位置。 生产环境中可能需要重新配置。
server.tomcat.basedir=.

7. PageHelper

  • 引入依赖
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper-spring-boot-starter</artifactId>
    <version>2.1.0</version>
</dependency>
  • 编写代码
@RestController
public class VipController {
    @Autowired
    private VipService vipService;
    
    @GetMapping("/list/{pageNo}")
    // 注意方法的返回值
    public PageInfo<Vip> list(@PathVariable("pageNo") Integer pageNo) {
        // 1.设置当前页码和每页显示的记录条数
        PageHelper.startPage(pageNo, Constant.PAGE_SIZE);
        // 2.获取数据(PageHelper会自动给SQL语句添加limit)
        List<Vip> vips = vipService.findAll();
        // 3.将分页数据封装到PageInfo
        PageInfo<Vip> vipPageInfo = new PageInfo<>(vips);
        return vipPageInfo;
    }
}

8. R

package org.example.learn.enums;


import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;


@AllArgsConstructor
@NoArgsConstructor
public enum CodeEnum {

  OK (200, "成功"),
  FAIL(500, "失败"),
  NOT_FOUND(404, "未找到资源"),
  UNAUTHORIZED(401, "未授权"),
  FORBIDDEN(403, "禁止访问"),
  BAD_REQUEST(400, "请求错误"),
  INTERNAL_SERVER_ERROR(500, "服务器错误"),
  SERVICE_UNAVAILABLE(503, "服务不可用"),
  GATEWAY_TIMEOUT(504, "网关超时"),
  INTERNAL_ERROR(500, "内部服务器错误"),
  MODIFICATION_FAILED(400, "修改失败"),
  DELETE_FAILED(400, "删除失败"),
  ADD_FAILED(400, "添加失败");

  @Setter
  @Getter
  private int code;
  @Setter
  @Getter
  private String message;
  
}
package org.example.learn.result;


import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.example.learn.enums.CodeEnum;

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class R<T> {
  private Integer code;
  private String message;
  private T data;

  public static R OK() {
    return R.builder().code(CodeEnum.OK.getCode()).message(CodeEnum.OK.getMessage()).build();
  }

  public static <T> R<T> OK(T data) {
    return R.<T>builder().code(CodeEnum.OK.getCode()).message(CodeEnum.OK.getMessage()).data(data).build();
  }

  public static <T> R<T> FAIL() {
    return R.<T>builder().code(CodeEnum.FAIL.getCode()).message(CodeEnum.FAIL.getMessage()).build();
  }

  public static <T> R<T> FAIL(CodeEnum codeEnum){
    return R.<T>builder().code(codeEnum.getCode()).message(codeEnum.getMessage()).build();
  }

}
  @GetMapping(value = "/all/{pageNo}")
  public R<PageInfo<Vip>> getAll(@PathVariable int pageNo) {
    PageHelper.startPage(pageNo,3);
    List<Vip> all = vipService.getAll();
    PageInfo<Vip> vipPageinfo = new PageInfo<>(all);
    return R.OK(vipPageinfo);
  }

  @GetMapping(value = "/fail")
  public R<String> fail() {
    return R.FAIL(CodeEnum.INTERNAL_ERROR);
  }

9. logger

  • springboot集成logback日志框架,且日志级别默认为info
  • 使用lombok的@Slf4j注解简化日志输出

1. 使用

@Slf4j
@SpringBootApplication
public class Learn10LogApplication {

  public static void main(String[] args) {
    SpringApplication.run(Learn10LogApplication.class, args);
    log.trace("main------trace级别日志");
    log.debug("main------debug级别日志");
    log.info("main------info级别日志");
    log.warn("main------warn级别日志");
    log.error("main------error级别日志");
  }

}

2. 调整日志级别及颗粒度

# springboot默认的级别是info
# 设置全局的日志级别是error
logging.level.root=error

# 指定包名调整日志级别
logging.level.org.example.learn.controller=info
# 指定类名调整日志级别,不能是接口
logging.level.org.example.learn.service.impl.UserServiceImpl=trace
  • 指定对数据库操作的日志级别,需要对mapper接口的包名指定日志级别
logging.level.org.example.learn.mapper=DEBUG

3. 指定日志的输出路径和日志文件名

  • 指定日志的输出路径和日志的文件名不能同时使用,如果同时使用,则指定路径无效
# 指定日志的输出路径和日志的文件名不能同时使用,如果同时使用,则指定路径无效

# 日志输出到文件,指定文件路径为当前工程根目录下的log目录
logging.file.path=./log/

# 日志输出到文件,指定日志文件的文件名,路径是当前项目的根路径
logging.file.name=my.log

4. 滚动日志

# 日志文件达到多大时进行归档
logging.logback.rollingpolicy.max-file-size=100MB

# 所有的归档日志文件总共达到多大时进行删除,默认是0B表示不删除
logging.logback.rollingpolicy.total-size-cap=50GB

# 归档日志文件最多保留几天
logging.logback.rollingpolicy.max-history=60

# 启动项目时是否要清理归档日志文件
logging.logback.rollingpolicy.clean-history-on-start=false

# 归档日志文件名的格式
logging.logback.rollingpolicy.file-name-pattern=${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz

10. 事务

在service的类或者方法上使用注解

@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
  • rollbackFor = Exception.class 表示当抛出任何类型的 Exception 时,都会回滚事务。
  • propagation = Propagation.REQUIRED 表示如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这是默认的传播行为。

网站公告

今日签到

点亮在社区的每一天
去签到