【Spring Boot】5.web开发

使用spring boot的开发流程:css

  1. 建立Spring Boot应用,选中咱们须要的模块;
  2. Spring Boot已经为咱们将全部的场景配置好了(spring-boot-autoconfigure包自动配置),咱们只须要在配置文件中指定对应Properties相关的少许配置就能够运行起来;
  3. 编写业务代码。

自动配置原理?html

请记住,饮水则思源,在每运用一个场景的时候,都要记住这是自动配置原理生效的。同时也要思考一下,spring-boot为咱们配置了什么?能不能修改?有哪些配置能够修改?是否能够扩展等。java

必定要记住:xxxAutoConfiguration帮咱们在容器中自动的配置相关组件,而其xxxProperties封装了配置文件的内容。react

5.1 spring boot对静态资源的映射规则

查询类(ctrl+shift+n)WebMvcAutoConfiguration查看其源码,与web相关的配置在此处均可以看到:jquery

@ConfigurationProperties(
    prefix = "spring.resources",
    ignoreUnknownFields = false
)
public class ResourceProperties {

也就是说有关配置能够经过spring.resourcec进行设置。web

webjars

再来看spring

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

                String staticPathPattern = this.mvcProperties.getStaticPathPattern();
                if (!registry.hasMappingForPattern(staticPathPattern)) {
                    this.customizeResourceHandlerRegistration(registry.addResourceHandler(new String[]{staticPathPattern}).addResourceLocations(getResourceLocations(this.resourceProperties.getStaticLocations())).setCachePeriod(this.getSeconds(cachePeriod)).setCacheControl(cacheControl));
                }

            }
        }

从该代码中咱们能够看到,全部/webjars/**都去classpath:/META-INF/resources/webjars中找资源;bootstrap

  • webjars: 以jar包的方式引入静态资源,如jquery等,咱们只须要配置pom文件便可引入这些静态资源,参考:官网地址

引入jquery:后端

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

咱们将下载到的jquery包展开能够看清楚目录结构,结合上面有关webjars的解释,咱们能够尝试经过访问127.0.0.1:8081/webjars/jquery/3.3.0/jquery.js,发现能够成功的访问到该静态资源。也就是说,打包后的项目的确是能够访问到的。数组

静态资源文件夹

还有一种配置资源的方式,"/**"访问当前项目的任何资源:

  • classpath:/META-INF/resources/
  • classpath:/resources/
  • classpath:/static/
  • classpath:/public/
  • /:当前项目的根路径

也就说,咱们在上述文件夹上面放置静态资源,若是没有特别处理,系统默认都是能够访问到的。

欢迎页

继续查看该类的源码:

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

此处配置的是欢迎页的映射。他都是静态资源文件夹下的全部index.html文件。被/**映射。 如localhost:8008则就是访问欢迎页面。

默认图标

@Bean
            public SimpleUrlHandlerMapping faviconHandlerMapping() {
                SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
                mapping.setOrder(-2147483647);
                mapping.setUrlMap(Collections.singletonMap("**/favicon.ico", this.faviconRequestHandler()));
                return mapping;
            }

该处配置咱们喜欢的图标,即浏览器表标签页左方的小图标。仍是映射到了**.favicon.ico,也就是咱们的全部静态文件夹下面。

从上面能够看出,系统默认用一个staticLocations做为映射路径,该路径默认为刚才所列出的默认路径,若是要配置自定义的资源路径,则能够:

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

就能够了,但要注意以前的默认值就不生效了。

5.2 模板引擎

市面上比较经常使用的有JSP、Velocity、FreeMarker、以及Spring boot推荐的Thymeleaf。

虽然模板引擎不少,可是其核心思想都是同样的,即按照必定的语法标准,将你的数据转化为实际的html页面,他们的区别只在于语法。

spring boot推荐的themeleaf语法比较简单,并且功能更强大。

引入themeleaf

注意,若是是1.x版本的spring boot此处若想使用3.x版本的thymeleaf的话,请在properties配置节配置其版本号以及布局版本号,以覆盖SB中默认的低版本thymeleaf。

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

查看spring-boot-autoconfigure包能够查看到有关thymeleaf相关的配置信息ThymeleafAutoConfiguration。其无非就是为thymeleaf添加相关的组件。咱们主要关注的是其相关的配置规则:ThymeleafProperties。

@ConfigurationProperties(
    prefix = "spring.thymeleaf"
)
public class ThymeleafProperties {
    private static final Charset DEFAULT_ENCODING;
    public static final String DEFAULT_PREFIX = "classpath:/templates/";
    public static final String DEFAULT_SUFFIX = ".html";
    private boolean checkTemplate = true;
    private boolean checkTemplateLocation = true;
    private String prefix = "classpath:/templates/";
    private String suffix = ".html";
    private String mode = "HTML";
    private Charset encoding;
    private boolean cache;
    private Integer templateResolverOrder;
    private String[] viewNames;
    private String[] excludedViewNames;
    private boolean enableSpringElCompiler;
    private boolean renderHiddenMarkersBeforeCheckboxes;
    private boolean enabled;
    private final ThymeleafProperties.Servlet servlet;
    private final ThymeleafProperties.Reactive reactive;

从默认规则里面咱们不难看出不少东西其实已经无需修改,就按该默认配置进行业务代码编写就好了。也就是从配置中能够看出,咱们的全部页面只须要放在classpath:/templates/资源文件夹下面,并以.html即为便可,根本无需其余多余的配置:

@RequestMapping("success")
    public String success(){
        return "success";
    }

该代码交给thymeleaf渲染template/success.html的文件。

5.3 thymeleaf语法

详细的官方文件请点击:参考文档

使用步骤:

  1. 导入thymeleaf的名称空间在html标签中,这样就有语法提示了;
<html xmlns:th="http://www.thymeleaf.org">
  1. controller传值
@RequestMapping("success")
    public String success(Map<String, Object> map){
        map.put("hello", "你好");
        return "success";
    }
  1. 渲染页使用thymeleaf语法
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
this is success page.
<p th:text="${hello}">这里显示默认信息。</p>
</body>
</html>

从输出结果能够看出,该页面彻底能够独立于出来直接运行,其输出的结果不通过渲染只不过只会输出这里是默认信息这样的字段而已,作到了很好的先后端分离。

标签语法

改变当前元素的文本内容等,例如:th:html属性 其值均可以替换原生的元素对应的值。 相关的介绍参考官方文档;

标签语法详解

th:each

参考文档Iteration部分。

该标签用于遍历数组数据。须要注意的是,该标签写在那个标签上,那个标签都会在每次遍历中生成一次。由此咱们能够了解到,该标签常常会和表格行标签<tr>搭配使用。

<h1>Product list</h1>
<table>
<tr>
<th>NAME</th>
<th>PRICE</th>
<th>IN STOCK</th>
</tr>
<tr th:each="prod : ${prods}">
<td th:text="${prod.name}">Onions</td>
<td th:text="${prod.price}">2.41</td>
<td th:text="${prod.inStock}? #{true} : #{false}">yes</td>
</tr>
</table>

表达式语法

  1. 简单表达式:
  • Variable Expressions: ${...}
  • Selection Variable Expressions: *{...}
  • Message Expressions: #{...}
  • Link URL Expressions: @{...}
  • Fragment Expressions: ~{...}
  1. 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 ,…
  1. Text operations(文本操做)
  • String concatenation: +
  • Literal substitutions: |The name is ${name}|
  1. Arithmetic operations(数学运算)
  • Binary operators: + , - , * , / , %
  • Minus sign (unary operator): -
  1. Boolean operations(布尔运算)
  • Binary operators: and , or
  • Boolean negation (unary operator): ! , not
  1. Comparisons and equality(比较运算)
  • Comparators: > , < , >= , <= ( gt , lt , ge , le )
  • Equality operators: == , != ( eq , ne )
  1. Conditional operators(条件操做)
  • If-then: (if) ? (then)
  • If-then-else: (if) ? (then) : (else)
  • Default: (value) ?: (defaultvalue)
  1. Special tokens(特殊标记-token:特征;记号)
  • No-Operation: _

以上全部的片断均可以组合使用,例如:

'User is of type ' + (${user.isAdmin()} ? 'Administrator' : (${user.type} ?: 'Unknown'))

表达式语法详解

${...}

该表达式用于获取变量的值,使用的是OGNL语法:

  1. 获取对象的属性、调用方法
  2. 使用内置的基本对象
${session.user.id}
  1. 内置的工具对象

*{...}

和${...}的功能是同样的,不过有一个补充使用,即配合<th:object>使用,以下面的示例:

<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>Name: <span th:text="${session.user.firstName}">Sebastian</span>.</p>
<p>Surname: <span th:text="${session.user.lastName}">Pepper</span>.</p>
<p>Nationality: <span th:text="${session.user.nationality}">Saturn</span>.</p>
</div>

也就是说,用*{}能够省略公共的对象信息。固然,咱们也能够在循环体内部混合使用这两种语法:

<div th:object="${session.user}">
<p>Name: <span th:text="*{firstName}">Sebastian</span>.</p>
<p>Surname: <span th:text="${session.user.lastName}">Pepper</span>.</p>
<p>Nationality: <span th:text="*{nationality}">Saturn</span>.</p>
</div>

#{...}

该表达式用于获取国际化内容。

@{...}

定义url。

<!-- Will produce 'http://localhost:8080/gtvg/order/details?orderId=3' (plus rewriting) -->
<a href="details.html"
th:href="@{http://localhost:8080/gtvg/order/details(orderId=${o.id})}">view</a>
<!-- Will produce '/gtvg/order/details?orderId=3' (plus rewriting) -->
<a href="details.html" th:href="@{/order/details(orderId=${o.id})}">view</a>
<!-- Will produce '/gtvg/order/3/details' (plus rewriting) -->
<a href="details.html" th:href="@{/order/{orderId}/details(orderId=${o.id})}">view</a>

若是须要添加多个参数的话,用逗号分隔便可。

@{/order/process(execId=${execId},execType='FAST')}

~{...}

片断引用表达式。

<div th:insert="~{commons :: main}">...</div>

行内表达式

参考文档Inlining部分。

不少时候,咱们想显示的文本实际上是单纯的文本节点,彻底不想使用html标签包裹,这时候须要怎么写呢。咱们显然不能这样写:

my name is ${$name}.

正确的写法应该使用行内表达式

my name is [[${name}]]!

关于行内式有两种标签写法:

  • [[...]] 等价于th:text,会转义特殊字符,即按原样输出,<h1>标签会格式化内部文字;
  • [(...)] 等价于th:utext,不会转义特殊字符,即按相应的标签格式输出,例如<h1>标签直接输出为<h1>

转义这个有点绕,不当心把本身也绕进去了。简单一点的记法:想要有html标签格式化文本,用text([[...]]),想原样输出标签用utext,。

5.4 spring mvc自动配置

经过官方文档查阅

源码查阅

查看文档

Spring boot 自动配置好了Spring mvc,如下是SB对spring mvc的默认配置,这些配置组件均可以在WebMvcConfigurationSupport类中查看相应源码:

  1. 自动配置了ViewResoulver(视图解析器:根据方法的返回值获得视图对象(View),视图对象决定如何渲染...转发、重定向)

    • ContentNegotiatingViewResolver : 组合全部的视图解析器。
    • 如何定制:咱们能够本身给容器中添加一个视图解析器,他会自动的将其组合起来。
  2. Support for serving static resources, including support for WebJars (covered later in this document)): 静态资源文件夹路径,webjars.

  3. Automatic registration of Converter, GenericConverter, and Formatter beans.

    • Converter:转换器,类型转化
    • Formatter 格式化器:2017.1.1===Date;
  4. Support for HttpMessageConverters (covered later in this document)

    • springMVC中用来转换http请求和响应的User===Json
    • HttpMessageConverters是从容器中肯定获取全部的HttpMessageConver;本身给容器中添加赌赢的组件,只须要将本身的组件注册到容器中便可(@Bean,@Component)
  5. Automatic registration of MessageCodesResolver (covered later in this document).

    • 定义错误代码的生成规则的;
  6. Static index.html support.

    • 静态页面支持
  7. Custom Favicon support (covered later in this document).

    • 图标支持
  8. Automatic use of a ConfigurableWebBindingInitializer bean (covered later in this document).

    • 做用为初始化WebDataBinder的,例如请求数据JavaBean,就须要用到数据绑定器,里面牵涉到类型转化、格式化等等;
    • 咱们能够配置一个ConfigurableWebBindingInitializer来替换默认的。

SB对web的自动配置其实不止这个类中作了,其实还有其余的以***AutoConfiguration的类,都对web场景进行了一些配置。

5.5 修改SpringBoot的默认配置

模式:给容器中添加对应的自定义组件就能够了。

  1. Spring Boot在自动配置不少组件的时候,都会先看容器中有没有用户本身配置(@Bean、@Component),若是有就用用户配置的,若是没有就用Spring Boot本身默认配置的;可是若是组件能够有多个的话,则是将其全部配置的组合起来;
  2. 仅靠Spring boot的默认配置仍是不够的,咱们还须要一些本身新的配置,那么咱们则须要扩展以及全面接管springMVC的相关功能。如何作呢?见下一节。

5.6 扩展与全面接管SpringMVC

咱们之前配置试图映射、拦截器均可以在springmvc.xml中进行配置,以下图所示:

```xml
    <mvc:view-controller path="/hello" view-name="success"/>
    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/hello"/>
            <bean></bean>
        </mvc:interceptor>
    </mvc:interceptors>

那在spring boot中该怎么作呢?

编写一个配置类(@Configuration),是WebMvcConfigurerAdapter类型;不能标注@EnableWebMvc

注意此处是1.x版本,2.x版本提示这个类准备移除了,咱们再也不须要继承WebMvcConfigurerAdapter,而是直接实现接口WebMvcConfigurer来写配置类便可。

@Configuration
public class MyConfig implements WebMvcConfigurer {
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        // 浏览器发送/restweb也会来到hello页面.
        registry.addViewController("/restweb").setViewName("hello");
    }
}

该方式即保留了spring boot的全部自动配置,也能用咱们的扩展配置。能够查看以下了解原理:

  • WebMvcAutoConfiguration是springmvc的自动配置类;
  • 再作其余自动配置时会导入,@import(EnableWebMvcConfiguration.class)
@Autowired(required = false)
	public void setConfigurers(List<WebMvcConfigurer> configurers) {
		if (!CollectionUtils.isEmpty(configurers)) {
			this.configurers.addWebMvcConfigurers(configurers);
		}
	}

spring boot 会将全部的WebMvcConfiguration相关配置一块儿调用

  • 容器中全部的WebMvcConfigurer都会起做用,包括咱们本身配置的;

全面接管SpringMVC

这种状况下,咱们通常是不想要SpringBoot的全部配置,全部的都是由咱们本身来指定。只须要在自定义配置类中添加一个配置@EnableWebMvc便可,这样以后全部的SpringMVC的自动配置都失效了。

@EnableWebMvc原理:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
}

注意其中的DelegatingWebMvcConfiguration,其源码以下:

@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {

咱们再来看自动配置类的源码:

@Configuration
@ConditionalOnWebApplication(
    type = Type.SERVLET
)
@ConditionalOnClass({Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class})
@ConditionalOnMissingBean({WebMvcConfigurationSupport.class})
@AutoConfigureOrder(-2147483638)
@AutoConfigureAfter({DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class})
public class WebMvcAutoConfiguration {

咱们能够看到这一句

@ConditionalOnMissingBean({WebMvcConfigurationSupport.class})

其生效方式恰好就是若容器中没有WebMvcConfigurationSupport,因为前面的注解致使该组件导入了进来,所以自动配置类就不生效了。导入的WebMvcConfigurationSupport只是SpringMVC最基本的功能。

使用SpringBoot通常不推荐SpringMVC全面接管(那就不必用SpringBoot了)

5.7 restweb制做

引入资源

添加entities包以及dao包,引入测试用例类以及静态资源,这里参考原视频教程配套代码。 添加thymeleaf场景包,springboot会自动启用该场景:

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

配置首页映射

咱们已经将index.html放在了template文件夹下面,此时,咱们为了将index.html映射到网站的首页,通常会写以下的控制方法:

@RequestMapping({"/","index.html"})
    public String index(){
        return "index";
    }

该控制器方法能够将请求路径"/","index.html"都映射为index.html的资源信息(thymeleaf渲染),还能够经过在配置类中配置映射的方式完成上述效果:

@Override
    public void addViewControllers(ViewControllerRegistry registry) {
        // 浏览器发送/restweb也会来到hello页面.
        registry.addViewController("/").setViewName("index");
       // registry.addViewController("/index.html").setViewName("index");
    }

再有,咱们前面有讲到,springboot会将咱们生成的全部组件添加到相应的组件族中,所以,咱们这里也能够手动的建立一个WebMvcConfigurer,也一样生效(别忘了添加组件注解:@Bean,将组件注册到容器中),上下两端代码完成的效果是一致的,固然也能够是互补的(映射不一样的话):

@Bean
    public WebMvcConfigurer myWebMvcConfigurer(){
        return new WebMvcConfigurer() {
            @Override
            public void addViewControllers(ViewControllerRegistry registry) {
                registry.addViewController("/index").setViewName("index");
            }
        };
    }

经过webjars引入其余静态资源

<dependency>
    <groupId>org.webjars</groupId>
    <artifactId>jquery</artifactId>
    <version>3.3.1-1</version>
</dependency>
<dependency>
    <groupId>org.webjars</groupId>
    <artifactId>bootstrap</artifactId>
    <version>4.1.3</version>
</dependency>

引入以后,则能够将以前的相对路径引入的静态资源替换掉了,找到index.html页面(改成login.html),使用th:xxx修改路径引入:

<link th:href="@{/webjars/bootstrap/4.1.3/css/bootstrap.css}" href="asserts/css/bootstrap.min.css" rel="stylesheet">

<html xmlns:th="http://www.thymeleaf.org"> 别忘了经过这个能够添加thymeleaf语法提示哦。

5.8 国际化

在springMVC中,咱们须要:

  1. 编写国际化配置文件;
  2. 使用ResourcebundleMessageSource管理国际化资源文件;
  3. 在页面使用fmt:message提取国际化内容。

步骤:

  1. 编写国际化配置文件,抽去页面须要显示的国际化信息;
    • /resource目录下新建i18n文件夹
    • 在i18n下简历login文件夹
    • 建立login.properties表明默认显示、login_en_US.properties(英国)、login_zh_CN.properties(中国);
    • 在编译器提供的国际化操做界面一次性写入相关的文本信息,如:login.username login.password等(在同一界面进行配置,手写比较繁琐。)。
  2. 若是是SpringMVC的话,如上面步骤所示,咱们还要配置使用ResourcebundleMessageSource管理国际化资源文件,在spring boot中,则不须要了(他已经帮我配置好了)。查看类MessageSourceAutoConfiguration中已经有该组件的配置了。
@Configuration
@ConditionalOnMissingBean(
    value = {MessageSource.class},
    search = SearchStrategy.CURRENT
)
@AutoConfigureOrder(-2147483648)
@Conditional({MessageSourceAutoConfiguration.ResourceBundleCondition.class})
@EnableConfigurationProperties
public class MessageSourceAutoConfiguration {
     @Bean
    @ConfigurationProperties(
        prefix = "spring.messages"
    )
    public MessageSourceProperties messageSourceProperties() {
        return new MessageSourceProperties();
    }

配置了组件

@Bean
    public MessageSource messageSource(MessageSourceProperties properties) {
        ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
        if (StringUtils.hasText(properties.getBasename())) {
            messageSource.setBasenames(StringUtils.commaDelimitedListToStringArray(StringUtils.trimAllWhitespace(properties.getBasename())));
        }

咱们注意到这里

String basename = context.getEnvironment().getProperty("spring.messages.basename", "messages");

也就是说,咱们的配置文件能够直接放在类路径下叫messages.properties便可,可是这样作显然不是很好,所以咱们只须要修改spring.messages.basename配置便可自定义国际化内容的存放位置。

spring.messages.basename=i18n/login

再次提醒,这里2.x版本. 1.x版本这里彷佛是不同的写法都行i18n.login

  1. 去页面获取国际化的值 还记得学习thymeleaf的时候讲过一个获取国际化值的标签吗?没错,就是他:
#{...}

部分源码以下:

<input type="text" class="form-control" th:placeholder="#{login.username}" placeholder="Username" required="" autofocus="">

区域信息对象

如今咱们想经过点击下方的中文``English两个按钮切换国际化页面该怎么作呢?

国家化Locale(区域信息对象);LocaleResolver获取区域信息对象,在WebMvcAutoConfiguration:

@Bean
        @ConditionalOnMissingBean
        @ConditionalOnProperty(
            prefix = "spring.mvc",
            name = {"locale"}
        )
        public LocaleResolver localeResolver() {
            if (this.mvcProperties.getLocaleResolver() == org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties.LocaleResolver.FIXED) {
                return new FixedLocaleResolver(this.mvcProperties.getLocale());
            } else {
                AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver();
                localeResolver.setDefaultLocale(this.mvcProperties.getLocale());
                return localeResolver;
            }
        }

默认的区域信息解析器就是根据请求头带来的区域信息进行的国际化。

Accept-Language: en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7

所以,咱们彻底能够本身写一个区域化信息解析器来本身处理请求信息。

  1. 修改切换按钮的连接信息:
<a class="btn btn-sm" th:href="@{/index.html(l='zh_CN')}">中文</a>
			<a class="btn btn-sm" th:href="@{/index.html(l='en_US')}">English</a>
  1. 自定义一个区域信息解析器:

别忘了添加该组件:

@Bean
    public LocaleResolver localeResolver(){
        return new MyLocaleResolver();
    }

注意此处方法名必须写为localResolver,由于Spring会默认将其做为Bean的名字,而SB的默认区域解析器会以此做为是否生成的条件。

  1. 点击连接,便可实现切换国际化。

5.9 拦截器

模板修改实时生效

模板殷勤修改之后,若想实时生效,须要作以下操做:

(1) 禁用模板引擎缓存(开发过程当中建议关闭,生产环境可开启)

spring.thymeleaf.cache=false

(2)idea编译器的话,按ctrl + F9,从新编译一下便可。

登陆错误消息的提示

#工具.工具方法 if标签的优先级比text高

<p style="color:red" th:text="{msg}" th:if="${not #strings.isEmpty(msg)}"></p>

重定向防止表单重复提交

return "redirect:/main.html"

拦截器

经过进行登陆检查,即实现SpringMVC中的HandlerIntercepter拦截方法.

LoginController 登陆逻辑检测

@Controller
public class LoginController {
    @PostMapping(value = {"/user/login"})
    public String login(@RequestParam("username")String username,
                        @RequestParam("password") String password,
                        HttpSession session,
                        Map<String, Object> map){
        if(!StringUtils.isEmpty(username) && "123456".equals(password)){
            session.setAttribute("loginUser","admin");
            return "redirect:/index";
        }else{
            map.put("msg", "用户名或密码错误");
            return "login";
        }

    }
}

其中:

  1. @PostMapping(value = {"/user/login","login"}) 等价于 @RequestMapping(value={"/user/login","login"}, method = RequestMethod.POST);
  2. @RequestParam("username") 强调该请求必须接受一个名为username的参数,不然会报400错误,并提示要求的参数不存在。
  3. 若是咱们直接返回index给模板引擎解析,显然当前是转发user/login请求,若是咱们刷新页面,会出现表单的重复提交,而且,若是index.html中若是写的是相对路径,则其路径会误认为user是一个项目名称,致使资源所有引入失败。所以,咱们须要将其重定向。
return "redirect:/index";

视图解析器添加相关映射

@Override
    public void addViewControllers(ViewControllerRegistry registry) {
        // 浏览器发送/restweb也会来到hello页面.
        registry.addViewController("/login").setViewName("login");
        registry.addViewController("/").setViewName("index");
        registry.addViewController("/index").setViewName("index");
    }

其中:

registry.addViewController("/login").setViewName("login");

就等价于咱们写一个Controller进行映射:

@RequestMapping(value="/login")
public String login(){
    return "login";
}

所以,若是只是单纯的映射跳转而没有其余的业务逻辑的话,推荐在这里直接配置便可。

配置拦截器

首先,咱们要编写一个拦截器组件:

public class LoginHandlerInterupter implements HandlerInterceptor {
    /**
     * 其余的方法因为java8的default,咱们能够不实现了,专心写这部分代码进行登陆检查便可
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        Object loginUser = request.getSession().getAttribute("loginUser");
        // 若登陆,放行请求
        if(loginUser != null){
            return true;
        }
        request.setAttribute("msg","未登陆,请先登陆");
        System.out.println("this is is a preHandler method.");
        request.getRequestDispatcher("/login").forward(request, response);
        return false;
    }
}

注意:

  • 自定义的拦截器都要实现HandlerInterceptor接口,经过查看该接口的源码能够发现:
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {

		return true;
	}

其全部的方法都用了java8新关键字default修饰了全部的接口方法,所以,在咱们的实现类中,无需实现全部的方法(若是不了解的话,请参考java8相关书籍——推荐 java8实战 ,大概就是default修饰的方法不须要实现类实现了,挑本身想覆盖的方法覆盖便可。)

  • 咱们这里实现preHandler方法,该方法会在全部的Http请求以前执行,也就是说,咱们能够经过此方法对Session进行检索,从而判断用户此刻是否登陆。若登陆,咱们放行请求;若未登陆,则对请求惊醒拦截,而且将当前请求跳转到登陆界面;

添加拦截器组件到容器

添加拦截器组件到容器中,并配置相关的参数:

/**
     * 添加自定义拦截器到容器中,并配置相关参数
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 视频中老师有讲到spring boot已经对静态资源作了映射,不过他是针对1.x版本这样解释,可是我2.0版本这里是不行的,仍是得放行静态资源,因此加了/assert和webjars这两个静态资源路径的放行,否则静态资源仍是会被拦截下来。请注意一下。
        registry.addInterceptor(new LoginHandlerInterupter()).addPathPatterns("/**")
                . excludePathPatterns("/login","/user/login","/asserts/**","/webjars/**");
    }
  • 该覆盖方法配置在实现WebMvcConfigurer的配置类中,目前其彻底代码以下所示:
@Configuration
public class MyConfig implements WebMvcConfigurer {
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        // 浏览器发送/restweb也会来到hello页面.
        registry.addViewController("/login").setViewName("login");
        registry.addViewController("/").setViewName("index");
        registry.addViewController("/index").setViewName("index");
    }

      /**
     * 添加自定义拦截器到容器中,并配置相关参数
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // SB已经对静态资源 *.css , *.jss进行了映射,咱们能够不加管理
        registry.addInterceptor(new LoginHandlerInterupter()).addPathPatterns("/**")
                . excludePathPatterns("/login","/user/login","/asserts/**","/webjars/**");
    }

    /**
     * 自定义本地解析器
     * @return
     */
    @Bean
    public LocaleResolver localeResolver(){
        return new MyLocaleResolver();
    }

能够看到,该配置类实现WebMvcConfigurer接口,用于修改spring mvc相关的默认配置,好比这里添加了视图控制器、拦截器,另外则是一个以前为了作本地化配置的自定义本地化解析器。

  • addPathPatterns("/**") 代表该拦截器会拦截全部的请求
  • .excludePathPatterns("/login","/user/login")代表"/login","/user/login","/asserts/**","/webjars/**"这四个请求,是特殊的、不拦截的,注意后两个是资源文件入口。

测试运行

测试运行正常的登陆,一切运行正常。

相关文章
相关标签/搜索