SpringMVC的全注解开发
# 1、spring-mvc.xml 中组件转化为注解形式
跟之前全注解开发思路一致, xml配置文件使用核心配置类替代,xml中的标签使用对应的注解替代
<!-- 组件扫描 web 层 -->
<context:component-scan base-package="com.itheima.controller"/>
<!-- 注解驱动,支持 @RequestMapping 等注解 -->
<mvc:annotation-driven/>
<!-- 配置文件上传解析器 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"/>
<!-- 配置拦截器 -->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/*"/>
<bean class="com.itheima.interceptor.MyInterceptor01"/>
</mvc:interceptor>
</mvc:interceptors>
<!-- 配置 DefaultServletHttpRequestHandler,处理静态资源 -->
<mvc:default-servlet-handler/>
组件扫描,可以通过@ComponentScan注解完成;
文件上传解析器multipartResolver可以通过非自定义Bean的注解配置方式,即@Bean注解完成
@Configuration
@ComponentScan("com.itheima.controller")
public class SpringMVCConfig {
@Bean
public CommonsMultipartResolver multipartResolver() {
CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
// 设置默认编码
multipartResolver.setDefaultEncoding("UTF-8");
// 设置最大上传总大小(3MB)
multipartResolver.setMaxUploadSize(3145728);
// 设置单个文件最大上传大小(1MB)
multipartResolver.setMaxUploadSizePerFile(1048576);
// 设置内存中缓存的最大大小(1MB)
multipartResolver.setMaxInMemorySize(1048576);
return multipartResolver;
}
}
<mvc:default-servlet-handler /> 和 <mvc:interceptor > 怎么办呢?SpringMVC 提供了一个注解叫做@EnableWebMvc,我们看一下源码,内部通过@Import 导入了DelegatingWebMvcConfiguration类
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({DelegatingWebMvcConfiguration.class})
public @interface EnableWebMvc {}
@Configuration(proxyBeanMethods = false)
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
// 用于保存所有注入的 WebMvcConfigurer 实例
private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
/**
* 从 Spring 容器中自动注入所有 WebMvcConfigurer 类型的 Bean
* @param configurers 注入的 WebMvcConfigurer 列表
*/
@Autowired(required = false)
public void setConfigurers(List<WebMvcConfigurer> configurers) {
if (!CollectionUtils.isEmpty(configurers)) {
this.configurers.addWebMvcConfigurers(configurers);
}
}
// 省略其他代码
}
WebMvcConfigurer类型的Bean会被注入进来,然后被自动调用,所以可以实现WebMvcConfigurer接口,完成一些解析器、默认Servlet等的指定,WebMvcConfigurer接口定义如下:
public interface WebMvcConfigurer {
/** 配置默认的 Servlet 处理器,用于静态资源处理 */
default void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { }
/** 添加拦截器 */
default void addInterceptors(InterceptorRegistry registry) { }
/** 添加静态资源处理器,比如映射 /static/** 路径 */
default void addResourceHandlers(ResourceHandlerRegistry registry) { }
/** 添加视图控制器,直接实现请求到视图的映射,无需写 Controller */
default void addViewControllers(ViewControllerRegistry registry) { }
/** 配置视图解析器 */
default void configureViewResolvers(ViewResolverRegistry registry) { }
/** 添加自定义的方法参数解析器 */
default void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) { }
// ... 省略其他代码 ...
}
创建MyWebMvcConfigurer实现WebMvcConfigurer接口,实现addInterceptors 和 configureDefaultServletHandling方法
@Component
public class MyWebMvcConfigurer implements WebMvcConfigurer {
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
// 开启 DefaultServlet 处理静态资源,等价于 <mvc:default-servlet-handler/>
configurer.enable();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 注册自定义拦截器,拦截所有一级路径请求(/xxx)
// 多个拦截器执行顺序按注册顺序决定
registry.addInterceptor(new MyInterceptor01()).addPathPatterns("/*");
}
}
最后,在SpringMVC核心配置类上添加@EnableWebMvc注解
@Configuration
@ComponentScan("com.itheima.controller")
@EnableWebMvc
public class SpringMVCConfig {
@Bean
public CommonsMultipartResolver multipartResolver() {
CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
// 设置默认编码
multipartResolver.setDefaultEncoding("UTF-8");
// 设置最大上传总大小(3MB)
multipartResolver.setMaxUploadSize(3145728);
// 设置单个文件最大上传大小(1MB)
multipartResolver.setMaxUploadSizePerFile(1048576);
// 设置内存缓存最大大小(1MB)
multipartResolver.setMaxInMemorySize(1048576);
return multipartResolver;
}
}
# 2、DispatcherServlet加载核心配置类
DispatcherServlet在进行SpringMVC配置文件加载时,使用的是以下方式:
<!-- 配置 Spring MVC 前端控制器 -->
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 指定 Spring MVC 配置文件位置 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<!-- 服务器启动时立即创建 DispatcherServlet -->
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
现在是使用SpringMVCConfig核心配置类提替代的spring-mvc.xml,怎么加载呢?参照Spring的ContextLoaderListener加载核心配置类的做法,定义了一个AnnotationConfigWebApplicationContext,通过代码注册核心配置类
public class MyAnnotationConfigWebApplicationContext extends AnnotationConfigWebApplicationContext {
public MyAnnotationConfigWebApplicationContext() {
// 注册核心配置类 SpringMVCConfig,加载 Spring MVC 配置
super.register(SpringMVCConfig.class);
}
}
<!--指定springMVC的applicationContext全限定名 -->
<init-param>
<param-name>contextClass</param-name>
<param-value>com.itheima.config.MyAnnotationConfigWebApplicationContext</param-value>
</init-param>
# 3、消除web.xml
目前,几乎消除了配置文件,但是web工程的入口还是使用的web.xml进行配置的,如下
<!-- 配置 Spring MVC 前端控制器 -->
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 指定 Spring MVC 的 ApplicationContext 全限定类名 -->
<init-param>
<param-name>contextClass</param-name>
<param-value>com.itheima.config.MyAnnotationConfigWebApplicationContext</param-value>
</init-param>
<!-- 服务器启动时创建 DispatcherServlet -->
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
Servlet3.0环境中,web容器提供了javax.servlet.ServletContainerInitializer接口,实现了该接口后,在对应的类加载路径的META-INF/services 目录创建一个名为javax.servlet.ServletContainerInitializer的文件,文件内容指定具体的ServletContainerInitializer实现类,那么,当web容器启动时就会运行这个初始化器做一些组件内的初始化工作;
基于这个特性,Spring就定义了一个SpringServletContainerInitializer实现了ServletContainerInitializer接口;
而SpringServletContainerInitializer会查找实现了WebApplicationInitializer的类,Spring又提供了一个WebApplicationInitializer的基础实现类AbstractAnnotationConfigDispatcherServletInitializer,当我们编写类继承AbstractAnnotationConfigDispatcherServletInitializer时,容器就会自动发现我们自己的类,在该类中我们就可以配置Spring和SpringMVC的入口了。
按照下面的配置就可以完全省略web.xml
public class MyAnnotationConfigDispatcherServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
/**
* 返回带有 @Configuration 注解的类,用来配置 ContextLoaderListener(Spring 容器)
*/
@Override
protected Class<?>[] getRootConfigClasses() {
System.out.println("加载核心配置类创建 ContextLoaderListener");
return new Class[]{ApplicationContextConfig.class};
}
/**
* 返回带有 @Configuration 注解的类,用来配置 DispatcherServlet(Spring MVC 容器)
*/
@Override
protected Class<?>[] getServletConfigClasses() {
System.out.println("加载核心配置类创建 DispatcherServlet");
return new Class[]{SpringMVCConfig.class};
}
/**
* 将一个或多个路径映射到 DispatcherServlet 上,通常是 "/"
*/
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
}