- 2023-04-13 13:57:15
- 1626 热度
- 0 评论
SpringMVC 源码分析系列最后一篇,和大家聊一聊 Theme。
Theme,就是主题,点一下就给网站更换一个主题,相信大家都用过类似功能,这个其实和前面所说的国际化功能很像,代码其实也很像,今天我们就来捋一捋。
考虑到有的小伙伴可能还没用过 Theme,所以这里無名先来说下用法,然后我们再进行源码分析。
1.一键换肤
来做一个简单的需求,假设我的页面上有三个按钮,点击之后就能一键换肤,像下面这样:
我们来看下这个需求怎么实现。
首先三个按钮分别对应了三个不同的样式,我们先把这三个不同的样式定义出来,分别如下:
blue.css:
1 |
body{ |
green.css:
1 |
body{ |
red.css:
1 |
body{ |
主题的定义,往往是一组样式,因此我们一般都是在一个 properties 文件中将同一主题的样式配置在一起,这样方便后期加载。
所以接下来我们在 resources 目录下新建 theme 目录,然后在 theme 目录中创建三个文件,内容如下:
blue.properties:
1 |
index.body=/css/blue.css |
green.properties:
1 |
index.body=/css/green.css |
red.properties:
1 |
index.body=/css/red.css |
在不同的 properties 配置文件中引入不同的样式,但是样式定义的 key 都是 index.body,这样方便后期在页面中引入。
接下来在 SpringMVC 容器中配置三个 Bean,如下:
1 |
<mvc:interceptors> |
- 首先配置拦截器 ThemeChangeInterceptor,这个拦截器用来解析主题参数,参数的 key 为 theme,例如请求地址是
/index?theme=blue
,该拦截器就会自动设置系统主题为 blue。当然也可以不配置拦截器,如果不配置的话,就可以单独提供一个修改主题的接口,然后手动修改主题,类似下面这样:
1 |
@Autowired |
themeStr 就是新的主题名称,将其配置给 themeResolver 即可。
- 接下来配置 ResourceBundleThemeSource,这个 Bean 主要是为了加载主题文件,需要配置一个 basenamePrefix 属性,如果我们的主题文件放在文件夹中,这个 basenamePrefix 的值就是
文件夹名称.
。 - 接下来配置主题解析器,主题解析器有三种,分别是 CookieThemeResolver、FixedThemeResolver、SessionThemeResolver,这里我们使用的是 SessionThemeResolver,主题信息将被保存在 Session 中,只要 Session 不变,主题就一直有效。这三个主题解析器無名会在下一小节中和大家仔细分析。
配置完成后,我们再来提供一个测试页面,如下:
1 |
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> |
最关键的是:
1 |
<link rel="stylesheet" href="<spring:theme code="index.body" />" > |
css 样式不直接写,而是引用我们在 properties 文件中定义的 index.body,这样将根据当前主题加载不同的 css 文件。
最后再提供一个处理器,如下:
1 |
@GetMapping(path = "/index") |
这个就很简单了,没啥好说的。
最后启动项目进行测试,大家就可以看到我们文章一开始给出的图片了,点击不同的按钮就可以实现背景的切换。
是不是非常 Easy!
2.原理分析
主题这块涉及到的东西主要就是主题解析器,主题解析器和我们前面所说的国际化的解析器非常类似,但是比它更简单,我们一起来分析下。
先来看下 ThemeResolver 接口:
1 |
public interface ThemeResolver { |
这个接口中就两个方法:
- resolveThemeName:从当前请求中解析出主题的名字。
- setThemeName:设置当前主题。
ThemeResolver 主要有三个实现类,继承关系如下:
接下来我们对这几个实现类来逐个分析。
2.1 CookieThemeResolver
直接上源码吧:
1 |
@Override |
先来看 resolveThemeName 方法:
- 首先会尝试直接从请求中获取主题名称,如果获取到了,就直接返回。
- 如果第一步没有获取到主题名称,接下来就尝试从 Cookie 中获取主题名称,Cookie 也是从当前请求中提取,利用 WebUtils 工具进行解析,如果解析到了主题名称,就赋值给 themeName 变量。
- 如果前面没有获取到主题名称,就使用默认的主题名称,开发者可以自行配置默认的主题名称,如果不配置,就是 theme。
- 将解析出来的 theme 保存到 request 中,以备后续使用。
再来看 setThemeName 方法:
- 如果存在 themeName 就进行设置,同时将 themeName 添加到 Cookie 中。
- 如果不存在 themeName,就设置一个默认的主题名,同时从 response 中移除 Cookie。
可以看到,整个实现思路还是非常简单的。
2.2 AbstractThemeResolver
1 |
public abstract class AbstractThemeResolver implements ThemeResolver { |
AbstractThemeResolver 主要提供了配置默认主题的能力。
2.3 FixedThemeResolver
1 |
public class FixedThemeResolver extends AbstractThemeResolver { |
FixedThemeResolver 就是使用默认的主题名称,并且不允许修改主题。
2.4 SessionThemeResolver
1 |
public class SessionThemeResolver extends AbstractThemeResolver { |
- resolveThemeName:从 session 中取出主题名称并返回,如果 session 中的主题名称为 null,就返回默认的主题名称。
- setThemeName:将主题配置到请求中。
不想多说,因为很简单。
2.5 ThemeChangeInterceptor
最后我们再来看一看 ThemeChangeInterceptor 拦截器,这个拦截器会自动从请求中提取出主题参数,并设置到请求中,核心部分在 preHandle 方法中:
1 |
@Override |
从请求中提取出 theme 参数,并设置到 themeResolver 中。
3.小结
好啦,这就是今天和小伙伴们分享的一键换肤!无论是功能性还是源码,都和国际化非常类似,但是比国际化简单很多,不知道小伙伴们有没有 GET 到呢?
- Spring(403)
- Boot(208)
- Spring Boot(187)
- Java(82)
- Cloud(82)
- Spring Cloud(82)
- Security(60)
- Spring Security(54)
- Boot2(51)
- Spring Boot2(51)
- Redis(31)
- SQL(29)
- Mysql(25)
- IDE(24)
- Dalston(24)
- MVC(22)
- JDBC(22)
- IDEA(22)
- mongoDB(22)
- Web(21)
- CLI(20)
- SpringMVC(19)
- Alibaba(19)
- SpringBoot(17)
- Docker(17)
- Git(16)
- Eclipse(16)
- Vue(16)
- ORA(15)
- JPA(15)
- Apache(15)
- Mybatis(14)
- Oracle(14)
- jdk(14)
- Tomcat(14)
- Linux(14)
- HTTP(14)
- XML(13)
- JdbcTemplate(13)
- OAuth(13)
- Nacos(13)
- Pro(13)
- Data(12)
- JSON(12)
- OAuth2(12)
- stream(11)
- int(11)
- Myeclipse(11)
- Bug(10)
- not(10)
- ast(9)
- maven(9)
- Map(9)
- Hystrix(9)
- Swagger(8)
- APP(8)
- Bit(8)
- API(8)
- session(8)
- Window(8)
- HTML(7)
- Github(7)
- JavaMail(7)
- Cache(7)
- File(7)
- IntelliJ(7)
- mail(7)
- windows(7)
- too(7)
- RabbitMQ(6)
- and(6)
- star(6)
- Excel(6)
- Log4J(6)
- pushlet(6)
- apt(6)
- read(6)
- Freemarker(6)
- WebFlux(6)
- JSP(6)
- Bean(6)
- error(6)
- nginx(6)
- Server(6)
- jar(6)
- ueditor(6)
- ehcache(6)
- UDP(6)
- JWT(5)
- rdquo(5)
- PHP(5)
- Struts(5)
- string(5)
- Syntaxhighlighter(5)
- script(5)
- Tool(5)
- Controller(5)
- swagger2(5)
- ldquo(5)
- input(5)