SpringBoot进阶学习下篇

2021-01-07  本文已影响0人  HelloWorld打断点

5、SprinBoot整合Thymeleaf模版引擎
5.1 SpringBoot对模板的支持
SpringBoot框架支持很多种模板,(FreeMarker、Thymeleaf、Mustache等),大大方便了前端的开发。

SpringBoot对Jsp不太支持,并且官方没有提供整合的配置,原因:

1、SpringBoot使用的是嵌入式Servlet web容器,默认使用jar的打包方式,jar包本身不支持jsp模板。

2、如果使用Undertow嵌入式容器部署SpringBoot项目的话,不支持Jsp模板。

3、SpringBoot默认提供了一个处理请求路径"/error"的统一错误处理器,来返回具体的异常信息,如果使用jsp模板的话无法对默认的错误信息进行覆盖,只能在指定的位置指定错误页面。

5.2 Thymeleaf模板引擎技术
5.2.1 Thymeleaf介绍
Thymeleaf是一种基于服务器端的java模板引擎技术,也是一个优秀的面向Java的XML、XHTML、HTML5页面模板,它具有丰富的标签语言、函数和表达式,在使用Spring Boot框架进行页面设计时,一般会选择Thymeleaf模板。

5.2.2Thymeleaf语法
在HTML页面上使用Thymeleaf标签,Thymeleaf 标签能够动态地替换掉静态内容,使页面动态展示。

常用的标签
th:标签 说明
th:insert 布局标签,替换内容到引入的文件
th:replace 页面片段包含(类似JSP中的include标签)
th:each 元素遍历(类似JSP中的c:forEach标签)
th:if 条件判断,如果为真
th:unless 条件判断,如果为假
th:switch 条件判断,进行选择性匹配
th:case 条件判断,进行选择性匹配
th:value 属性值修改,指定标签属性值
th:href 用于设定链接地址
th:src 用于设定链接地址
th:text 用于指定标签显示的文本内容
标准表达式
1、变量表达式 ${...}

来获取到上下文中变量的值。

Demo:<p th:text="${title}">这是标题</p>

Thymeleaf内置的上下文对象有:

ctx 上下文对象
vars 上下文变量
locale 上下文区域设置
request (仅限Web Context)HttpServletRequest对象
response (仅限Web Context)HttpServletResponse对象
session (仅限Web Context)HttpSession对象
servletContext (仅限Web Context)ServletContext对象
2、选择变量表达式 *{...}

也是获取到上下问的变量只不过是从业务对象中获取

Demo:

<div th:object="${book}"> <p>titile: <span th:text="*{title}">标题</span>.</p></div>

3、消息表达式 #{...}

消息表达式#{...}主要用于Thymeleaf模板页面国际化内容的动态替换和展示,使用消息表达式#

{...}进行国际化设置时,还需要提供一些国际化配置文件。后边会说明

4、链接表达式 @{...}

链接表达式@{...}一般用于页面跳转或者资源的引入,在Web开发中占据着非常重要的地位,并且使用

也非常频繁,Demo:

<a th:href="@{http://localhost:8080/order/details(orderId={o.id})}">view</a><a th:href="@{/order/details(orderId={o.id})}">view</a>

5、片段表达式 ~{...}

片段表达式~{...}用来标记一个片段模板,并根据需要移动或传递给其他模板。其中,最常见的用法是使用th:insert或th:replace属性插入片段。

<div th:insert="~{thymeleafDemo::title}"></div>

thymeleaf Demo
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" type="text/css" media="all"
href="../../css/gtvg.css" th:href="@{/css/gtvg.css}" />
<title>Title</title>
</head>
<body>
<p th:text="${hello}">HelloWorld</p>
</body>
</html>
5.2.3Thymeleaf使用Demo
POM
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
配置

模板缓存

spring.thymeleaf.cache = true

模板编码

spring.thymeleaf.encoding=utf-8

应用于模板的模板模式

spring.thymeleaf.mode = HTML5

指定模板页面存放路径

spring.thymeleaf.prefix = classpath:/templates/

指定模板页面名称的后缀

spring.thymeleaf.suffix = .html

配置国际化文件基础名

spring.messages.basename=i18n.login

代码
@Controller
public class LoginController {
@RequestMapping("/toLoginPage")
public String toLoginPage(Model model){
model.addAttribute("currentYear", Calendar.getInstance().get(Calendar.YEAR));
return "login";
}
}

HTML
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1,shrink-to-fit=no">
<title>用户登录界面</title>
<link th:href="@{/login/css/bootstrap.min.css}" rel="stylesheet">
<link th:href="@{/login/css/signin.css}" rel="stylesheet">
</head>
<body class="text-center">

<form class="form-signin">
<img class="mb-4" th:src="@{/login/img/login.jpg}" width="72" height="72">
<h1 class="h3 mb-3 font-weight-normal" th:text="#{login.tip}">请登录</h1>
<input type="text" class="form-control"
th:placeholder="#{login.username}" required="" autofocus="">
<input type="password" class="form-control"
th:placeholder="#{login.password}" required="">
<div class="checkbox mb-3">
<label>
<input type="checkbox" value="remember-me"> [[#{login.rememberme}]]
</label>
</div>
<button class="btn btn-lg btn-primary btn-block" type="submit" th:text="#{login.button}">登录</button>
<p class="mt-5 mb-3 text-muted">© <span th:text="{currentYear}">2019</span>-<span th:text="{currentYear}+1">2020</span></p>
<a class="btn btn-sm" th:href="@{/toLoginPage(l='zh_CN')}">中文</a>
<a class="btn btn-sm" th:href="@{/toLoginPage(l='en_US')}">English</a>
</form>
</body>
</html>

5.2.4Thymeleaf设置本地化语言
首先需要在resources目录下创建一个文件夹来存放默认的和翻译好的语言
login.properties,login_zh_CN.properties 【默认和中文的都一样】
login.tip=请登录
login.username=用户名
login.password=密码
login.button=登录
login.rememberme=记住我

login_en_US.properties【英文】
login.tip=please sign in
login.username=username
login.password=password
login.button=login
login.rememberme=remmeber me

配置

配置国际化文件基础名

spring.messages.basename=language.login

Html中修改

<form class="form-signin">
<img class="mb-4" th:src="@{/login/img/login.jpg}" width="72" height="72">
<h1 class="h3 mb-3 font-weight-normal" th:text="#{login.tip}">请登录</h1>
<input type="text" class="form-control"
th:placeholder="#{login.username}" required="" autofocus="">
<input type="password" class="form-control"
th:placeholder="#{login.password}" required="">
<div class="checkbox mb-3">
<label>
<input type="checkbox" value="remember-me"> [[#{login.rememberme}]]
</label>
</div>
<button class="btn btn-lg btn-primary btn-block" type="submit" th:text="#{login.button}">登录</button>
<p class="mt-5 mb-3 text-muted">© <span th:text="{currentYear}">2019</span>-<span th:text="{currentYear}+1">2020</span></p>

<a class="btn btn-sm" th:href="@{/toLoginPage(setLanguage='zh_CN')}">中文</a>
<a class="btn btn-sm" th:href="@{/toLoginPage(setLanguage='en_US')}">English</a>
</form>
</body>
</html>

设置本地化解析器

public class LanguageLocaleResolver implements LocaleResolver {
@Override
public Locale resolveLocale(HttpServletRequest request) {
String setLanguage = request.getParameter("setLanguage");
Locale locale;
if (!StringUtils.isEmpty(setLanguage)) {
String[] s = setLanguage.split("_");
locale = new Locale(s[0],s[1]);
}else{//为空的情况
String header = request.getHeader("Accept-Language");
String[] split = header.split(",");
String[] split1 = split[0].split("-");
locale = new Locale(split1[0],split1[1]);
}
return locale;
}

@Override
public void setLocale(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) {
}

}

配置到Spring容器中进行覆盖 一定要是localeResolver才行

@Bean
public LocaleResolver localeResolver(){
return new LanguageLocaleResolver();
}
总结:在进行本地化配置的时候一定要注意配置文件的命名 文件名语言国家

5.3SpringBoot缓存
SpringBoot支持很多缓存组件:

Generic
JCache (JSR-107) (EhCache 3、Hazelcast、Infinispan等)
EhCache 2.x
Hazelcast
Infinispan
Couchbase
Redis
Caffeine
Simple

添加@EnableCaching之后默认会从这个9个缓存组件中去查找,没有的话默认使用Simple缓存组件。

5.3.1默认缓存使用
使用方法:

在启动类上贴上@EnableCaching,在对应的service查询方法上贴上@Cacheable(cacheNames = "name")来开启缓存。

@SpringBootApplication
@EnableCaching
public class CacheApplication {
public static void main(String[] args) {
SpringApplication.run(CacheApplication.class, args);
}
}

//根据id查询person对象爱ing
@Cacheable(cacheNames = "person")
public Person findById(Integer id){
Optional<Person> op = personRepository.findById(id);
return op.isPresent() ? op.get() : null;
}

@Cacheable 注解
属性名 说明
value/cacheNames 指定缓存空间的名称,必配属性。这两个属性二选一使用
key 指定缓存数据的key,默认使用方法参数值,可以使用SpEL表达式SpEL表达式:#root.mathodName ->获取当前方法名#root.mathod ->当前被调用的方法 #root.target ->当前被调用的目标对象#root.targetClass ->当前被调用的目标对象类#root.args[0] 第一个参数, #root.args[1] 第二个参数... ->当前被调用的方法的参数列表#参数名, 也可以使用 #p0 #a0 0是参数的下标索引 ->根据参数名字取出值#result ->当前方法的返回值
keyGenerator 指定缓存数据的key的生成器,与key属性二选一使用
cacheManager 指定缓存管理器
cacheResolver 指定缓存解析器,与cacheManager属性二选一使用
condition 指定在符合某条件下,进行数据缓存
unless 指定在符合某条件下,不进行数据缓存
sync 指定是否使用异步缓存。默认false
执行原理

Cacheable通过动态代理的方式进行缓存,方法运行之前,先去查询Cache(缓存组件),按照cacheNames指定的名字获取,(CacheManager先获取相应的缓存),第一次获取缓存如果没有Cache组件会自动创建;

@Cacheable在不引入任何的依赖的情况下默认使用的是SimpleCacheConfiguration配置的ConcurrentMapCacheManager缓存组件,将缓存结果存储在cacheMap中。使用一个key,默认就是方法的参数,如果多个参数或者没有参数,是按照某种策略生成的,默认是使用KeyGenerator生成的。

使用SimpleKeyGenerator生成key策略:

参数个数 key
没有参数 new SimpleKey()
有一个参数 参数值
多个参数 new SimpleKey(params)
@CachePut注解
目标方法执行完之后生效, @CachePut被使用于修改操作比较多,哪怕缓存中已经存在目标值了,但是这个

注解保证这个方法依然会执行,执行之后的结果被保存在缓存中

@CacheEvict注解
@CacheEvict注解是由Spring框架提供的,可以作用于类或方法(通常用在数据删除方法上),该注解

的作用是删除缓存数据。@CacheEvict注解的默认执行顺序是,先进行方法调用,然后将缓存进行清

除。

5.3.2Redis缓存
1、注解方式使用redis缓存
在使用注解的方式使用redis缓存的时候只需要导入redis的包和配置redis,SpringBoot默认就会走redis缓存,使用方法和默认缓存的使用方法一致。

POM

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

配置

Redis配置

Redis服务地址

spring.redis.host=127.0.0.1

Redis服务器连接端口

spring.redis.port=6379

Redis服务器连接密码(默认为空)

spring.redis.password=
spring.redis.database=1

对基于注解的Redis缓存数据统一设置有效期为1分钟,单位毫秒

spring.cache.redis.time-to-live=60000

原理是在引入redis配置之后,系统直接就会使用RedisCacheConfiguration对缓存进行配置,使用RedisCacheManager来管理缓存,使用方式也是通过@Cacheable等注解来实现的。

修改默认的序列化方式为json方式

通过查看RedisCacheConfiguration 类的源码发现默认是jdk的序列化方式

修改默认的序列化方式

@Configuration
public class RedisAnnoConfig {

@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
    // 分别创建String和JSON格式序列化对象,对缓存数据key和value进行转换
    RedisSerializer<String> strSerializer = new StringRedisSerializer();
    Jackson2JsonRedisSerializer jacksonSeial =
            new Jackson2JsonRedisSerializer(Object.class);

    // 解决查询缓存转换异常的问题
    ObjectMapper om = new ObjectMapper();
    om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
    om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
    jacksonSeial.setObjectMapper(om);

    // 定制缓存数据序列化方式及时效
    RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
            .entryTtl(Duration.ofDays(1))//缓存时间为1天
            .serializeKeysWith(RedisSerializationContext.SerializationPair
                    .fromSerializer(strSerializer))
            .serializeValuesWith(RedisSerializationContext.SerializationPair
                    .fromSerializer(jacksonSeial))
            .disableCachingNullValues();
    RedisCacheManager cacheManager = RedisCacheManager
            .builder(redisConnectionFactory).cacheDefaults(config).build();
    return cacheManager;
}

}

2、使用RedisTemplate进行缓存
直接注入RedisTemplate进行操作,但是有个问题,注入的redisTemplate在保存数据的时候还是jdk默认的序列化方式因此需要修改序列化方式。RedisAutoConfiguration类来分析。

修改RedisTemplate序列化方式,思路是直接覆盖自动注册的redisTemplate实例。

@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<Object,Object> redisTemplate(
RedisConnectionFactory redisConnectionFactory){
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<Object, Object>();
redisTemplate.setConnectionFactory(redisConnectionFactory);

    //设置序列化方式
    Jackson2JsonRedisSerializer jackSonSerializer = 
            new Jackson2JsonRedisSerializer(Object.class);
    redisTemplate.setDefaultSerializer(jackSonSerializer);

    //解决查询缓存转换异常问题
    ObjectMapper om = new ObjectMapper();
    om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
    om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
    jackSonSerializer.setObjectMapper(om);
    // 设置RedisTemplate模板API的序列化方式为JSON
    redisTemplate.setDefaultSerializer(jackSonSerializer);

    return redisTemplate;
}

}

上一篇下一篇

猜你喜欢

热点阅读