【Spring】ThemeResolver主题解析器源码分析
ThemeResolver 主题解析器
作用:相同页面切换不同样式显示,类似换主页皮肤操作
工作原理:本质和LocaleResolver无区别,都是将数据保存在Session、Cookie等位置且与请求绑定,实现JSP页面数据动态化处理
1. jsp 案例代码
主题资源文件配置
/** * function: 主题配置
* author: zhiwei_yang
* time: 2020/6/21-23:50
*/
@Configuration
public class ThemeConfig implements WebMvcConfigurer {
/**
* 配置主题资源文件: theme. 表示主题资源配置文件: classpath/theme 目录下
*
* bean名称固定:themeSource
* org.springframework.web.context.support.GenericWebApplicationContext#onRefresh()
* org.springframework.ui.context.support.UiApplicationContextUtils#initThemeSource
*
* @return
*/
@Bean("themeSource")
public ResourceBundleThemeSource resourceBundleThemeSource(){
ResourceBundleThemeSource resourceBundleThemeSource = new ResourceBundleThemeSource();
resourceBundleThemeSource.setBasenamePrefix("theme.");
return resourceBundleThemeSource;
}
/**
* 主题解析器
* FixedThemeResolver: 固定主题名,不能变更
* SessionThemeResolver:主题信息保存Session
* CookieThemeResolver: 主题信息保存Cookie
*
* @return
*/
@Bean
public ThemeResolver themeResolver(){
return new SessionThemeResolver();
}
/**
* 主题变更拦截器,设置请求对应主题
* @return
*/
@Bean
public ThemeChangeInterceptor themeChangeInterceptor(){
return new ThemeChangeInterceptor();
}
/**
* 注入拦截器
* @param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry){
registry.addInterceptor(themeChangeInterceptor());
}
}
主题资源文件:
## 简单指定主题样式文件访问路径blue.properties:
theme=/theme/blue/css/theme.css
red.proeprties:
theme=/theme/red/css/theme.css
主题样式文件:指定主题不同样式
theme.css(blue):div{
background-color: blue
}
theme.css(red):
div{
background-color: red
}
主题JSP样例页面:
<%@ page language="java" contentType="text/html;charset=utf-8" pageEncoding="utf-8" %><%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<html>
<head>
<title>spring theme</title>
<link rel="stylesheet" type="text/css" href="<spring:theme code="theme"/>"/>
</head>
<body>
<div id="divTheme">
<h1><spring:message code="page.description"/></h1>
</div>
<a href="${pageContext.request.contextPath}/theme?themeName=blue"> blue</a>
<a href="${pageContext.request.contextPath}/theme?themeName=red"> red</a>
</body>
</html>
主题控制器:
/** * 主题控制器
*/
@Controller
@RequestMapping("/theme")
@Slf4j
public class ThemeController {
@Autowired
private ThemeResolver themeResolver;
@GetMapping
public String theme(HttpServletRequest request, HttpServletResponse response, String themeName) {
//设置默认主题
if(themeName == null){
themeName = "red";
themeResolver.setThemeName(request, response, themeName);
}
log.info("current theme change to {}", themeName);
return "theme";
}
}
项目结构图:注意静态资源文件存放目录
项目效果图:
2. 工作原理
2.1 请求属性绑定主题解析器、主题配置资源
源码:org.springframework.web.servlet.DispatcherServlet.doService
## 这里我们配置SessionThemeResolver, Session存储主题信息,默认FixedThemeResolver不支持自定义主题request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
## 设置主题配置资源:ResourceBundleThemeSource baseName=theme.
request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
2.2 请求主题解析
源码:org.springframework.web.servlet.theme.ThemeChangeInterceptor.preHandle
// 默认从请求参数theme获取主题名,可通过ThemeChangeInterceptor 配置paramName修改默认参数名String newTheme = request.getParameter(this.paramName);
if (newTheme != null) {
ThemeResolver themeResolver = RequestContextUtils.getThemeResolver(request);
if (themeResolver == null) {
throw new IllegalStateException("No ThemeResolver found: not in a DispatcherServlet request?");
}
// 设置当前请求主题名
themeResolver.setThemeName(request, response, newTheme);
}
// Proceed in any case.
return true;
2.3 设置当前请求主题名
源码:org.springframework.web.servlet.theme.SessionThemeResolver.resolveThemeName
@Overridepublic String resolveThemeName(HttpServletRequest request) {
// 将新主题名存储在Session
String themeName = (String) WebUtils.getSessionAttribute(request, THEME_SESSION_ATTRIBUTE_NAME);
// A specific theme indicated, or do we need to fallback to the default?
return (themeName != null ? themeName : getDefaultThemeName());
}
2.4 spring:theme 标签解析
实体类:ThemeTag
分析:ThemeTag继承MessageTag,只是修改自身的主题配置资源路径,DispatcherServlet提前存储在请求属性中
public class ThemeTag extends MessageTag { /**
* Use the theme MessageSource for theme message resolution.
*/
@Override
protected MessageSource getMessageSource() {
// 获取主题资源:本质从Request属性获取,底层逻辑:ResourceBundleThemeSource.getTheme
return getRequestContext().getTheme().getMessageSource();
}
/**
* Return exception message that indicates the current theme.
*/
@Override
protected String getNoSuchMessageExceptionDescription(NoSuchMessageException ex) {
return "Theme "" + getRequestContext().getTheme().getName() + "": " + ex.getMessage();
}
}
2.4.1 主题资源获取
org.springframework.ui.context.support.ResourceBundleThemeSource.getTheme
@Override public Theme getTheme(String themeName) {
if (themeName == null) {
return null;
}
Theme theme = this.themeCache.get(themeName);
if (theme == null) {
synchronized (this.themeCache) {
theme = this.themeCache.get(themeName);
if (theme == null) {
// 提前配置ResourceBundleThemeSource basenamePrefix=theme
// 若主题名称为red: basename = theme.red,资源文件为classpath/theme/red*.properties
String basename = this.basenamePrefix + themeName;
MessageSource messageSource = createMessageSource(basename);
theme = new SimpleTheme(themeName, messageSource);
initParent(theme);
this.themeCache.put(themeName, theme);
if (logger.isDebugEnabled()) {
logger.debug("Theme created: name "" + themeName + "", basename [" + basename + "]");
}
}
}
}
return theme;
}
2.4.2 标签数据解析展示
因ThemeTag是MessageTag子类,只是修改配置数据源,详细解析可以参考博客LocaleResolver、MessageSources实现国际化显示源码分析
解析后前端源码:
红色主题:
<html><head>
<title>spring theme</title>
<link rel="stylesheet" type="text/css" href="/theme/red/css/theme.css"/>
</head>
<body>
<div id="divTheme">
<h1>页面语言</h1>
</div>
<a href="/theme?themeName=blue"> blue</a>
<a href="/theme?themeName=red"> red</a>
</body>
</html>
蓝色主题:
<html><head>
<title>spring theme</title>
<link rel="stylesheet" type="text/css" href="/theme/blue/css/theme.css"/>
</head>
<body>
<div id="divTheme">
<h1>页面语言</h1>
</div>
<a href="/theme?themeName=blue"> blue</a>
<a href="/theme?themeName=red"> red</a>
</body>
</html>
以上是 【Spring】ThemeResolver主题解析器源码分析 的全部内容, 来源链接: utcz.com/z/517695.html