从Spring到SpringBoot构建WEB MVC核心配置详解
理解Spring WEB MVC架构的演变
-
基础Servlet架构
-
核心架构:前端控制器
- Spring WEB MVC架构
认识Spring WEB MVC
传统时代的Spring WEB MVC
怎么讲呢?就是很传统的使用Spring Framework WEB MVC的方式,例如Bean配置在xml中,前端控制器配置在web.xml中等。
Maven 依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.1.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.3.RELEASE</version>
</dependency>
</dependencies>
web.xml
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
Spring.xml
<context:component-scan base-package="com.jimisun.web"/>
<mvc:annotation-driven/>
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
Controller
@Controller
public class HelloSpringController {
@RequestMapping("")
public String index(){
return "index";
}
}
新时代Spring WEB MVC
所谓的新时代相比肯定是在传统时代上的升级。提到升级我们首先会想到会提升使用的便捷程度以及性能。
- 基于注解驱动配置WEB MVC
- 自动装配前端控制器
基于注解驱动配置WEB MVC
@Configuration
@EnableWebMvc
public class WebDispatcherServletConfigure implements WebMvcConfigurer {
@Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver resourceViewResolver = new InternalResourceViewResolver();
resourceViewResolver.setViewClass(JstlView.class);
resourceViewResolver.setPrefix("/WEB-INF/jsp/");
resourceViewResolver.setSuffix(".jsp");
return resourceViewResolver;
}
}
在Spring.xml文件中则不用再配置处理器映射器,处理器适配器和内部资源视图解析器(JSP)。只需要保留<context:component-scan base-package="com.jimisun.web"/>
以便发现我们自定义的前端控制器配置类WebDispatcherServletConfigure
。因为@EnableWebMvc
注解为我们自动配置内部资源视图解析器不符合我们自定义的要求,所以上述代码中我们使用@Bean
注解重新装配了InternalResourceViewResolver
类。
基于注解驱动配置的重点是@EnableWebMvc
注解,此模块注解将导入DelegatingWebMvcConfiguration
配置类,该类又集成了WebMvcConfigurationSupport
类。WebMvcConfigurationSupport
中则配置了一些关于 WEB MVC的相关Bean;例如:RequestMappingHandlerMapping
,ContentNegotiationManager
,HandlerMapping
,BeanNameUrlHandlerMapping
,RequestMappingHandlerAdapter
,ViewResolver
等等……
自动装配前端控制器
原理 : 在web容器启动时为提供给第三方组件机会做一些初始化的工作,例如注册servlet或者filtes等,servlet规范中通过ServletContainerInitializer实现此功能。每个框架要使用ServletContainerInitializer就必须在对应的jar包的META-INF/services 目录创建一个名为javax.servlet.ServletContainerInitializer的文件,文件内容指定具体的ServletContainerInitializer实现类,那么,当web容器启动时就会运行这个初始化器做一些组件内的初始化工作。一般伴随着ServletContainerInitializer一起使用的还有HandlesTypes注解,通过HandlesTypes可以将感兴趣的一些类注入到ServletContainerInitializerde的onStartup方法作为参数传入。
我们从spring-web的依赖包中找到这个文件,所以我们可以将前端控制器在Servlet容器启动的时候将Spring WEB MVC的前端控制器添加入Servlet容器中,而不用再配置在web.xml文件中。
我们可以看一下Spring的这个SpringServletContainerInitializer
类
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
......
}
我们可以看到Spring定义的这个类实现了Servlet3.0的ServletContainerInitializer
接口,并且标注了@HandlesTypes
;表示将实现WebApplicationInitializer.class
接口的类注入到ServletContainerInitializer
的回调方法onStartup()
进行初始化。
我们从上面图可以看到WebApplicationInitializer接口的实现类共有四个,所以就意味着我们自定义一个类实现这三个抽象方法其中的一个就行。下面是我们自定义的Servlet容器初始化器类。
public class DefaultAnnotationConfigDispatcherServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
protected Class<?>[] getRootConfigClasses() {
return new Class[0];
}
protected Class<?>[] getServletConfigClasses() {
return new Class[]{DispathcherConfig.class};
}
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
getServletMappings()
方法用来返回前端控制器要拦截的路径形式。其中getRootConfigClasses()
方法用于配置Spring Framework与Service,response有关的Bean;而getServletConfigClasses()
方法则用来配置Spring Framework中的WEB层相关的Bean。资料参考 :https://stackoverflow.com/questions/35258758/getservletconfigclasses-vs-getrootconfigclasses-when-extending-abstractannot
总的来说只要自定义一个类,实现WebApplicationInitializer接口即可;我们就可以不用在web.xml中配置前端控制器了。
SpringBoot简化WEB MVC开发
Spring Boot从一下三个方面来简化WEB开发;分别是“自动装配”,“条件装配”,“外部化配置” 三个方面来简化WEB MVC开发。
自动装配
SpringBooot对于WEB MVC的完全自动装配主要体现在一下三个方面。
- 前端控制器自动装配
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
- WEB MVC相关组件自动装配
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
- WEB容器自动装配
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
条件装配
从完全自动装配可以看到,SpringBoot替我们装配了很多东西;但是在大部分情况下就是基于条件进行装配的,既满足某某条件才会进行装配。还是来看一下前端控制器自定装配类org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass(DispatcherServlet.class)
@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
public class DispatcherServletAutoConfiguration {
}
从该类的注解上我们可以知道@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
该类的装配顺序,@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
在WEB容器装配之后再进行装配,@Configuration
可被管理的Bean,@ConditionalOnWebApplication(type = Type.SERVLET)
满足条件为WEB类型应用,@ConditionalOnClass(DispatcherServlet.class)
满足条件存在DispatcherServlet
类。
可以看到我们需要满足两个条件SpringBoot才会为我们装配这个前端控制器DispatcherServlet
类。
外部化配置
关于WEB MVC的外部化配置我们需要了解一下WebMvcProperties
类和ResourceProperties类;分别对WebMvc和资源文件进行配置。
@ConfigurationProperties(prefix = "spring.mvc")
public class WebMvcProperties {
.......
}
@ConfigurationProperties(prefix = "spring.resources", ignoreUnknownFields = false)
public class ResourceProperties {
......
}
从注解上我们可以了解到,我们可以在application.properties
文件或者application.yml
文件中进行相关配置。
例如:SpringBoot在自动装配WEB MVC组件的时候装配了一个内部资源视图解析器(JSP)
@Bean
@ConditionalOnMissingBean
public InternalResourceViewResolver defaultViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix(this.mvcProperties.getView().getPrefix());
resolver.setSuffix(this.mvcProperties.getView().getSuffix());
return resolver;
}
其中this.mvcProperties.getView().getPrefix()
就是从application.properties
文件或者application.yml
文件中获取的值进行的操作而非硬编码。
本章源码下载
该教程所属Java工程师之SpringBoot系列教程,本系列相关博文目录 Java工程师之SpringBoot系列教程前言&目录