springMVC源码简读——2.7 ThemeResolver 和 LocaleResolver

    xiaoxiao2025-08-13  5

    ThemeResolver

    概述

    提供动态样式的业务逻辑,这个以前用的就不多,现在前后端分离的趋势下更是少见。

    类图

    接口

    public interface ThemeResolver { /** * 从请求中,解析出使用的主题。例如,从请求头 User-Agent ,判断使用 PC 端,还是移动端的主题 */ String resolveThemeName(HttpServletRequest request); /** * 设置请求,所使用的主题。 */ void setThemeName(HttpServletRequest request, @Nullable HttpServletResponse response, @Nullable String themeName); }

    初始化

    它的初始化逻辑和上一篇组件一样,优先从容器中获取,假如抛出异常则在catch中获得默认值

    private void initThemeResolver(ApplicationContext context) { try { // 容器中获得指定bean this.themeResolver = context.getBean(THEME_RESOLVER_BEAN_NAME, ThemeResolver.class); if (logger.isTraceEnabled()) { logger.trace("Detected " + this.themeResolver); } else if (logger.isDebugEnabled()) { logger.debug("Detected " + this.themeResolver.getClass().getSimpleName()); } } catch (NoSuchBeanDefinitionException ex) { // We need to use the default. // 获得默认的解析器 this.themeResolver = getDefaultStrategy(context, ThemeResolver.class); if (logger.isTraceEnabled()) { logger.trace("No ThemeResolver '" + THEME_RESOLVER_BEAN_NAME + "': using default [" + this.themeResolver.getClass().getSimpleName() + "]"); } } }

    实现类

    AbstractThemeResolver

    实现最基础的样式处理

    AbstractThemeResolver.java public abstract class AbstractThemeResolver implements ThemeResolver { /** * Out-of-the-box value for the default theme name: "theme". */ public static final String ORIGINAL_DEFAULT_THEME_NAME = "theme"; private String defaultThemeName = ORIGINAL_DEFAULT_THEME_NAME; /** * Set the name of the default theme. * Out-of-the-box value is "theme". */ public void setDefaultThemeName(String defaultThemeName) { this.defaultThemeName = defaultThemeName; } /** * Return the name of the default theme. */ public String getDefaultThemeName() { return this.defaultThemeName; } }

    设置了两个默认的值

    SessionThemeResolver

    就是将themeName 保存到 Session 中

    SessionThemeResolver.java public class SessionThemeResolver extends AbstractThemeResolver { public static final String THEME_SESSION_ATTRIBUTE_NAME = SessionThemeResolver.class.getName() + ".THEME"; @Override public String resolveThemeName(HttpServletRequest request) { 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()); } @Override public void setThemeName( HttpServletRequest request, @Nullable HttpServletResponse response, @Nullable String themeName) { WebUtils.setSessionAttribute(request, THEME_SESSION_ATTRIBUTE_NAME, (StringUtils.hasText(themeName) ? themeName : null)); } }

    和上面的不同之处,只是将值保存到session中了

    FixedThemeResolver

    实现 FixedThemeResolver 中没有具体的实现操作。

    FixedThemeResolver .java public class FixedThemeResolver extends AbstractThemeResolver { @Override public String resolveThemeName(HttpServletRequest request) { return getDefaultThemeName(); } @Override public void setThemeName( HttpServletRequest request, @Nullable HttpServletResponse response, @Nullable String themeName) { throw new UnsupportedOperationException("Cannot change theme - use a different theme resolution strategy"); } }

    CookieThemeResolver

    就是将themeName 保存到 Cookie 中

    CookieThemeResolver.java @Override public String resolveThemeName(HttpServletRequest request) { // Check request for preparsed or preset theme. // 假如从请求的参数中获得themeName直接返回 String themeName = (String) request.getAttribute(THEME_REQUEST_ATTRIBUTE_NAME); if (themeName != null) { return themeName; } // 或者从cookie中获取 // Retrieve cookie value from request. String cookieName = getCookieName(); if (cookieName != null) { Cookie cookie = WebUtils.getCookie(request, cookieName); if (cookie != null) { String value = cookie.getValue(); if (StringUtils.hasText(value)) { themeName = value; } } } // 假如没有,则设置默认值 // Fall back to default theme. if (themeName == null) { themeName = getDefaultThemeName(); } request.setAttribute(THEME_REQUEST_ATTRIBUTE_NAME, themeName); return themeName; } @Override public void setThemeName( HttpServletRequest request, @Nullable HttpServletResponse response, @Nullable String themeName) { Assert.notNull(response, "HttpServletResponse is required for CookieThemeResolver"); // 假如有值进行保存,添加至cookie中 if (StringUtils.hasText(themeName)) { // Set request attribute and add cookie. request.setAttribute(THEME_REQUEST_ATTRIBUTE_NAME, themeName); addCookie(response, themeName); } // 假如没值删除cookie else { // Set request attribute to fallback theme and remove cookie. request.setAttribute(THEME_REQUEST_ATTRIBUTE_NAME, getDefaultThemeName()); removeCookie(response); } }

    LocaleResolver

    概述

    LocaleResolver 是一个解决国际化的策略接口

    类图

    接口

    public interface LocaleResolver { /** * 从请求中,解析出要使用的语言。例如,请求头的 "Accept-Language" */ Locale resolveLocale(HttpServletRequest request); /** * 设置请求所使用的语言 */ void setLocale(HttpServletRequest request, @Nullable HttpServletResponse response, @Nullable Locale locale); }

    初始化

    这个不说了,和上面一样的套路

    private void initLocaleResolver(ApplicationContext context) { try { this.localeResolver = context.getBean(LOCALE_RESOLVER_BEAN_NAME, LocaleResolver.class); if (logger.isTraceEnabled()) { logger.trace("Detected " + this.localeResolver); } else if (logger.isDebugEnabled()) { logger.debug("Detected " + this.localeResolver.getClass().getSimpleName()); } } catch (NoSuchBeanDefinitionException ex) { // We need to use the default. this.localeResolver = getDefaultStrategy(context, LocaleResolver.class); if (logger.isTraceEnabled()) { logger.trace("No LocaleResolver '" + LOCALE_RESOLVER_BEAN_NAME + "': using default [" + this.localeResolver.getClass().getSimpleName() + "]"); } } }

    实现类

    AcceptHeaderLocaleResolver

    SpringMVC 默认使用,简单地使用 HTTP 请求头里的 Accept-Language 来指定 Locale对象

    resolveLocale

    解析本地化对象

    AcceptHeaderLocaleResolver .java @Override public Locale resolveLocale(HttpServletRequest request) { // 返回默认配置的语言环境(如果有) Locale defaultLocale = getDefaultLocale(); // 假如存在默认语言环境且头信息中Accept-Language缺失,则直接返回默认 if (defaultLocale != null && request.getHeader("Accept-Language") == null) { return defaultLocale; } // 获得请求中的本地化对象,和所有可支持的本地化对象比较 Locale requestLocale = request.getLocale(); List<Locale> supportedLocales = getSupportedLocales(); if (supportedLocales.isEmpty() || supportedLocales.contains(requestLocale)) { return requestLocale; } // Locale supportedLocale = findSupportedLocale(request, supportedLocales); if (supportedLocale != null) { return supportedLocale; } return (defaultLocale != null ? defaultLocale : requestLocale); } @Nullable private Locale findSupportedLocale(HttpServletRequest request, List<Locale> supportedLocales) { // 获得请求中本地化对象,返回一个枚举集合 Enumeration<Locale> requestLocales = request.getLocales(); Locale languageMatch = null; while (requestLocales.hasMoreElements()) { Locale locale = requestLocales.nextElement(); // 匹配则返回 if (supportedLocales.contains(locale)) { if (languageMatch == null || languageMatch.getLanguage().equals(locale.getLanguage())) { // Full match: language + country, possibly narrowed from earlier language-only match return locale; } } // 这个没怎么看明白? 按照上面的逻辑这个判断应该是无法进入的 else if (languageMatch == null) { // Let's try to find a language-only match as a fallback for (Locale candidate : supportedLocales) { if (!StringUtils.hasLength(candidate.getCountry()) && candidate.getLanguage().equals(locale.getLanguage())) { languageMatch = candidate; break; } } } } return languageMatch; }

    这个我看其他版本的没有下面的逻辑,暂时还不清楚加这一段的作用

    setLocale

    一个未实现的方法

    AcceptHeaderLocaleResolver.java @Override public void setLocale(HttpServletRequest request, @Nullable HttpServletResponse response, @Nullable Locale locale) { throw new UnsupportedOperationException( "Cannot change HTTP accept header - use a different locale resolution strategy"); }

    AbstractLocaleResolver

    LocaleResolver 抽象实现的基类,都返回一个默认值

    LocaleResolver.java public abstract class AbstractLocaleResolver implements LocaleResolver { @Nullable private Locale defaultLocale; /** * Set a default Locale that this resolver will return if no other locale found. */ public void setDefaultLocale(@Nullable Locale defaultLocale) { this.defaultLocale = defaultLocale; } /** * Return the default Locale that this resolver is supposed to fall back to, if any. */ @Nullable protected Locale getDefaultLocale() { return this.defaultLocale; } }

    LocaleContextResolver

    LocaleResolver 接口的扩展,扩展了两个方法,主要是LocaleContext的设置和解析获取

    LocaleContextResolver.java public interface LocaleContextResolver extends LocaleResolver { /** */ LocaleContext resolveLocaleContext(HttpServletRequest request); /** */ void setLocaleContext(HttpServletRequest request, @Nullable HttpServletResponse response, @Nullable LocaleContext localeContext); }

    AbstractLocaleContextResolver

    LocaleContextResolver 实现的抽象基类:提供对默认语言环境和默认时区的支持

    AbstractLocaleContextResolver .java /** * Set a default TimeZone that this resolver will return if no other time zone found. */ public void setDefaultTimeZone(@Nullable TimeZone defaultTimeZone) { this.defaultTimeZone = defaultTimeZone; } /** * Return the default TimeZone that this resolver is supposed to fall back to, if any. */ @Nullable public TimeZone getDefaultTimeZone() { return this.defaultTimeZone; }

    补充了时区设置,

    SessionLocaleResolver

    主要是将本地化的东西保存在会话中,存取都是在会话中进行

    SessionLocaleResolver .java @Override public Locale resolveLocale(HttpServletRequest request) { Locale locale = (Locale) WebUtils.getSessionAttribute(request, this.localeAttributeName); if (locale == null) { locale = determineDefaultLocale(request); } return locale; } @Override public LocaleContext resolveLocaleContext(final HttpServletRequest request) { return new TimeZoneAwareLocaleContext() { @Override public Locale getLocale() { // 从session获取指定参数 Locale locale = (Locale) WebUtils.getSessionAttribute(request, localeAttributeName); if (locale == null) { locale = determineDefaultLocale(request); } return locale; } @Override @Nullable public TimeZone getTimeZone() { TimeZone timeZone = (TimeZone) WebUtils.getSessionAttribute(request, timeZoneAttributeName); if (timeZone == null) { timeZone = determineDefaultTimeZone(request); } return timeZone; } }; } @Override public void setLocaleContext(HttpServletRequest request, @Nullable HttpServletResponse response, @Nullable LocaleContext localeContext) { Locale locale = null; TimeZone timeZone = null; if (localeContext != null) { locale = localeContext.getLocale(); if (localeContext instanceof TimeZoneAwareLocaleContext) { timeZone = ((TimeZoneAwareLocaleContext) localeContext).getTimeZone(); } } // 设置参数到session中 WebUtils.setSessionAttribute(request, this.localeAttributeName, locale); WebUtils.setSessionAttribute(request, this.timeZoneAttributeName, timeZone); }

    FixedLocaleResolver 因为并没有实现左右方法所以就不说了

    总结

    无论是ThemeResolver还是LocaleResolver。样式和国际化平时我们开发的时候很少接触这些东西。逻辑也不算多么的绕。可以做简单的了解了解。

    最新回复(0)