这章讲的主要是springboot在web开发中的一些问题,不会有springboot连接数据库的操作,主要记录了Spring boot对静态资源的映射规则,SpringMVC自动配置,错误处理机制以及通过一个RestfulCrud项目来记录Spring boot使用国际化以及拦截器等功能

使用springboot开发流程

  • 创建SpringBoot应用。选中我们需要的模块
  • SpringBoot已经默认将这些场景配置好了。只需要在配置文件中指定少量配置就可以运行起来
  • 自己编写业务代码

SpringBoot对静态资源的映射规则

通过SpringBoot的源码可以发现有这样几个有趣的功能

SpringBoot的静态资源文件夹

先看一段源码,这段源码是从 WebMvcAutoConfiguration 类中取出的

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
   if (!this.resourceProperties.isAddMappings()) {
      logger.debug("Default resource handling disabled");
      return;
   }
   Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
   CacheControl cacheControl = this.resourceProperties.getCache()
         .getCachecontrol().toHttpCacheControl();
   if (!registry.hasMappingForPattern("/webjars/**")) {
      customizeResourceHandlerRegistration(registry
            .addResourceHandler("/webjars/**")
            .addResourceLocations("classpath:/META-INF/resources/webjars/")
            .setCachePeriod(getSeconds(cachePeriod))
            .setCacheControl(cacheControl));
   }
   String staticPathPattern = this.mvcProperties.getStaticPathPattern();
   if (!registry.hasMappingForPattern(staticPathPattern)) {
      customizeResourceHandlerRegistration(
            registry.addResourceHandler(staticPathPattern)
                  .addResourceLocations(getResourceLocations(
                        this.resourceProperties.getStaticLocations()))
                  .setCachePeriod(getSeconds(cachePeriod))
                  .setCacheControl(cacheControl));
   }
}

通过上面的源码可以获得以下两个信息

所有/webjars/**的请求,都去 classpath:/META-INF/resources/webjars/ 下找资源

什么意思呢?就是说所有的localhost:8080/webjars/**请求都被映射到了 localhost:8080/META-INF/resources/webjars/ 

通过这个功能,我们就可以映入一些框架的文件,例如jQuery的,或者Bootstrap,不过得先通过Maven引入这些框架文件,我们可以webjars的官网去找到这些文件的依赖信息

SpringBoot_web

只需要将上图Maven栏中的相关信息放到我们项目的pom.xml文件中即可

这是映入相关框架的jar包后的目录,以jquery为例:

SpringBoot_web

前面说过所有的/webjars/**请求都去classpath:/META-INF/resources/webjars/ 下找资源,所以该jquery.js的文件的访问路径就是/webjars/jquery/3.3.1-1/jquery.js在项目中就可以通过这个路径去引入jquery文件 

 /**访问当前项目的任何资源,如果没有会去下面几个路径下找资源

"classpath:/META-INF/resources/"
"classpath:/resources/"
"classpath:/static/"
"classpath:/public/"
"/"     # 当前项目的根路径(java目录跟resources都是根目录)

通过上面的源码我们还能发现这个功能,就是localhost:8080/会被映射到上面几个文件中

例如访问 localhost:8080/abc.js,就会去上面所说的几个位置寻找名为abc.js的文件

那么有一个问题,我们可不可以修改静态资源的位置呢?自然是可以的,只需要在application.properties中去修改即可

spring.resources.static-locations=classpath:/hello/, classpath:/jinxin

接收的是一个数组,多个文件通过逗号间隔

欢迎页

WebMvcAutoConfiguration类中还有下面一段代码

@Bean
public WelcomePageHandlerMapping welcomePageHandlerMapping(
      ApplicationContext applicationContext) {
   return new WelcomePageHandlerMapping(
         new TemplateAvailabilityProviders(applicationContext),
         applicationContext, getWelcomePage(),
         this.mvcProperties.getStaticPathPattern());
}

我们可以发现这样一个规则,即访问 localhost:8080/ 就能访问到静态资源文件下的index.html页面,这个页面被称为欢迎页

页面图标

WebMvcAutoConfiguration类中还有一段代码

@Configuration
@ConditionalOnProperty(value = "spring.mvc.favicon.enabled", matchIfMissing = true)
public static class FaviconConfiguration implements ResourceLoaderAware {

   private final ResourceProperties resourceProperties;

   private ResourceLoader resourceLoader;

   public FaviconConfiguration(ResourceProperties resourceProperties) {
      this.resourceProperties = resourceProperties;
   }

   @Override
   public void setResourceLoader(ResourceLoader resourceLoader) {
      this.resourceLoader = resourceLoader;
   }

   @Bean
   public SimpleUrlHandlerMapping faviconHandlerMapping() {
      SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
      mapping.setOrder(Ordered.HIGHEST_PRECEDENCE + 1);
      mapping.setUrlMap(Collections.singletonMap("**/favicon.ico",
            faviconRequestHandler()));
      return mapping;
   }

   @Bean
   public ResourceHttpRequestHandler faviconRequestHandler() {
      ResourceHttpRequestHandler requestHandler = new ResourceHttpRequestHandler();
      requestHandler.setLocations(resolveFaviconLocations());
      return requestHandler;
   }

   private List<Resource> resolveFaviconLocations() {
      String[] staticLocations = getResourceLocations(
            this.resourceProperties.getStaticLocations());
      List<Resource> locations = new ArrayList<>(staticLocations.length + 1);
      Arrays.stream(staticLocations).map(this.resourceLoader::getResource)
            .forEach(locations::add);
      locations.add(new ClassPathResource("/"));
      return Collections.unmodifiableList(locations);
   }

由上面的代码可知所有的 **/favicon.ico 都在静态资源文件下找,即不管是那个页面都可以在静态资源下找这个图标,那么这个图标是显示在哪儿的呢?在浏览器的标签栏上边会有一个图标,这个图标就是显示在哪儿的

SpringBoot_web

SpringMVC自动配置

我们可以去官方文档查看关于SpringMVC的自动配置

Spring Boot已经自动配置好了SpringMVC,以下是官方文档中显示的SpringBootSpringMVC的默认配置:(WebMvcAutoConfiguration

  • 包含了 ContentNegotiatingViewResolver 和 BeanNameViewResolver 组件
    • 自动配置了ViewResolver(视图解析器:根据方法的返回值得到视图对象(view,视图对象决定如何渲染(转发还是重定向到页面))
    • ContentNegotiatingViewResolver :组合所有视图解析器的
    • 如何定制:我们可以自己给容器中添加一个视图解析器,ContentNegotiatingViewResolver 会自动的将其组装进来
  • 支持静态资源文件夹路径和webjars
  • 自动注册了 Converter, GenericConverter, Formatter 组件.
    • Converter:转换器,例如:如果页面提交了一组数据,我们使用user对象接收(public String hello(User user);,而涉及到一些非字符串属性的时候,例如18,从前端传来的是字符串类型,这里样转换成数字类型就会用到Converter
    • Formatter :格式化器,如果前端传来2017-1-12的字符串,格式化器就能将它转换成Date类型的数据
      • 自己添加格式化转换器,我们只需要放在容器中即可(通过@Bean@Component
  • 支持 HttpMessageConverters 
    • HttpMessageConverters :消息转换器,SpringMVC用来转换Http请求和响应的
      • 自己给容器中添加HttpMessageConverters ,只需要将自己注册的组件添加到容器即可(通过@Bean@Component
  • 定义错误代码生成规则 MessageCodesResolver.
  • index.html作为静态首页访问
  • 支持添加图标favicon.ico
  • Automatic use of a  ConfigurableWebBindingInitializerbean.
    • 初始化WebDataBinder
    • 请求的数据  --->  JavaBean
    • 我们也可以配置一个ConfigurableWebBindingInitializer 来替换默认的,只需要放到容器中即可

org.springframework.boot.autoconfigure.web:web的所有自动场景;

If you want to keep Spring Boot MVC features, and you just want to add additional MVC configuration (interceptors, formatters, view controllers etc.) you can add your own @Configuration class of type WebMvcConfigurerAdapter, but without @EnableWebMvc. If you wish to provide custom instances of RequestMappingHandlerMapping, RequestMappingHandlerAdapter or ExceptionHandlerExceptionResolver you can declare a WebMvcRegistrationsAdapter instance providing such components.

If you want to take complete control of Spring MVC, you can add your own @Configuration annotated with @EnableWebMvc.

扩展SprngMVC

虽然上面说的一些功能已经让我们眼花缭乱了,但是可惜的是使用上面的那些功能开发仍旧不够,我们还需要自己扩展。。。

想要扩展SpringMVC的配置就必须要自己定义一个配置类,并且使用@Configuration注解标注,让它成为一个配置类,而且要实现 WebMvcConfigurer 接口,最关键的是一定不能将这个类标注上 @EnableWebMvc 注解

这样做的特点:既保留了所有的自动配置,也能用我们扩展的配置

@Configuration
public class MyMvcConfig implements WebMvcConfigurer {


    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        // 浏览器发送 /jinxin 请求来到 success页面
        registry.addViewController("/jinxin").setViewName("success");
    }
}

全面接管SpringMVC

我们有时候不想使用SpringBoot默认配置的SpringMVC了,而是所有的配置都由我们自己写,那么如何让SpringBoot的默认配置失效呢?只需要在我们自己配置的类标注上@EnableWebMvc即可

@EnableWebMvc
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {


    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        // 浏览器发送 /jinxin 请求来到 success页面
        registry.addViewController("/jinxin").setViewName("success");
    }
}

原理:为什么@EnableWebMvc使自动配置失效了?

1、@EnableWebMvc的核心

@Import({DelegatingWebMvcConfiguration.class})
public @interface EnableWebMvc {
}

2、根据上一步@EnableWebMvc导入了DelegatingWebMvcConfiguration类,而这个类继承了WebMvcConfigurationSupport 类,所以这个类是WebMvcConfigurationSupport 类型的

@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {

3、而在WebMvcAutoConfiguration类中有一个注解很重要@ConditionalOnMissingBean,它表示容器中没有这个组件的时候,这个自动配置类才生效

@Configuration
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
// 表示容器中没有这个组件的时候,这个自动配置类才生效
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class,
      ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {

4、在上一步中@ConditionalOnMissingBean需要判断容器中没有某个组件才会让WebMvcAutoConfiguration自动配置类生效,而它检测的正是WebMvcConfigurationSupport 类,然而我们的@EnableWebMvc注解早已经把WebMvcConfigurationSupport类型的DelegatingWebMvcConfiguration类导入了容器中,所以这个注解不会判断通过,因此自动配置类不会加载,MVC自动配置全部失效

如何修改SpringBoot的默认配置

  • SpringBoot在自动配置很多组件的时候,先看容器中有没有用户自己配置的(@Bean、@Component)如果有就用用户配置的,如果没有,才自动配置;如果有些组件可以有多个(ViewResolver)将用户配置的和自己默认的组合起来;
  • 在SpringBoot中会有非常多的xxxConfigurer帮助我们进行扩展配置
  • 在SpringBoot中会有很多的xxxCustomizer帮助我们进行定制配置

RestfulCrud项目

默认访问首页

在上面提到过SpringBoot中有这样一个功能,就是访问localhost:8080/会默认访问到index.html页面,但是前提是这个index.html文件必须在静态资源文件夹中才行,否则访问不到,但是不巧的是在使用thymeleaf模板的时候,必须将页面放到templates目录下,也就是说必须要将index.html页面放到templates目录中,那么再使用localhost:8080就访问不到了,这时的解决办法就是将localhost:8080/这个访问路径转到templates目录下的某个页面,这里以login.html为例进行绑定

//@EnableWebMvc
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
    // 所有的WebMvcConfigurerAdapter组件都会一起起作用
    @Bean   // 将组件添加到容器中
    public WebMvcConfigurer webMvcConfigurer(){
        WebMvcConfigurer webMvcConfigurer = new WebMvcConfigurer() {
            @Override
            public void addViewControllers(ViewControllerRegistry registry) {
                registry.addViewController("/").setViewName("login");
                registry.addViewController("/index.html").setViewName("login");
            }
        };
        return webMvcConfigurer;
    }
}

上面将setViewName设置为了login,这里会使用到thymeleaf的字符串拼接,其实完整的路径是 /templates/login.html 

国际化

如果由SpringMVC来做国际化:

  • 编写国际化配置文件
  • 使用ResourceBundleMessageSource管理国际化资源文件
  • 在页面使用fmt:message取出国际化内容

现在SpringBoot来做:

1、编写国际化配置文件,抽取页面需要显示的国际化信息

SpringBoot_web

SpringBoot_web

2、将基础文件(去掉国家名的国际化资源文件)指定为我们自己所写的文件(去application.properties文件)

SpringBoot_web

3、去页面获取国际化的值

thymeleaf中有一个表达式 #{} 就是专门去获取国际化信息的

th:text="#{login.tip}"

这样做的效果:根据浏览器语言设置的信息切换了国际化

SpringBoot_web

原理:

国际化Locale(区域信息对象):通过LocaleResolver获取区域信息对象,根据请求头带来的区域信息获取Locale进行国际化 

SpringBoot_web

@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(prefix = "spring.mvc", name = "locale")
public LocaleResolver localeResolver() {
   if (this.mvcProperties
         .getLocaleResolver() == WebMvcProperties.LocaleResolver.FIXED) {
      return new FixedLocaleResolver(this.mvcProperties.getLocale());
   }
   AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver();
   localeResolver.setDefaultLocale(this.mvcProperties.getLocale());
   return localeResolver;
}
View Code

相关文章:

猜你喜欢
  • 2021-12-23
  • 2021-11-15
  • 2022-12-23
相关资源
相似解决方案