1. 概述
在設計 Web 應用程序時,其外觀和感覺(主題)是一個關鍵組成部分。它會影響應用程序的可用性和可訪問性,並進一步確立公司的品牌形象。
在本教程中,我們將介紹配置 Spring MVC 應用程序中主題的步驟。
2. 使用場景
簡單來説,主題是一組靜態資源,通常包括樣式表和圖像,它們會影響我們 Web 應用程序的視覺風格。
我們可以使用主題來:
- 使用固定主題來建立一致的視覺風格
- 針對品牌進行定製,使用品牌主題——這在 SaaS 應用程序中很常見,每個客户都希望擁有不同的視覺風格
- 通過可用性主題來解決可訪問性問題——例如,我們可能需要一個深色主題或高對比度主題
3. Maven 依賴
首先,讓我們添加用於本教程第一部分所需的 Maven 依賴項。
我們需要 Spring WebMVC 和 Spring Context 依賴項:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>
由於我們將使用 JSP 在我們的示例中,因此我們需要 Java Servlet,JSP,以及 JSTL:
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>4. 配置 Spring 主題
4.1. 主題屬性
現在,讓我們為我們的應用程序配置淺色和深色主題。
對於深色主題,讓我們創建一個名為 dark.properties 的文件:
styleSheet=themes/black.css
background=black對於亮色主題,請參考 light.properties:
styleSheet=themes/white.css
background=white從以上屬性中可以看出,一個指向 CSS 文件,另一個指向 CSS 樣式。稍後我們將看到它們在視圖中是如何體現的。
4.2. ResourceHandler
閲讀以上配置信息,black.css 和 white.css 文件必須放置在名為 /themes 的目錄下。
並且,我們必須配置一個 ResourceHandler 以啓用 Spring MVC 正確地定位這些文件:
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/themes/**").addResourceLocations("classpath:/themes/");
}4.3. ThemeSource
我們可以使用 ResourceBundle 作為資源包,通過 ResourceBundleThemeSource 來管理這些主題特定的 .properties 文件。
@Bean
public ResourceBundleThemeSource resourceBundleThemeSource() {
return new ResourceBundleThemeSource();
}4.4. <em >ThemeResolver</em>
我們需要一個 <em >ThemeResolver</em> 來確定應用程序的主題。 根據我們的設計需求,我們可以選擇現有的實現方案或創建自己的方案。
對於我們的示例,讓我們配置 <em >CookieThemeResolver</em>。 如其名稱所示,它從瀏覽器 Cookie 中解析主題信息,或者在信息不可用時回退到默認主題:
@Bean
public ThemeResolver themeResolver() {
CookieThemeResolver themeResolver = new CookieThemeResolver();
themeResolver.setDefaultThemeName("light");
return themeResolver;
}框架中提供的 ThemeResolver 的其他變體包括:
- FixedThemeResolver:用於在應用程序中指定固定的主題
- SessionThemeResolver:用於允許用户在活動會話中切換主題
4.5. 視圖
為了將主題應用於我們的視圖,我們必須配置一個機制來查詢資源包。
我們將僅限於 JSP,儘管也可以配置類似的查找機制,用於替代視圖渲染引擎。
對於 JSP,我們可以導入一個標籤庫來完成這項工作:
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>然後,我們可以引用任何指定適當屬性名稱的屬性。
<link rel="stylesheet" href="<spring:theme code='styleSheet'/>"/>或者:
<body bgcolor="<spring:theme code='background'/>">現在,讓我們將一個名為 index.jsp的視圖添加到我們的應用程序中,並將其放置在 WEB-INF/目錄下:
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link rel="stylesheet" href="<spring:theme code='styleSheet'/>"/>
<title>Themed Application</title>
</head>
<body>
<header>
<h1>Themed Application</h1>
<hr />
</header>
<section>
<h2>Spring MVC Theme Demo</h2>
<form action="<c:url value='/'/>" method="POST" name="themeChangeForm" id="themeChangeForm">
<div>
<h4>
Change Theme
</h4>
</div>
<select id="theme" name="theme" onChange="submitForm()">
<option value="">Reset</option>
<option value="light">Light</option>
<option value="dark">Dark</option>
</select>
</form>
</section>
<script type="text/javascript">
function submitForm() {
document.themeChangeForm.submit();
}
</script>
</body>
</html>實際上,我們的應用程序在這個階段已經可以正常工作,始終選擇淺色主題。
讓我們看看如何允許用户更改主題。
4.6. ThemeChangeInterceptor
The job of the ThemeChangeInterceptor is to understand the theme change request.
Let’s now add a ThemeChangeInterceptor and configure it to look for a theme request parameter:
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(themeChangeInterceptor());
}
@Bean
public ThemeChangeInterceptor themeChangeInterceptor() {
ThemeChangeInterceptor interceptor = new ThemeChangeInterceptor();
interceptor.setParamName("theme");
return interceptor;
}5. 進一步依賴
接下來,我們實現自己的 ThemeResolver,該組件將用户的偏好存儲到數據庫中。
為了實現這一點,我們需要使用 Spring Security 來識別用户:
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>以及 Spring Data、Hibernate 和 HSQLDB 用於存儲用户的偏好設置:
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>2.2.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.4.9.Final</version>
</dependency>
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>2.5.0</version>
</dependency>
6. 自定義主題解析器 (ThemeResolver)
現在,讓我們更深入地瞭解主題解析器 (ThemeResolver) 並實現一個自定義的。這個自定義主題解析器 (ThemeResolver) 將保存用户的自定義主題偏好到數據庫中。
為了實現這一點,首先添加一個用户偏好實體 (UserPreference):
@Entity
@Table(name = "preferences")
public class UserPreference {
@Id
private String username;
private String theme;
}接下來,我們將創建 UserPreferenceThemeResolver,它必須實現 ThemeResolver 接口。 它的主要職責是解析和保存主題信息。
讓我們首先通過實現 UserPreferenceThemeResolver#resolveThemeName 來解決主題名稱:
@Override
public String resolveThemeName(HttpServletRequest request) {
String themeName = findThemeFromRequest(request)
.orElse(findUserPreferredTheme().orElse(getDefaultThemeName()));
request.setAttribute(THEME_REQUEST_ATTRIBUTE_NAME, themeName);
return themeName;
}
private Optional<String> findUserPreferredTheme() {
Authentication authentication = SecurityContextHolder.getContext()
.getAuthentication();
UserPreference userPreference = getUserPreference(authentication).orElse(new UserPreference());
return Optional.ofNullable(userPreference.getTheme());
}
private Optional<String> findThemeFromRequest(HttpServletRequest request) {
return Optional.ofNullable((String) request.getAttribute(THEME_REQUEST_ATTRIBUTE_NAME));
}
private Optional<UserPreference> getUserPreference(Authentication authentication) {
return isAuthenticated(authentication) ?
userPreferenceRepository.findById(((User) authentication.getPrincipal()).getUsername()) :
Optional.empty();
}現在我們可以為保存主題編寫我們的實現,在 UserPreferenceThemeResolver#setThemeName 中:
@Override
public void setThemeName(HttpServletRequest request, HttpServletResponse response, String theme) {
Authentication authentication = SecurityContextHolder.getContext()
.getAuthentication();
if (isAuthenticated(authentication)) {
request.setAttribute(THEME_REQUEST_ATTRIBUTE_NAME, theme);
UserPreference userPreference = getUserPreference(authentication).orElse(new UserPreference());
userPreference.setUsername(((User) authentication.getPrincipal()).getUsername());
userPreference.setTheme(StringUtils.hasText(theme) ? theme : null);
userPreferenceRepository.save(userPreference);
}
}最後,讓我們現在更改應用程序中的ThemeResolver:
@Bean
public ThemeResolver themeResolver() {
return new UserPreferenceThemeResolver();
}現在,用户的偏好主題已保存在數據庫中,而不是通過 cookie 保存。
另一種保存用户偏好的方式可以是使用 Spring MVC 控制器和單獨的 API。
7. 結論
在本文中,我們學習了配置 Spring MVC 主題的步驟。