<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Spring Boot一鍵換膚,so easy!

          共 9876字,需瀏覽 20分鐘

           ·

          2021-07-05 17:05

          松哥原創(chuàng)的 Spring Boot 視頻教程已經(jīng)殺青,感興趣的小伙伴戳這里-->Spring Boot+Vue+微人事視頻教程


          SpringMVC 源碼分析系列最后一篇,和大家聊一聊 Theme。

          Theme,就是主題,點(diǎn)一下就給網(wǎng)站更換一個(gè)主題,相信大家都用過(guò)類(lèi)似功能,這個(gè)其實(shí)和前面所說(shuō)的國(guó)際化功能很像,代碼其實(shí)也很像,今天我們就來(lái)捋一捋。

          考慮到有的小伙伴可能還沒(méi)用過(guò) Theme,所以這里松哥先來(lái)說(shuō)下用法,然后我們?cè)龠M(jìn)行源碼分析。

          1.一鍵換膚

          來(lái)做一個(gè)簡(jiǎn)單的需求,假設(shè)我的頁(yè)面上有三個(gè)按鈕,點(diǎn)擊之后就能一鍵換膚,像下面這樣:

          我們來(lái)看下這個(gè)需求怎么實(shí)現(xiàn)。

          首先三個(gè)按鈕分別對(duì)應(yīng)了三個(gè)不同的樣式,我們先把這三個(gè)不同的樣式定義出來(lái),分別如下:

          blue.css:

          body{
              background-color#05e1ff;
          }

          green.css:

          body{
              background-color#aaff9c;
          }

          red.css:

          body{
              background-color#ff0721;
          }

          主題的定義,往往是一組樣式,因此我們一般都是在一個(gè) properties 文件中將同一主題的樣式配置在一起,這樣方便后期加載。

          所以接下來(lái)我們?cè)?resources 目錄下新建 theme 目錄,然后在 theme 目錄中創(chuàng)建三個(gè)文件,內(nèi)容如下:

          blue.properties:

          index.body=/css/blue.css

          green.properties:

          index.body=/css/green.css

          red.properties:

          index.body=/css/red.css

          在不同的 properties 配置文件中引入不同的樣式,但是樣式定義的 key 都是 index.body,這樣方便后期在頁(yè)面中引入。

          接下來(lái)在 SpringMVC 容器中配置三個(gè) Bean,如下:

          <mvc:interceptors>
              <mvc:interceptor>
                  <mvc:mapping path="/**"/>
                  <bean class="org.springframework.web.servlet.theme.ThemeChangeInterceptor">
                      <property name="paramName" value="theme"/>
                  </bean>
              </mvc:interceptor>
          </mvc:interceptors>
          <bean id="themeSource" class="org.springframework.ui.context.support.ResourceBundleThemeSource">
              <property name="basenamePrefix" value="theme."/>
          </bean>
          <bean id="themeResolver" class="org.springframework.web.servlet.theme.SessionThemeResolver">
              <property name="defaultThemeName" value="blue"/>
          </bean>
          1. 首先配置攔截器 ThemeChangeInterceptor,這個(gè)攔截器用來(lái)解析主題參數(shù),參數(shù)的 key 為 theme,例如請(qǐng)求地址是 /index?theme=blue,該攔截器就會(huì)自動(dòng)設(shè)置系統(tǒng)主題為 blue。當(dāng)然也可以不配置攔截器,如果不配置的話,就可以單獨(dú)提供一個(gè)修改主題的接口,然后手動(dòng)修改主題,類(lèi)似下面這樣:
          @Autowired
          private ThemeResolver themeResolver;
          @RequestMapping(path = "/01/{theme}",method = RequestMethod.GET)
          public String theme1(@PathVariable("theme") String themeStr, HttpServletRequest request, HttpServletResponse response){
              themeResolver.setThemeName(request,response, themeStr);
              return "redirect:/01";
          }

          themeStr 就是新的主題名稱(chēng),將其配置給 themeResolver 即可。

          1. 接下來(lái)配置 ResourceBundleThemeSource,這個(gè) Bean 主要是為了加載主題文件,需要配置一個(gè) basenamePrefix 屬性,如果我們的主題文件放在文件夾中,這個(gè) basenamePrefix 的值就是 文件夾名稱(chēng).
          2. 接下來(lái)配置主題解析器,主題解析器有三種,分別是 CookieThemeResolver、FixedThemeResolver、SessionThemeResolver,這里我們使用的是 SessionThemeResolver,主題信息將被保存在 Session 中,只要 Session 不變,主題就一直有效。這三個(gè)主題解析器松哥會(huì)在下一小節(jié)中和大家仔細(xì)分析。

          配置完成后,我們?cè)賮?lái)提供一個(gè)測(cè)試頁(yè)面,如下:

          <%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
          <%@ page contentType="text/html;charset=UTF-8" language="java" %>
          <html>
          <head>
              <title>Title</title>
              <link rel="stylesheet" href="<spring:theme code="index.body" />" >
          </head>
          <body>
          <div>
              一鍵切換主題:<br/>
              <a href="/index?theme=blue">托帕藍(lán)</a>
              <a href="/index?theme=red">多巴胺紅</a>
              <a href="/index?theme=green">石竹青</a>
          </div>
          <br/>
          </body>
          </html>

          最關(guān)鍵的是:

          <link rel="stylesheet" href="<spring:theme code="index.body" />" >

          css 樣式不直接寫(xiě),而是引用我們?cè)?properties 文件中定義的 index.body,這樣將根據(jù)當(dāng)前主題加載不同的 css 文件。

          最后再提供一個(gè)處理器,如下:

          @GetMapping(path = "/index")
          public  String getPage(){
              return "index";
          }

          這個(gè)就很簡(jiǎn)單了,沒(méi)啥好說(shuō)的。

          最后啟動(dòng)項(xiàng)目進(jìn)行測(cè)試,大家就可以看到我們文章一開(kāi)始給出的圖片了,點(diǎn)擊不同的按鈕就可以實(shí)現(xiàn)背景的切換。

          是不是非常 Easy!

          2.原理分析

          主題這塊涉及到的東西主要就是主題解析器,主題解析器和我們前面所說(shuō)的國(guó)際化的解析器非常類(lèi)似,但是比它更簡(jiǎn)單,我們一起來(lái)分析下。

          先來(lái)看下 ThemeResolver 接口:

          public interface ThemeResolver {
           String resolveThemeName(HttpServletRequest request);
           void setThemeName(HttpServletRequest request, @Nullable HttpServletResponse response, @Nullable String themeName);
          }

          這個(gè)接口中就兩個(gè)方法:

          1. resolveThemeName:從當(dāng)前請(qǐng)求中解析出主題的名字。
          2. setThemeName:設(shè)置當(dāng)前主題。

          ThemeResolver 主要有三個(gè)實(shí)現(xiàn)類(lèi),繼承關(guān)系如下:

          接下來(lái)我們對(duì)這幾個(gè)實(shí)現(xiàn)類(lèi)來(lái)逐個(gè)分析。

          2.1 CookieThemeResolver

          直接上源碼吧:

          @Override
          public String resolveThemeName(HttpServletRequest request) {
           String themeName = (String) request.getAttribute(THEME_REQUEST_ATTRIBUTE_NAME);
           if (themeName != null) {
            return themeName;
           }
           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;
             }
            }
           }
           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) {
           if (StringUtils.hasText(themeName)) {
            request.setAttribute(THEME_REQUEST_ATTRIBUTE_NAME, themeName);
            addCookie(response, themeName);
           } else {
            request.setAttribute(THEME_REQUEST_ATTRIBUTE_NAME, getDefaultThemeName());
            removeCookie(response);
           }
          }

          先來(lái)看 resolveThemeName 方法:

          1. 首先會(huì)嘗試直接從請(qǐng)求中獲取主題名稱(chēng),如果獲取到了,就直接返回。
          2. 如果第一步?jīng)]有獲取到主題名稱(chēng),接下來(lái)就嘗試從 Cookie 中獲取主題名稱(chēng),Cookie 也是從當(dāng)前請(qǐng)求中提取,利用 WebUtils 工具進(jìn)行解析,如果解析到了主題名稱(chēng),就賦值給 themeName 變量。
          3. 如果前面沒(méi)有獲取到主題名稱(chēng),就使用默認(rèn)的主題名稱(chēng),開(kāi)發(fā)者可以自行配置默認(rèn)的主題名稱(chēng),如果不配置,就是 theme。
          4. 將解析出來(lái)的 theme 保存到 request 中,以備后續(xù)使用。

          再來(lái)看 setThemeName 方法:

          1. 如果存在 themeName 就進(jìn)行設(shè)置,同時(shí)將 themeName 添加到 Cookie 中。
          2. 如果不存在 themeName,就設(shè)置一個(gè)默認(rèn)的主題名,同時(shí)從 response 中移除 Cookie。

          可以看到,整個(gè)實(shí)現(xiàn)思路還是非常簡(jiǎn)單的。

          2.2 AbstractThemeResolver

          public abstract class AbstractThemeResolver implements ThemeResolver {
           public static final String ORIGINAL_DEFAULT_THEME_NAME = "theme";
           private String defaultThemeName = ORIGINAL_DEFAULT_THEME_NAME;
           public void setDefaultThemeName(String defaultThemeName) {
            this.defaultThemeName = defaultThemeName;
           }
           public String getDefaultThemeName() {
            return this.defaultThemeName;
           }
          }

          AbstractThemeResolver 主要提供了配置默認(rèn)主題的能力。

          2.3 FixedThemeResolver

          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");
           }

          }

          FixedThemeResolver 就是使用默認(rèn)的主題名稱(chēng),并且不允許修改主題。

          2.4 SessionThemeResolver

          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);
            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));
           }
          }
          • resolveThemeName:從 session 中取出主題名稱(chēng)并返回,如果 session 中的主題名稱(chēng)為 null,就返回默認(rèn)的主題名稱(chēng)。
          • setThemeName:將主題配置到請(qǐng)求中。

          不想多說(shuō),因?yàn)楹芎?jiǎn)單。

          2.5 ThemeChangeInterceptor

          最后我們?cè)賮?lái)看一看 ThemeChangeInterceptor 攔截器,這個(gè)攔截器會(huì)自動(dòng)從請(qǐng)求中提取出主題參數(shù),并設(shè)置到請(qǐng)求中,核心部分在 preHandle 方法中:

          @Override
          public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws ServletException 
          {
           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);
           }
           return true;
          }

          從請(qǐng)求中提取出 theme 參數(shù),并設(shè)置到 themeResolver 中。

          3.小結(jié)

          好啦,這就是今天和小伙伴們分享的一鍵換膚!無(wú)論是功能性還是源碼,都和國(guó)際化非常類(lèi)似,但是比國(guó)際化簡(jiǎn)單很多,不知道小伙伴們有沒(méi)有 GET 到呢?

          瀏覽 53
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  香港三级电影麻豆 | 一卡二卡国产在线 | 91黄色一级电影 | 一區二區三區色 | 亚洲av免费在线 亚洲免费观看在线 |