社区微信群开通啦,扫一扫抢先加入社区官方微信群
社区微信群
Spring Web MVC是构建在Servlet API上的原始Web框架,从一开始就包含在Spring Framework中。正式名称“Spring Web MVC”来自其源模块(spring-webmvc)的名称,但它通常被称为“Spring MVC”。
与Spring Web MVC并行,Spring Framework 5.0引入了一个反应堆栈Web框架,其名称“Spring WebFlux”也基于其源模块(spring-webflux)。本节介绍Spring Web MVC。在下一节 介绍spring WebFlux。
有关基本信息以及与Servlet容器和Java EE版本范围的兼容性,请参阅Spring Framework Wiki。
与其他许多Web框架一样,SpringMVC是围绕前控制器模式设计的,在前控制器模式中,中央servlet(DispatcherServlet)为请求处理提供共享算法,而实际工作则由可配置的委托组件执行。该模型灵活,支持多种工作流。
DispatcherServlet作为一个servlet,需要通过使用Java配置或Web.xml来根据servlet规范声明和映射。反过来,DispatcherServlet使用Spring配置来发现请求映射、视图解析、异常处理等所需的代理组件。
下面的Java配置示例注册并初始化Deservlet Servlet,该servlet由servlet容器自动检测(参见Servlet Config):
public class MyWebApplicationInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletCxt) {
// Load Spring web application configuration
AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext();
ac.register(AppConfig.class);
ac.refresh();
// Create and register the DispatcherServlet
DispatcherServlet servlet = new DispatcherServlet(ac);
ServletRegistration.Dynamic registration = servletCxt.addServlet("app", servlet);
registration.setLoadOnStartup(1);
registration.addMapping("/app/*");
}
}
除了直接使用ServletContext API之外,您还可以扩展 AbstractAnnotationConfigDispatcherServletInitializer和覆盖特定方法(请参阅Context Hierarchy下的示例)。
以下web.xml配置示例注册并初始化DispatcherServlet:
<web-app>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/app-context.xml</param-value>
</context-param>
<servlet>
<servlet-name>app</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value></param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>app</servlet-name>
<url-pattern>/app/*</url-pattern>
</servlet-mapping>
</web-app>
Spring Boot遵循不同的初始化顺序。Spring Boot使用Spring配置来引导自身和嵌入式Servlet容器,而不是挂钩到Servlet容器的生命周期。Filter和Servlet声明在Spring配置中检测到并在Servlet容器中注册。有关更多详细信息,请参阅 Spring Boot文档。
上下文层次结构
DispatcherServlet需要一个WebApplicationContext(纯ApplicationContext的扩展)来进行自己的配置。WebApplicationContext具有指向servletContext及其关联的servlet的链接。它还绑定到servletcontext,这样应用程序可以在requestContextUtils上使用静态方法在需要访问WebApplicationContext时查找它。
对于许多应用程序,拥有一个WebApplicationContext是简单且足够的。还可以有一个上下文层次结构,其中一个根WebApplicationContext在多个DispatcherServlet(或其他servlet)实例之间共享,每个实例都有自己的子WebApplicationContext配置。有关上下文层次结构功能的更多信息,请参阅ApplicationContext的其他功能。
webapplicationContext通常包含基础架构bean,例如需要跨多个servlet实例共享的数据存储库和业务服务。这些bean是有效继承的,可以在servlet特定的子webapplicationContext中重写(即重新声明),后者通常包含给定servlet的本地bean。下图显示了这种关系:
以下示例配置WebApplicationContext层次结构:
public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] { RootConfig.class };
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[] { App1Config.class };
}
@Override
protected String[] getServletMappings() {
return new String[] { "/app1/*" };
}
}
如果不需要应用程序上下文层次结构,则应用程序可以通过getrootconfigclasss()返回所有配置,并从getservletconfigclasss()返回空值。
以下示例显示了web.xml等效项:
<web-app>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/root-context.xml</param-value>
</context-param>
<servlet>
<servlet-name>app1</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/app1-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>app1</servlet-name>
<url-pattern>/app1/*</url-pattern>
</servlet-mapping>
</web-app>
如果不需要应用程序上下文层次结构,则应用程序只能配置“root”上下文,并将ContextConfigLocation servlet参数保留为空。
DispatcherServlet委托给特殊bean处理请求并提供适当的响应。“特殊bean”是指实现WebFlux框架契约的Spring管理对象实例。这些通常带有内置契约,但您可以自定义它们的属性并扩展或替换它们。
下表列出了DispatcherHandler检测到的特殊bean:
Bean type | Explanation |
---|---|
HandlerMapping | 将请求与拦截器列表一起映射到处理程序,以便进行预处理和后处理。映射基于某些标准,其细节因handlerMapping实现而异。 两个主要的handlerMapping实现是RequestMappingHandlerMapping(支持@RequestMapping注释方法)和SimpleUrlHandlerMapping(维护对处理程序的URI路径模式的显式注册)。 |
HandlerAdapter | 帮助DispatcherServlet调用映射到请求的处理程序,不管实际如何调用该处理程序。例如,调用带注释的控制器需要解析注释。handlerAdapter的主要目的是保护DispatcherServlet不受此类细节的影响。 |
HandlerExceptionResolver | 解决异常的策略,可能将异常映射到处理程序、HTML错误视图或其他目标。参见异常。 |
ViewResolver | 将从处理程序返回的基于逻辑字符串的视图名称解析为要用其呈现给响应的实际视图。请参见视图分辨率和视图技术。 |
LocaleResolver, LocaleContextResolver | 解析客户端正在使用的区域设置,并可能解析其时区,以便能够提供国际化的视图。查看区域设置。 |
ThemeResolver | 解决Web应用程序可以使用的主题,例如,提供个性化布局。参见主题。 |
MultipartResolver | 在某些多部分分析库的帮助下,用于分析多部分请求(例如,浏览器表单文件上载)的抽象。请参见多部分分解器。 |
FlashMapManager | 存储并检索“输入”和“输出”flashmap,这些flashmap可用于将属性从一个请求传递到另一个请求,通常是通过重定向传递的。请参见Flash属性。 |
Same as in Spring WebFlux
应用程序可以声明在处理请求所需的特殊bean类型中列出的基础结构bean。DispatcherServlet检查每个特殊bean的WebApplicationContext。如果没有匹配的bean类型,则返回DispatcherServlet.properties中列出的默认类型。
在大多数情况下,MVC配置是最好的起点。它在Java或XML中声明所需的bean,并提供更高级的配置回调API来定制它。
Spring Boot依赖于MVC Java配置来配置Spring MVC并提供许多额外的便捷选项。
在Servlet 3.0+环境中,您可以选择以编程方式配置Servlet容器作为替代方案或与web.xml文件组合。以下示例注册DispatcherServlet:
import org.springframework.web.WebApplicationInitializer;
public class MyWebApplicationInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext container) {
XmlWebApplicationContext appContext = new XmlWebApplicationContext();
appContext.setConfigLocation("/WEB-INF/spring/dispatcher-config.xml");
ServletRegistration.Dynamic registration = container.addServlet("dispatcher", new DispatcherServlet(appContext));
registration.setLoadOnStartup(1);
registration.addMapping("/");
}
}
WebApplicationInitializer是SpringMVC提供的一个接口,它确保检测到您的实现并自动用于初始化任何servlet 3容器。名为AbstractDispatcherServletInitializer的WebApplicationInitializer的抽象基类实现通过重写方法来指定servlet映射和DispatcherServlet配置的位置,使注册DispatcherServlet更加容易。
建议使用基于Java的Spring配置的应用程序,如以下示例所示:
public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return null;
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[] { MyWebConfig.class };
}
@Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
如果使用基于XML的Spring配置,则应直接扩展 AbstractDispatcherServletInitializer,如下例所示:
public class MyWebAppInitializer extends AbstractDispatcherServletInitializer {
@Override
protected WebApplicationContext createRootApplicationContext() {
return null;
}
@Override
protected WebApplicationContext createServletApplicationContext() {
XmlWebApplicationContext cxt = new XmlWebApplicationContext();
cxt.setConfigLocation("/WEB-INF/spring/dispatcher-config.xml");
return cxt;
}
@Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
AbstractDispatcherServletInitializer还提供了一种方便的方法来添加Filter 实例并让它们自动映射到DispatcherServlet,如下例所示:
public class MyWebAppInitializer extends AbstractDispatcherServletInitializer {
// ...
@Override
protected Filter[] getServletFilters() {
return new Filter[] {
new HiddenHttpMethodFilter(), new CharacterEncodingFilter() };
}
}
每个过滤器都会根据其具体类型添加一个默认名称,并自动映射到DispatcherServlet。
AbstractDispatcherServletInitializer 的isAsyncSupported protected方法提供了一个单独的位置,可以在DispatcherServlet和映射到它的所有筛选器上启用异步支持。默认情况下,此标志设置为“true”。
最后,如果需要进一步自定义DispatcherServlet本身,可以重写CreateSpatcherServlet方法。
Same as in Spring WebFlux
DispatcherServlet按如下方式处理请求:
WebApplicationContext中声明的handlerExceptionResolver bean用于解决请求处理期间引发的异常。这些异常解析器允许定制逻辑来处理异常。有关详细信息,请参阅例外。
Spring DispatcherServlet还支持返回servlet API指定的最后修改日期。确定特定请求的最后修改日期的过程很简单:DispatcherServlet查找适当的处理程序映射,并测试找到的处理程序是否实现了最后修改的接口。如果是这样,则将向客户端返回lastmodified接口的long getlastmodified(request)方法的值。
通过将servlet初始化参数(init param元素)添加到web.xml文件中的servlet声明,可以自定义各个DispatcherServlet实例。下表列出了支持的参数:
DispatcherServlet初始化参数
Parameter | Explanation |
---|---|
contextClass | 实现了ConfigurableWebApplicationContext的类,通过这个类来实例化和本地配置,默认XmlWebApplicationContext 被使用 |
contextConfigLocation | 传递到上下文实例(由ContextClass指定)的字符串,用于指示可以在何处找到上下文。字符串可能包含多个字符串(使用逗号作为分隔符),以支持多个上下文。如果有两次定义bean的多个上下文位置,则以最新位置为准。 |
namespace | WebApplicationContext的命名空间,默认[servlet-name]-servlet |
throwExceptionIfNoHandlerFound | 在找不到请求的处理程序时是否引发NoHandlerFoundException。然后,可以使用handlerExceptionResolver捕获异常(例如,使用@exceptionHandler控制器方法),并像任何其他方法一样进行处理。默认情况下,此设置为false,在这种情况下,DispatcherServlet将响应状态设置为404(未找到),而不会引发异常。注意,如果还配置了默认servlet处理,未解析的请求总是被转发到默认servlet,并且永远不会引发404。 |
所有handlerMapping实现都支持处理程序拦截器,当您希望将特定功能应用于某些请求时,这些拦截器很有用-例如,检查主体。拦截器必须使用三种方法从org.springframework.web.servlet包中实现handlerInterceptor,这三种方法应提供足够的灵活性来执行各种预处理和后处理:
prehandle(…)方法返回一个布尔值。可以使用此方法中断或继续执行链的处理。当此方法返回true时,处理程序执行链将继续。当返回false时,DispatcherServlet假定拦截器本身处理了请求(例如,呈现了适当的视图),并且不继续执行执行链中的其他拦截器和实际处理程序。
有关如何配置拦截器的示例,请参阅MVC配置部分中的拦截器。还可以通过在单个handlerMapping实现上使用setter直接注册它们。
请注意,posthandle对于在handlerAdapter和posthandle之前写入和提交响应的@responseBody和responseEntity方法不太有用。这意味着对响应进行任何更改(例如添加额外的头)都为时已晚。对于这种情况,您可以实现ResponseBodyAdvice,并将其声明为控制器建议bean,或者直接在RequestMappingHandlerAdapter上配置它。
Same as in Spring WebFlux
如果在请求映射期间发生异常或从请求处理程序(如@controller)抛出异常,则DispatcherServlet将委托给handlerExceptionResolver bean链以解决异常并提供替代处理,这通常是一个错误响应。
下表列出了可用的HandlerExceptionResolver实现:
HandlerExceptionResolver | Description |
---|---|
SimpleMappingExceptionResolver | 异常类名称和错误视图名称之间的映射。用于在浏览器应用程序中呈现错误页面 |
DefaultHandlerExceptionResolver | 解决Spring MVC引发的异常并将它们映射到HTTP状态代码。另请参阅替代ResponseEntityExceptionHandler和REST API异常。 |
ResponseStatusExceptionResolver | 使用@ResponseStatus注释解析异常,并根据注释中的值将它们映射到HTTP状态代码。 |
ExceptionHandlerExceptionResolver | 通过调用@controller或@controlleradvice类中的@exceptionhandler方法来解决异常。请参见@exceptionhandler方法。 |
通过在Spring配置中声明多个handlerExceptionResolver bean并根据需要设置它们的顺序属性,可以形成异常冲突解决程序链。Order属性越高,异常解决程序的位置就越晚。
handlerExceptionResolver的协定指定它可以返回:
MVC配置自动为默认的Spring MVC异常、@responseStatus注释的异常以及支持@exceptionhandler方法声明内置的冲突解决程序。您可以自定义或替换该列表。
如果异常仍然未被任何handlerExceptionResolver解决,因此仍保留传播状态,或者如果响应状态设置为错误状态(即4xx、5xx),servlet容器可以用HTML呈现默认错误页。要自定义容器的默认错误页,可以在web.xml中声明错误页映射。以下示例显示了如何执行此操作:
<error-page>
<location>/error</location>
</error-page>
在前面的示例中,当异常冒泡或响应具有错误状态时,servlet容器会在容器内将错误分派到配置的URL(例如,/error)。然后,DispatcherServlet会对其进行处理,可能会将其映射到@Controller,该控制器可以实现为返回带有模型的错误视图名称或呈现JSON响应,如下例所示:
@RestController
public class ErrorController {
@RequestMapping(path = "/error")
public Map<String, Object> handle(HttpServletRequest request) {
Map<String, Object> map = new HashMap<String, Object>();
map.put("status", request.getAttribute("javax.servlet.error.status_code"));
map.put("reason", request.getAttribute("javax.servlet.error.message"));
return map;
}
}
Servlet API不提供在Java中创建错误页面映射的方法。但是,您可以同时使用WebApplicationInitializer和最小的web.xml。
Same as in Spring WebFlux
SpringMVC定义了视图解析器和视图接口,这些接口允许您在浏览器中呈现模型,而不必绑定到特定的视图技术。ViewResolver提供视图名称和实际视图之间的映射。视图解决了在移交给特定视图技术之前的数据准备问题。
下表提供了有关ViewResolver层次结构的详细信息:
ViewResolver | Description |
---|---|
AbstractCachingViewResolver | AbstractCachingViewResolver的子类缓存它们解析的视图实例。缓存提高了某些视图技术的性能。通过将cache属性设置为false,可以关闭缓存。此外,如果必须在运行时刷新某个视图(例如,修改FreeMarker模板时),则可以使用removeFromCache(string viewname,locale loc)方法。 |
XmlViewResolver | ViewResolver的实现,它接受用XML编写的配置文件,该配置文件的DTD与Spring的XML bean工厂相同。默认配置文件是/WEB-INF/views.xml。 |
ResourceBundleViewResolver | ViewResolver 的实现使用ResourceBundle中的bean定义,通过一些基础的名字被指定。对于应该解析的每个视图,它使用属性[viewname].(class)的值作为视图类,使用属性[viewname].url的值作为视图URL。您可以在有关视图技术的章节中找到示例。 |
UrlBasedViewResolver | viewresolver接口的简单实现,它影响逻辑视图名称到URL的直接解析,而无需显式映射定义。如果您的逻辑名称以直接的方式匹配视图资源的名称,而不需要任意映射,那么这是适当的。 |
InternalResourceViewResolver | 支持InternalResourceView(实际上是servlets和jsp)的URLBasedView解析器的方便子类,以及JSTLView和TilesView等子类。可以使用setViewClass(…)为该解析程序生成的所有视图指定视图类。有关详细信息,请参阅urlbasedview解析器javadoc。 |
FreeMarkerViewResolver | 方便的子类UrlBasedViewResolver支持FreeMarkerView它们的自定义子类。 |
ContentNegotiatingViewResolver | viewresolver接口的实现,该接口基于请求文件名或接受头解析视图。参见Content Negotiation.。 |
Same as in Spring WebFlux
可以通过声明多个冲突解决程序bean来链接视图冲突解决程序,如果需要,还可以通过设置order属性来指定排序。记住,order属性越高,视图解析器在链中的位置就越晚。
ViewResolver的协定指定它可以返回空值,以指示找不到该视图。但是,对于JSP和InternalResourceViewResolver,了解JSP是否存在的唯一方法是通过RequestDispatcher执行调度。因此,必须始终按照视图冲突解决程序的总体顺序将InternalResourceViewResolver配置为最后一个。
配置视图分辨率和向Spring配置中添加viewresolver bean一样简单。MVC配置为视图解析器和添加无逻辑视图控制器提供了专用的配置API,这些配置API对于不使用控制器逻辑的HTML模板呈现非常有用。
Same as in Spring WebFlux
视图名称中的特殊redirect:prefix允许您执行重定向。URLBasedView解析器(及其子类)将此识别为需要重定向的指令。视图名称的其余部分是重定向URL。
net effect与控制器返回RedirectView的效果相同,但现在控制器本身可以按照逻辑视图名称进行操作。逻辑视图名称(例如redirect:/myapp/some/resource)相对于当前servlet上下文重定向,而名称(例如redirect:http://myhost.com/some/arbitrary/path)则重定向到绝对URL。
注意,如果用@responseStatus注释控制器方法,则注释值优先于RedirectView设置的响应状态。
对于最终由URLBasedView解析器和子类解析的视图名称,也可以使用特殊的forward:prefix。这将创建一个InternalResourceView,它执行requestDispatcher.forward()。因此,此前缀对于InternalResourceViewResolver和InternalResourceView(对于JSP)不太有用,但如果您使用其他视图技术,但仍希望强制资源转发由servlet/JSP引擎处理,则此前缀可能会有所帮助。请注意,您也可以链接多个视图解析器。
Same as in Spring WebFlux
ContentNegotiangViewResolver不解析视图本身,而是委托给其他视图冲突解决程序,并选择类似于客户端请求的表示的视图。可以从accept头或查询参数(例如,"/path?format=pdf")确定表示形式。
ContentNegotiangViewResolver通过将请求媒体类型与与其每个视图冲突解决程序关联的视图所支持的媒体类型(也称为内容类型)进行比较来选择适当的视图来处理请求。列表中具有兼容内容类型的第一个视图将表示返回给客户端。如果viewresolver链无法提供兼容的视图,则将查询通过defaultviews属性指定的视图列表。后一个选项适用于单实例视图,该视图可以呈现当前资源的适当表示,而不考虑逻辑视图名称。接受头可以包含通配符(例如text/*),在这种情况下,内容类型为text/xml的视图是兼容的匹配项。
有关配置详细信息,请参阅MVC配置下的视图冲突解决程序。
与SpringWebMVC框架一样,Spring架构的大多数部分支持国际化。DispatcherServlet允许您使用客户端的区域设置自动解析消息。这是通过LocaleResolver对象完成的。
当一个请求进入时,DispatcherServlet会查找一个区域设置解析器,如果找到了一个,它会尝试使用它来设置区域设置。通过使用requestContext.getLocale()方法,可以始终检索由区域设置解析程序解析的区域设置。
除了自动区域设置解析之外,还可以将拦截器附加到处理程序映射(有关处理程序映射拦截器的详细信息,请参阅拦截器),以便在特定情况下(例如,基于请求中的参数)更改区域设置。
区域设置解析器和拦截器是在org.springframework.web.servlet.i18n包中定义的,并以正常方式在应用程序上下文中配置。以下区域设置冲突解决程序的选择包含在Spring中。
除了获得客户机的区域设置外,了解其时区通常也很有用。LocaleContextResolver接口提供了对LocaleResolver的扩展,允许冲突解决程序提供更丰富的LocaleContext,其中可能包括时区信息。
如果可用,可以使用requestContext.getTimeZone()方法获取用户的时区。时区信息由任何向Spring的ConversionService注册的日期/时间转换器和格式化程序对象自动使用。
此区域设置冲突解决程序检查客户端(例如,Web浏览器)发送的请求中的接受语言头。通常,此头字段包含客户端操作系统的区域设置。请注意,此冲突解决程序不支持时区信息。
此区域设置冲突解决程序检查客户端上可能存在的cookie,以查看是否指定了区域设置或时区。如果是,则使用指定的详细信息。通过使用此区域设置冲突解决程序的属性,可以指定cookie的名称以及最长使用期限。以下示例定义了一个CookieCalersolver:
bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver">
<property name="cookieName" value="clientlanguage"/>
<!-- in seconds. If set to -1, the cookie is not persisted (deleted when browser shuts down) -->
<property name="cookieMaxAge" value="100000"/>
</bean>
下表描述了这些属性CookieLocaleResolver:
Property | Default | Description |
---|---|---|
cookieName | classname + LOCALE | The name of the cookie |
cookieMaxAge | Servlet container default | cookie在客户端上的最长持续时间。如果指定了-1,则不会持久化cookie。只有在客户端关闭浏览器之前,它才可用。 |
cookiePath | / | 将cookie的可见性限制在网站的某个部分。当指定了cookie path时,cookie仅对该路径及其下面的路径可见。 |
sessionLocaleResolver允许您从可能与用户请求相关联的会话中检索区域设置和时区。与cookielocalersolver不同,此策略将本地选择的区域设置存储在servlet容器的httpsession中。因此,这些设置对于每个会话都是临时的,因此在每个会话终止时都会丢失。
请注意,与外部会话管理机制(如Spring会话项目)没有直接关系。此sessionLocalerResolver针对当前的httpServletRequest评估并修改相应的httpSession属性。
您可以通过将localeChangeInterceptor添加到一个handlerMapping定义来启用区域设置的更改。它在请求中检测到一个参数,并相应地更改区域设置,在调度程序的应用程序上下文中调用LocaleResolver上的setLocale方法。下一个示例显示,对包含名为sitelanguage的参数的所有*.view资源的调用现在都会更改区域设置。例如,请求URL,http://www.sf.net/home.view?site language=nl,将站点语言更改为荷兰语。以下示例显示如何截取区域设置:
<bean id="localeChangeInterceptor"
class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
<property name="paramName" value="siteLanguage"/>
</bean>
<bean id="localeResolver"
class="org.springframework.web.servlet.i18n.CookieLocaleResolver"/>
<bean id="urlMapping"
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="interceptors">
<list>
<ref bean="localeChangeInterceptor"/>
</list>
</property>
<property name="mappings">
<value>/**/*.view=someController</value>
</property>
</bean>
您可以应用Spring Web MVC框架主题来设置应用程序的整体外观,从而增强用户体验。主题是静态资源的集合,通常是样式表和图像,它们会影响应用程序的视觉样式。
要在Web应用程序中使用主题,必须设置org.springframework.ui.context.themesource接口的实现。WebApplicationContext接口扩展了ThemeSource,但将其职责委托给了专用的实现。默认情况下,委托是一个org.springframework.ui.context.support.r
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!