SpringBoot 第四章WEB(1)

资料

四、Web开发

简介
使用SpringBoot;

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

自动配置原理?
这个场景SpringBoot帮我们配置了什么?能不能修改?能修改哪些配置?能不能扩展?xxx

  • 相对应的代码

    xxxxAutoConfiguration:帮我们给容器中自动配置组件;
    xxxxProperties:配置类来封装配置文件的内容;

配置一些静态资源
需要了解一些静态资源的在springboot中的映射情况

  • WebMvcAutoConfiguration 的文件

      WebMvcAuotConfiguration:
          @Override
          public void addResourceHandlers(ResourceHandlerRegistry registry) {
              if (!this.resourceProperties.isAddMappings()) {
                  logger.debug("Default resource handling disabled");
                  return;
              }
              Integer cachePeriod = this.resourceProperties.getCachePeriod();
              if (!registry.hasMappingForPattern("/webjars/**")) {
                  customizeResourceHandlerRegistration(
                          registry.addResourceHandler("/webjars/**")
                                  .addResourceLocations(
                                          "classpath:/META-INF/resources/webjars/")
                          .setCachePeriod(cachePeriod));
              }
              String staticPathPattern = this.mvcProperties.getStaticPathPattern();
                //静态资源文件夹映射
              if (!registry.hasMappingForPattern(staticPathPattern)) {
                  customizeResourceHandlerRegistration(
                          registry.addResourceHandler(staticPathPattern)
                                  .addResourceLocations(
                                          this.resourceProperties.getStaticLocations())
                          .setCachePeriod(cachePeriod));
              }
          }
    
          //配置欢迎页映射
          @Bean
          public WelcomePageHandlerMapping welcomePageHandlerMapping(
                  ResourceProperties resourceProperties) {
              return new WelcomePageHandlerMapping(resourceProperties.getWelcomePage(),
                      this.mvcProperties.getStaticPathPattern());
          }
    
         //配置喜欢的图标
          @Configuration
          @ConditionalOnProperty(value = "spring.mvc.favicon.enabled", matchIfMissing = true)
          public static class FaviconConfiguration {
    
              private final ResourceProperties resourceProperties;
    
              public FaviconConfiguration(ResourceProperties resourceProperties) {
                  this.resourceProperties = resourceProperties;
              }
    
              @Bean
              public SimpleUrlHandlerMapping faviconHandlerMapping() {
                  SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
                  mapping.setOrder(Ordered.HIGHEST_PRECEDENCE + 1);
                    //所有  **/favicon.ico 
                  mapping.setUrlMap(Collections.singletonMap("**/favicon.ico",
                          faviconRequestHandler()));
                  return mapping;
              }
    
              @Bean
              public ResourceHttpRequestHandler faviconRequestHandler() {
                  ResourceHttpRequestHandler requestHandler = new ResourceHttpRequestHandler();
                  requestHandler
                          .setLocations(this.resourceProperties.getFaviconLocations());
                  return requestHandler;
              }
    
          }

    1)、所有 /webjars/** ,都去 classpath:/META-INF/resources/webjars/ 找资源;对应的类的路径下找
    webjars:以jar包的方式引入静态资源;
    http://www.webjars.org/

    图片说明

          <dependency>
              <groupId>org.webjars</groupId>
              <artifactId>jquery</artifactId>
              <version>3.4.1</version>
          </dependency>

    http://localhost:8080/webjars/jquery/3.4.1/jquery.js 请求成功访问到js文件

    2)、"/**" 访问当前项目的任何资源,都去(静态资源的文件夹)找映射

    @ConfigurationProperties(prefix = "spring.resources", ignoreUnknownFields = false)
    public class ResourceProperties {
    
      private static final String[] CLASSPATH_RESOURCE_LOCATIONS = { "classpath:/META-INF/resources/",
              "classpath:/resources/", "classpath:/static/", "classpath:/public/" };
    
    "classpath:/META-INF/resources/", 
    "classpath:/resources/",  需要新创建
    "classpath:/static/",     
    "classpath:/public/"      需要新创建
    "/":当前项目的根路径

    图片说明

    例如其中的index.html页面

    图片说明

    private String staticPathPattern = "/**";

    3)、欢迎页; 静态资源文件夹下的所有index.html页面;被"/**"映射;
    ​ localhost:8080/ 找index页面
    4)、所有的 **/favicon.ico 都是在静态资源文件下找;

    5)、自定义路径下查找 其他默认的情况不会生效

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

3、模板引擎

  • JSP、Velocity、Freemarker、Thymeleaf

  • SpringBoot推荐的Thymeleaf:语法更简单,功能更强大;

    图片说明

    系统的是3.0.11 layout暂未知
          <dependency>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter-thymeleaf</artifactId>
          </dependency>
  • 找到thymeleafProperties 查看相关配置

    @ConfigurationProperties(prefix = "spring.thymeleaf")
    public class ThymeleafProperties {
    
      private static final Charset DEFAULT_ENCODING = StandardCharsets.UTF_8;
    
      public static final String DEFAULT_PREFIX = "classpath:/templates/";
    
      public static final String DEFAULT_SUFFIX = ".html";

    图片说明

  • 只要我们把HTML页面放在classpath:/templates/,thymeleaf就能自动渲染;

    @Controller
    public class TestController {
      @RequestMapping("/hello")  //注意这里不能加responsebody 不然就是输出一串字符串
      public String hello(){
          return "hello";
      }
    }

    自动将hello 拼接为/template/hello.html 不支持jsp

    图片说明

4、语法

  • 参考之前jsp的c: 做一个对比
    图片说明

  • 导入thymeleaf的名称空间

    <html lang="en" xmlns:th="http://www.thymeleaf.org">
  • 效果

    @Controller
    public class TestController {
      @RequestMapping("/hello")
      public String hello(Map<String,String>map){
          map.put("hello","hello,thymeleaf");
          return "hello";
      }
    }
    
    《!DOCTYPE html>
    《html lang="en" xmlns:th="http://www.thymeleaf.org">
    《head>
      《meta charset="UTF-8">
      《title>Title《/title>
    《/head>
    《body>
      《h1 th:text="${hello}">欢迎来到该页面《/h1>
    《/body>
    《/html>

    图片说明

  • 表达式 参考文献:https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html

    Simple expressions:(表达式语法)
      Variable Expressions: ${...}:获取变量值;OGNL;
              1)、获取对象的属性、调用方法
              2)、使用内置的基本对象:
                  #ctx : the context object.
                  #vars: the context variables.
                  #locale : the context locale.
                  #request : (only in Web Contexts) the HttpServletRequest object.
                  #response : (only in Web Contexts) the HttpServletResponse object.
                  #session : (only in Web Contexts) the HttpSession object.
                  #servletContext : (only in Web Contexts) the ServletContext object.
    
                  ${session.foo}
              3)、内置的一些工具对象:
                  #execInfo : information about the template being processed.
                  #messages : methods for obtaining externalized messages inside variables expressions, in the same way as they would be obtained using #{…} syntax.
                  #uris : methods for escaping parts of URLs/URIs
                  #conversions : methods for executing the configured conversion service (if any).
                  #dates : methods for java.util.Date objects: formatting, component extraction, etc.
                  #calendars : analogous to #dates , but for java.util.Calendar objects.
                  #numbers : methods for formatting numeric objects.
                  #strings : methods for String objects: contains, startsWith, prepending/appending, etc.
                  #objects : methods for objects in general.
                  #bools : methods for boolean evaluation.
                  #arrays : methods for arrays.
                  #lists : methods for lists.
                  #sets : methods for sets.
                  #maps : methods for maps.
                  #aggregates : methods for creating aggregates on arrays or collections.
                  #ids : methods for dealing with id attributes that might be repeated (for example, as a result of an iteration).
    
      Selection Variable Expressions: *{...}:选择表达式:和${}在功能上是一样;
          补充:配合 th:object="${session.user}:
          <div th:object="${session.user}">
              <p>Name: <span th:text="*{firstName}">Sebastian</span>.</p>
              <p>Surname: <span th:text="*{lastName}">Pepper</span>.</p>
              <p>Nationality: <span th:text="*{nationality}">Saturn</span>.</p>
          </div>
          相当于
          <div><p>${session.user,firstName}</p></div>
    
      Message Expressions: #{...}:获取国际化内容
      Link URL Expressions: @{...}:定义URL; <th:href="@{...(orderId=${o.id})}"> 自动传入多个值
              @{/order/process(execId=${execId},execType='FAST')}
      Fragment Expressions: ~{...}:片段引用表达式
              <div th:insert="~{commons :: main}">...</div>
    
    Literals(字面量)
        Text literals: 'one text' , 'Another one!' ,…
        Number literals: 0 , 34 , 3.0 , 12.3 ,…
        Boolean literals: true , false
        Null literal: null
        Literal tokens: one , sometext , main ,…
    Text operations:(文本操作)
      String concatenation: +
      Literal substitutions: |The name is ${name}|
    Arithmetic operations:(数***算)
      Binary operators: + , - , * , / , %
      Minus sign (unary operator): -
    Boolean operations:(布尔运算)
      Binary operators: and , or
      Boolean negation (unary operator): ! , not
    Comparisons and equality:(比较运算)
      Comparators: > , < , >= , <= ( gt , lt , ge , le )
      Equality operators: == , != ( eq , ne )
    Conditional operators:条件运算(三元运算符)
      If-then: (if) ? (then)
      If-then-else: (if) ? (then) : (else)
      Default: (value) ?: (defaultvalue)
    Special tokens:
      No-Operation: _ 
  • 用例

    《!DOCTYPE html>
    《html lang="en" xmlns:th="http://www.thymeleaf.org">
    《head>
      《meta charset="UTF-8">
      《title>Title《/title>
    《/head>
    《body>
      《div th:text="${hello}">《/div>
      《hr>
      《div th:text="html">《/div>
      《div th:utext="html">《/div>
      《hr>
      《h4 th:each="user:${users}" th:text="${user}">《/h4>
      《h4>
          《span th:each="user:${users}">[[${user}]]《/span>
      《/h4>
    《/body>
    《/html>

    图片说明

自动配置

Spring MVC auto-configuration

  • Spring Boot 自动配置好了SpringMVC

  • Inclusion of ContentNegotiatingViewResolver and BeanNameViewResolver beans.

    • 自动配置了ViewResolver(视图解析器:根据方法的返回值得到视图对象(View),视图对象决定如何渲染(转发?还是 重定向?))
    • ContentNegotiatingViewResolver:组合所有的视图解析器的;
    WebMvcAutoConfiguration类中的方法  Mvc自动配置区域
       || 
       \/ 里面的方法
          @Bean
          @ConditionalOnBean(ViewResolver.class)
          @ConditionalOnMissingBean(name = "viewResolver", value = ContentNegotiatingViewResolver.class)
          public ContentNegotiatingViewResolver viewResolver(BeanFactory beanFactory) {
              ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
              resolver.setContentNegotiationManager(beanFactory.getBean(ContentNegotiationManager.class));
              // ContentNegotiatingViewResolver uses all the other view resolvers to locate
              // a view so it should have a high precedence
              resolver.setOrder(Ordered.HIGHEST_PRECEDENCE);
              return resolver;
          }
    ||
    \/
    ContentNegotiatingViewResolver 来解析视图 如何解析视图
    ||
    \/   
    class ContentNegotiatingViewResolver {
          @Nullable
          public View resolveViewName(String viewName, Locale locale) throws Exception { //解析视图
      ...
    //获取候选的视图  
              List<View> candidateViews = this.getCandidateViews(viewName, locale, requestedMediaTypes);
    //获取最优的视图
              View bestView = this.getBestView(candidateViews, requestedMediaTypes, attrs);
    }
    ||
    \/  把所有的解析器拿来 一个一个解析
    private List<View> getCandidateViews(String viewName, Locale locale, List<MediaType> requestedMediaTypes) throws Exception {
          List<View> candidateViews = new ArrayList();
          if (this.viewResolvers != null) {
              Assert.state(this.contentNegotiationManager != null, "No ContentNegotiationManager set");
              Iterator var5 = this.viewResolvers.iterator();//获取所有的解析器 挨个解析
    ...
    }
    ||
    \/ initServletContext从容器中获取所有的视图解析器 ,以下是解析器的获取
      protected void initServletContext(ServletContext servletContext) {
          Collection<ViewResolver> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(this.obtainApplicationContext(), ViewResolver.class).values();
          ViewResolver viewResolver;
          if (this.viewResolvers == null) {
              this.viewResolvers = new ArrayList(matchingBeans.size());
              Iterator var3 = matchingBeans.iterator();
          }
          ...
      }
    • 如何定制:我们可以自己给容器中添加一个视图解析器;自动的将其组合进来;
    @Bean //在配置类里面写
      public ViewResolver getViewResolver(){
          return new MyViewResolver();
      }
    class MyViewResolver implements ViewResolver{
    
      @Override
      public View resolveViewName(String s, Locale locale) throws Exception {
          System.out.println("MyviewResolver");
          return null;
      }
    }
    ||
    \/
    所有的请求会先到doDispatch  看看里面的视图解析器是什么
      protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    ...
    }

    DispatcherServlet中包含的组件 含视图解析器

    图片说明

  • Support for serving static resources, including support for WebJars (see below).静态资源文件夹路径,webjars

  • Static index.html support. 静态首页访问

  • Custom Favicon support (see below). favicon.ico

  • 自动注册了 of Converter, GenericConverter, Formatter beans.

    • Converter:转换器; public String hello(User user):类型转换使用Converter
    • Formatter 格式化器; 2017.12.***te;
          @Bean
          @ConditionalOnProperty(prefix = "spring.mvc", name = "date-format")//在文件中配置日期格式化的规则
          public Formatter<Date> dateFormatter() {
              return new DateFormatter(this.mvcProperties.getDateFormat());//日期格式化组件
          }

自己添加的格式化器转换器,我们只需要放在容器中即可

  • Support for HttpMessageConverters (see below).

    • HttpMessageConverter:SpringMVC用来转换Http请求和响应的;User---Json;

    • HttpMessageConverters 是从容器中确定;获取所有的HttpMessageConverter;
      自己给容器中添加HttpMessageConverter,只需要将自己的组件注册容器中(@Bean,@Component)

  • Automatic registration of MessageCodesResolver (see below).定义错误代码生成规则

  • Automatic use of a ConfigurableWebBindingInitializer bean (see below).

    我们可以配置一个ConfigurableWebBindingInitializer来替换默认的;(添加到容器)

          @Override
          protected ConfigurableWebBindingInitializer getConfigurableWebBindingInitializer(
                  FormattingConversionService mvcConversionService, Validator mvcValidator) {
              try {
    //从容器中拿
                  return this.beanFactory.getBean(ConfigurableWebBindingInitializer.class);
              }
              catch (NoSuchBeanDefinitionException ex) {
                  return super.getConfigurableWebBindingInitializer(mvcConversionService, mvcValidator);
              }
          }

    初始化WebDataBinder;绑定器
    请求数据=====JavaBean;

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

扩展SpringMVC

通过拦截器 来拦截hello请求

  • 代码
      <mvc:view-controller path="/hello" view-name="success"/>
      <mvc:interceptors>
          <mvc:interceptor>
              <mvc:mapping path="/hello"/>
              <bean></bean>
          </mvc:interceptor>
      </mvc:interceptors>
    现在没有了配置文件 springboot如何做---> 编写一个@Configuration 接口是WebMvcConfigurer
    //使用WebMvcConfigurer可以来扩展SpringMVC的功能
    @Configuration
    public class MyMvcConfig implements WebMvcConfigurer {
      @Override
      public void addViewControllers(ViewControllerRegistry registry) {
          //浏览器发送 /hello请求来到 success
          registry.addViewController("/hello").setViewName("success");
      }
    }
    图片说明

扩展mvc成功的原理

  • 配置mvc需要经过WebMvcAutoConfiguration 来进行自动配置 ,是springmvc 的自动配置类

  • 导入EnableWebMvcConfiguration,在做其他自动配置时会导入

      @Configuration(proxyBeanMethods = false) //是一个配置类
      @Import(EnableWebMvcConfiguration.class)//导入EnableWebMvcConfiguration
      @EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class })
      @Order
      public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {
    }
    ||
    \/
      @Configuration(proxyBeanMethods = false)//配置类
      public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {
    实现RequestMappingHandlerAdapter、RequestMappingHandlerMapping等方法
    }
    ||
    \/ 获取所有的configurer
    @Configuration(proxyBeanMethods = false)
    public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
    @Autowired( required = false)  //自动装配 方法参数从容器中获取configurers
      public void setConfigurers(List<configurer> configurers) {
          if (!CollectionUtils.isEmpty(configurers)) {
              this.configurers.addWebMvcConfigurers(configurers);
          }
      }
    }
    ||
    \/  其中一个实现案例 就是所有的configurer都加上addViewControllers
      public void addViewControllers(ViewControllerRegistry registry) {
          Iterator var2 = this.delegates.iterator();
    
          while(var2.hasNext()) {
              WebMvcConfigurer delegate = (WebMvcConfigurer)var2.next();
              delegate.addViewControllers(registry);
          }
      }
    容器所有的WebMvcConfigure都会一起起作用 implements WebMvcConfigurer--> @Configuration包括自己写
  • 效果:springmvc 的自动配置和我们的扩展配置都会起作用

全面接管springmvc

  • @EnableWebMvc:springboot对springmvc的自动配置不需要了,所有都是我们自己配的 静态资源失效 所有的SpringMVC的自动配置都失效
    仅仅可以使用基本的设置,即DelegatingWebMvcConfiguration里设置的方法
    即如果容器中存在WebMvcConfigurationSupport类,则springboot关于springmvc的自动配置类不会生效。
    图片说明
    没有注解的效果
    图片说明

  • 为什么会失效

    @ConditionalOnMissingBean(WebMvcConfigurationSupport.class)  //<---//容器中没有这个组件的时候,这个自动配置类才生效ConditionalOnMissingBean
    @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
    @AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
          ValidationAutoConfiguration.class })
    public class WebMvcAutoConfiguration {}
    
    @Import({DelegatingWebMvcConfiguration.class})
    public @interface EnableWebMvc {
    }
    DelegatingWebMvcConfiguration==包括=>  private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();是一个WebMvcConfigurer 实现回调

如何修改springboot的默认配置

模式

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

实验部分

  • 修改web的首页 默认是去public找 我们需要将其改为template下找
    第一种
    @Controller
    public class ControllerTest {
      @RequestMapping({"/", "/index.html"})
      public String index() {
          return "index";
      }
    }
    第二种
    @Configuration
    public class MyMvcConfig implements WebMvcConfigurer {
      @Override
      public void addViewControllers(ViewControllerRegistry registry) {
          registry.addViewController("/index.html").setViewName("index");
          registry.addViewController("/").setViewName("index");
      }
    }
    第三种
    @Configuration
    public class MyMvcConfig implements WebMvcConfigurer {
      @Bean
      public WebMvcConfigurer webMvcConfigurer(){
          WebMvcConfigurer webMvcConfigurer = new WebMvcConfigurer() {
              @Override
              public void addViewControllers(ViewControllerRegistry registry) {
                  registry.addViewController("/index.html").setViewName("index");
                  registry.addViewController("/").setViewName("index");
              }
          };
          return webMvcConfigurer;
      }
    }
  • 通过th:src="@{/css..}" 会自动补上配置文件中的项目名 普通的src没法达到这一的效果
    初始效果

    《!DOCTYPE html>
    《html lang="en" xmlns:th="http://www.thymeleaf.org">
    《head>
      《meta charset="UTF-8">
      《title>Title《/title>
    《/head>
    《link th:href="@{/css/myCss1.css}" type="text/css" rel="stylesheet">
    《link href="css/myCss2.css" rel="stylesheet" type="text/css">
    
    《body>
    《p class="center1">hello,springboot th:src《/p>
    《p class="center2">hello,springboot not -> th:src《/p>
    《img th:src="@{/img/goIang.png}" th:height="50px"/>
    《img src="img/goIang.png" height="50px"/>
    《/body>
    《/html>
    myCss1.css
    .center1 {
      text-align: center;
      color: red;
      font-size: 10px;
    }
    myCss2.css
    .center2 {
      text-align: center;
      color: red;
      font-size: 10px;
    }

    图片说明

    图片说明
    加一个项目名

    server.servlet.context-path=/cznczai

    即使加了路径 同样也可以访问
    th:src 功能更加强大
    图片说明

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

    导入目标页面【对应资源不要加设置的目录名如static】
    图片说明
    编写国际化配置文件i18n/xxxx.properties 【zh_CN】 【en_US】
    idea会自动封装
    图片说明

    图片说明

    导入国际化配置路径

    在application.properties中配置
    # 注册国际化配置文件
    spring.messages.basename=i18n.login

    效果
    图片说明

    源码分析

    public class MessageSourceAutoConfiguration {
      private static final Resource[] NO_RESOURCES = {};
      @Bean
      @ConfigurationProperties(prefix = "spring.messages")
      public MessageSourceProperties messageSourceProperties() {
          return new MessageSourceProperties();
      }
      protected static class ResourceBundleCondition extends SpringBootCondition {
    
          private static ConcurrentReferenceHashMap<String, ConditionOutcome> cache = new ConcurrentReferenceHashMap<>();
    
          @Override //在环境中找到getProperty("spring.messages.basename", "messages");
          public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
              String basename = context.getEnvironment().getProperty("spring.messages.basename", "messages");
              ConditionOutcome outcome = cache.get(basename);
              if (outcome == null) {
                  outcome = getMatchOutcomeForBasename(context, basename);
                  cache.put(basename, outcome);
              }
              return outcome;
          }
      } 
    }

    国际化Locale(区域信息对象);LocaleResolver(获取区域信息对象) 我们需要重新配置一个LocaleResolver

          @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;  //返回国际化类型 
          }
    ||
    \/
    public class AcceptHeaderLocaleResolver implements LocaleResolver {
      public Locale resolveLocale(HttpServletRequest request) {
          Locale defaultLocale = this.getDefaultLocale();
          if (defaultLocale != null && request.getHeader("Accept-Language") == null) {
              return defaultLocale;
          } else {
              Locale requestLocale = request.getLocale(); //从请求头 获得国际化信息
              List<Locale> supportedLocales = this.getSupportedLocales();
              if (!supportedLocales.isEmpty() && !supportedLocales.contains(requestLocale)) {
                  Locale supportedLocale = this.findSupportedLocale(request, supportedLocales);
                  if (supportedLocale != null) {
                      return supportedLocale;
                  } else {
                      return defaultLocale != null ? defaultLocale : requestLocale;
                  }
              } else {
                  return requestLocale;
              }
          }
      }
    }

    自定义国际化

    public class MyLocaleResolver implements LocaleResolver {
      @Override
      public Locale resolveLocale(HttpServletRequest request) {
          String encoding = request.getParameter("encoding");
          Locale locale = null;
          if(StringUtils.isEmpty(encoding)){
              locale = Locale.getDefault();
          }else {
              String[] split = encoding.split("_");
               locale = new Locale(split[0], split[1]);
          }
          return locale;
      }
    
      //不写
      @Override
      public void setLocale(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) {
      }
    }
    @Configuration
    public class MyMvcConfig implements WebMvcConfigurer {
      @Bean
      public LocaleResolver localeResolver(){
          return new MyLocaleResolver();
      }
    }

    效果
    图片说明
    图片说明

    《!DOCTYPE html>
    《html lang="en" xmlns:th="http://www.thymeleaf.org">
    《head>
    《meta charset="UTF-8">
    《title>login《/title>
    《!-- Bootstrap core CSS -->
    《link th:href="@{/webjars/bootstrap/4.4.1/css/bootstrap.css}" rel="stylesheet" type="text/css">
    《!-- Custom styles for this template -->
    《link th:href="@{/css/signin.css}" rel="stylesheet" type="text/css">
    《/head>
    《body class="text-center">
    《form class="form-signin">
    《img class="mb-4" th:src="@{/img/bootstrap-solid.svg}" alt="no img" width="72" height="72">
    《h1 class="h3 mb-3 font-weight-normal" th:text="#{login.tip}">Please sign in《/h1>
    《label class="sr-only" th:text="#{login.username}">Username《/label>
    《input type="text" name="username"  class="form-control" th:placeholder="#{login.username}" required="" autofocus="">
    《label for="inputPassword" class="sr-only" th:text="#{login.password}">Password《/label>
    《input type="password" name="password id="inputPassword" class="form-control" th:placeholder="#{login.password}" required>
    《div class="checkbox mb-3">
      《label>
        《input type="checkbox" value="remember-me">[[#{login.remember}]]
      《/label>
    《/div>
    《button class="btn btn-lg btn-primary btn-block" type="submit" th:text="#{login.sign}">Sign in《/button>
    《p class="mt-5 mb-3 text-muted">© 2019-2020《/p>
    《a class="btn btn-sm" th:href="@{/login(encoding='zh_CN')}">中文《/a>
    《a class="btn btn-sm" th:href="@{/login(encoding='en_US')}">English《/a>
    《/form>
    《/body>
    《/html>
  • 登陆

    进入页面后普通的连接什么样式都没有【因为是转发 因为相对路径发生改变 需要采用重定向】
    图片说明

    @Controller
    public class InfoController {
      @RequestMapping("/user/login")
      public String login(@RequestParam("username") String username, @RequestParam("password") String password, Map<String, Object> map, HttpSession session) {
          System.out.println(username);
          System.out.println(password);
          session.setAttribute("loginUser","cznczai");//需要将登陆成功的用户放在session 方便后面检验
          if (!StringUtils.isEmpty(username) && "123456".equals(password)) {
              return "redirect:/success";
          } else {
              map.put("msg","用户名或密码错误");
              return "login";
          }
      }
    }
    转发 变成了转发路径下的css文件见 
    《link href="css/myCss2.css" rel="stylesheet" type="text/css">

    重定向后
    图片说明

    设置错误信息提示

    Map ---> request中
    《p style="color:red" th:text="${msg}" th:if="${not #strings.isEmpty(msg)}">《/p> 《!--如果msg为空 不显示-->

    图片说明

    登陆拦截器

    @Configuration
    public class MyMvcConfig implements WebMvcConfigurer {
      //注册拦截器
      @Override
      public void addInterceptors(InterceptorRegistry registry) {
          //静态资源也不用拦截 springboot已经配置好了
          registry.addInterceptor(new MyLoginInterceptor()).addPathPatterns("/**")
                  .excludePathPatterns("/login","/user/login");//登陆请求不能拦截
      }
    }
    
    //登陆检查
    public class MyLoginInterceptor  implements HandlerInterceptor {
      @Override
      public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
          Object loginUser = request.getSession().getAttribute("loginUser");
          System.out.println("权限验证");
          if(loginUser!=null){
              return true;
          }else{
              request.setAttribute("msg","没有权限,请登录");
              request.getRequestDispatcher("/login").forward(request,response);
              return false;
          }
      }
    }

    图片说明

  • restful CRUD-员工列表
    RestfulCRUD:CRUD满足Rest风格;

    URI: /资源名称/资源标识 HTTP请求方式区分对资源CRUD操作

    普通CRUD(uri来区分操作) RestfulCRUD
    查询 getEmp emp---GET
    添加 addEmp?xxx emp---POST
    修改 updateEmp?id=xxx&xxx=xx emp/{id}---PUT
    删除 deleteEmp?id=1 emp/{id}---DELETE

    实验的请求架构;

    实验功能 请求URI 请求方式
    查询所有员工 emps GET
    查询某个员工(来到修改页面) emp/1 GET
    来到添加页面 emp GET
    添加员工 emp POST
    来到修改页面(查出员工进行信息回显) emp/1 GET
    修改员工 emp PUT
    删除员工 emp/1 DELETE
  • 回显值:th:value="" 放入book对象 进行回显 利用thymeleaf 来将add update页面结合在一起

  • 添加修改的put post 修改

    1. SpringMVC中配置HiddenHttpMethodFilter;(SpringBoot自动配置好的)
    2. 页面创建一个post表单
    3. 创建一个input项,name="_method";值就是我们指定的请求方式

    注册拦截器的代码需要修改

    springboot2.0版本后配置拦截器会导致静态资源被拦截的解决方案
      //注册拦截器
      @Override
      public void addInterceptors(InterceptorRegistry registry) {
          //静态资源也不用拦截 springboot已经配置好了
          registry.addInterceptor(new MyLoginInterceptor()).addPathPatterns("/**")
                  .excludePathPatterns("/login","/","/user/login","/webjars/bootstrap/**");//登陆请求不能拦截
      }
    //加入了webjars下的路径 不然会被拦截

    list页面的代码

    《table>
    《tr>
      《th>bookId《/th>
      《th>bookName《/th>
    《/tr>
    《tr th:each="name:${bookList}">
      《td th:text="${nameStat.index+1}">
      《td th:text="${name}">《/td>
      《td>
        《form th:action="@{/book/}+${nameStat.index+1}" method="post">
          《input name="_method" value="delete" type="hidden">
          《button type="submit">Delete《/button>
        《/form>
      《/td>
    《/tr>
    《/table>

    控制器

    @Controller
    public class BookController {
    
      @Autowired
      private BookDao dao;
    
      @GetMapping("/books")
      public String getAllBookHandle(Model mv) {
          mv.addAttribute("bookList", dao.getAll());
          System.out.println("getAllBookHandle...");
          return "restfulTest";
      }
    //Get不要带/{id}
      @RequestMapping(value="/book",method = RequestMethod.GET)
      public String getBookById(@RequestParam("id") Integer id, Model mv) {
          mv.addAttribute("bookList", dao.getNameById(id));
          System.out.println("getBookById..." + id);
          return "restfulTest";
      }
    
      @PutMapping("/book")
      public String updateBookById(Integer id, String name) {
          dao.updateBookById(id, name);
          System.out.println("updateBookById..." + id);
          return "redirect:/books";
      }
    
      @DeleteMapping("/book/{id}")
      public String deleteBookById(@PathVariable("id") Integer id) {
          dao.deleteBookById(id);
          System.out.println("deleteBookById..." + id);
          return "redirect:/books";
      }
    
      @PostMapping("/book")
      public String setBook(String name) {
          dao.addBook(name);
          System.out.println("setBook...");
          return "redirect:/books";
      }
    }

    Dao

    @Repository
    public class BookDao {
      private static HashMap<Integer, String> hm = new HashMap<Integer, String>();
    
      static {
          hm.put(1, "西瓜书");
          hm.put(2, "java编程思想");
          hm.put(3, "算法导论");
          hm.put(4, "算法");
      }
    
      public List<String> getAll() {
          List<String> ls = new ArrayList<String>();
          for (Map.Entry<Integer, String> entry : hm.entrySet()) {
              ls.add(entry.getValue());
          }
          return ls;
      }
    
      public String getNameById(int id) {
          return hm.get(id);
      }
    
      public void deleteBookById(int id) {
          hm.remove(id);
      }
    
      public void updateBookById(int id, String name) {
          hm.replace(id,name);
      }
    
      public void addBook(String name) {
          hm.put(hm.size()+1, name);
      }
    }

    请求页面

    《a th:href="@{/books}">所有的书本《/a>
    《br>
    查找某id的书本
    《form th:action="@{/book}" method="get">
    书的Id:《input type="text" name="id"/>
    《input type="submit" value="提交"/>
    《/form>
    《br>
    添加书本
    《form th:action="@{/book}" method="post">
    书的名称:《input type="text" name="name"/>
    《input type="submit" value="提交"/>
    《/form>
    《br>
    修改某id的书本信息
    《form th:action="@{/book}" method="post">
    《input name="_method" value="put" type="hidden">
    书的id:《input type="text" name="id"/>
    书的名称:《input type="text" name="name"/>
    《input type="submit" value="提交"/>
    《/form>

    CRUD-员工删除

    《tr th:each="emp:${emps}">
    《td th:text="${emp.id}">《/td>
    《td>[[${emp.lastName}]]《/td>
    《td th:text="${emp.email}">《/td>
    《td th:text="${emp.gender}==0?'女':'男'">《/td>
    《td th:text="${emp.department.departmentName}">《/td>
    《td th:text="${#dates.format(emp.birth, 'yyyy-MM-dd HH:mm')}">《/td>
    《td>
    《a class="btn btn-sm btn-primary" th:href="@{/emp/}+${emp.id}">编辑《/a>
    《button th:attr="del_uri=@{/emp/}+${emp.id}" class="btn btn-sm btn-danger deleteBtn">删除《/button>
    《/td>
    《/tr>
    
    《script>
    $(".deleteBtn").click(function(){
    //删除当前员工的
    $("#deleteEmpForm").attr("action",$(this).attr("del_uri")).submit();
    return false;
    });
    《/script>
全部评论

相关推荐

点赞 收藏 评论
分享
牛客网
牛客企业服务