【Spring】ThemeResolver主题解析器源码分析

﹥>﹥吖頭↗ 提交于 2020-08-07 19:29:27

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

@Override
public 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>
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!