Spring MVC主题

2023/05/19

1. 概述

在设计Web应用程序时,其外观或主题是一个关键组件。它会影响我们应用程序的可用性和可访问性,并可以进一步树立我们公司的品牌。

在本教程中,我们将完成在Spring MVC应用程序中配置主题所需的步骤。

2. 用例

简而言之,主题是一组静态资源,通常是样式表和图像,它们会影响我们的Web应用程序的视觉风格。

我们可以使用主题来:

  • 建立具有固定主题的通用外观
  • 为具有品牌主题的品牌定制,这在SAAS应用程序中很常见,每个客户都希望获得不同的外观和感觉
  • 使用可用性主题解决可访问性问题,例如,我们可能需要深色或高对比度主题

3. Maven依赖

所以,首先,让我们添加我们将在本教程的第一部分中使用的Maven依赖项。

我们需要Spring WebMVCSpring 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 ServletsJSPJSTL

<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

我们可以通过ResourceBundleThemeSource将这些特定于主题的.properties文件作为ResourceBundle进行管理:

@Bean
public ResourceBundleThemeSource resourceBundleThemeSource() {
    return new ResourceBundleThemeSource();
}

4.4 ThemeResolver

接下来,我们需要一个ThemeResolver来解析应用程序的正确主题。根据我们的设计需求,我们可以在现有实现之间进行选择或创建我们自己的实现。

对于我们的示例,让我们配置CookieThemeResolver。顾名思义,这会从浏览器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

ThemeChangeInterceptor的工作是了解主题更改请求。

现在让我们添加一个ThemeChangeInterceptor并将其配置为查找主题请求参数:

@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 DataHibernateHSQLDB

<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主题的步骤。

与往常一样,本教程的完整源代码可在GitHub上获得。

Show Disqus Comments

Post Directory

扫码关注公众号:Taketoday
发送 290992
即可立即永久解锁本站全部文章