Spring MVC配置(替代web.xml)
Spring MVC配置主要由以下几个部分组成:
- Spring MVC启动器
- RootApplicationContext
- ServletApplicationContext
Spring MVC启动器
Spring MVC启动器主要是实现WebApplicationInitializer
接口的实现类。该接口提供一个方法:onStartup(ServletContext servletContext)
。当WEB服务容器启动时会扫描所有实现该接口的实现类,并调用onStartup方法。在实现类中可以通过ServletContext注册接口、Servlet等web.xml能实现的配置。
public interface WebApplicationInitializer {
void onStartup(ServletContext servletContext) throws ServletException;
}
在Spring Web包中提供了1个实现WebApplicationInitializer
接口的抽象类,在Spring Webmvc包中提供了2个继承于AbstractContextLoaderInitializer
的抽象类:
AbstractContextLoaderInitializer
AbstractDispatcherServletInitializer
AbstractAnnotationConfigDispatcherServletInitializer
AbstractContextLoaderInitializer
该抽象类只是为了创建ContextLoaderListener
,并通过抽象方法createRootApplicationContext
创建的RootApplicationContext注入进Servlet容器事件里面去。
AbstractDispatcherServletInitializer
该抽象类继承于AbstractContextLoaderInitializer
,默认注册了一个 通过ServletApplicationContext创建的DispatcherServlet
。以及提供了配置Mappers的抽象方法、注册Filters的默认返回null的基本方法。
AbstractAnnotationConfigDispatcherServletInitializer
该抽象类继承于AbstractDispatcherServletInitializer
,使用AnnotationConfigWebApplicationContext
来创建RootApplicationContext与ServletApplicationContext。getRootConfigClasses和getServletConfigClasses两个抽象方法,通过两个抽象方法来注册java配置类。
开始配置
我的配置有以下配置类
SpringStartupInitializer
ServletRootConfig
ServletWebConfig
SpringCacheEhcacheConfig
SpringDataSourceConfig
SpringShiroSourceConfig
SpringStartupInitializer
SpringStartupInitializer
是为了替代web.xml,它是一个实现了WebApplicationInitializer
接口的类,由于我的配置主要是使用注解配置,因此SpringStartupInitializer
是为了替代web只需基础于AbstractAnnotationConfigDispatcherServletInitializer
。
public class SpringStartupInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected FrameworkServlet createDispatcherServlet(WebApplicationContext servletAppContext) {
DispatcherServlet dispatcherServlet = new DispatcherServlet(servletAppContext);
dispatcherServlet.setThrowExceptionIfNoHandlerFound(true);
return dispatcherServlet;
}
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
super.onStartup(servletContext);
registerDruidStatViewServlet(servletContext);
}
@Override
protected Filter[] getServletFilters() {
return new Filter[] { //
new CharacterEncodingFilter("UTF-8"), // 编码转换
new DelegatingFilterProxy("springSessionRepositoryFilter"), // Spring Session
new DelegatingFilterProxy("shiroFilter") };
}
@Override
protected Class>[] getRootConfigClasses() {
return new Class>[] { ServletRootConfig.class };
}
@Override
protected Class>[] getServletConfigClasses() {
return new Class>[] { ServletWebConfig.class };
}
@Override
protected String[] getServletMappings() {
return new String[] { "/*" };
}
}
ServletRootConfig
该配置类主要是配置缓存、数据源、Shiro安全框架
@Configuration
@EnableCaching // 启用缓存注解
@EnableScheduling // 启用注解配置定时器
@Import(value = { SpringCacheEhcacheConfig.class, SpringDataSourceConfig.class, SpringShiroConfig.class })
public class ServletRootConfig {
/**
* Session ID 管理
*
* @return
*/
@Bean
public HttpSessionIdResolver httpSessionIdResolver() {
// Session ID 获取,主要作用是为了从请求中获取sessionID,这里使用的我自己实现的一个方式
// Spring 提供有三种获取方式:
// 1、CookieHttpSessionIdResolver 从Cookie中获取
// 2、HeaderHttpSessionIdResolver 从请求头中获取
// 3、ParamHttpSessionIdResolver 从请求参数中获取
return new OrderHttpSessionIdResolver();
}
/** 加载配置文件 */
@Bean("propertiesBean")
public PropertiesFactoryBean propertiesFactoryBean() throws IOException {
PropertiesFactoryBean factoryBean = new PropertiesFactoryBean();
factoryBean.setLocations(ResourceUtil.getResources("classpath:config/*.properties"));
return factoryBean;
}
/** 注入配置文件 */
@Bean("propertyPlaceholderConfigurer")
public static PropertyPlaceholderConfigurer propertyPlaceholderConfigurer(@Qualifier("propertiesBean") Properties properties) throws IOException {
PropertyPlaceholderConfigurer configurer = new PropertyPlaceholderConfigurer();
configurer.setProperties(properties);
return configurer;
}
}
ServletWebConfig
ServletWebConfig
是实现WebMvcConfigurer
的配置类,主要是为了配置Spring WebMvc。在WebMvcConfigurer
类中提供有一下空方法
/** */
@Override
default void configurePathMatch(PathMatchConfigurer configurer) ;
/** 配置内容协商器 */
@Override
default void configureContentNegotiation(ContentNegotiationConfigurer configurer) ;
/** */
@Override
default void configureAsyncSupport(AsyncSupportConfigurer configurer) ;
/** */
@Override
default void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) ;
/** 添加格式转换器 */
@Override
default void addFormatters(FormatterRegistry registry);
/** 添加过滤器 */
@Override
default void addInterceptors(InterceptorRegistry registry);
/** 添加静态资源 */
@Override
default void addResourceHandlers(ResourceHandlerRegistry registry);
/** 添加跨域配置 */
@Override
default void addCorsMappings(CorsRegistry registry);
/** */
@Override
default void addViewControllers(ViewControllerRegistry registry);
/** 配置视图转换器 */
@Override
default void configureViewResolvers(ViewResolverRegistry registry);
/** */
@Override
default void addArgumentResolvers(List argumentResolvers);
/** */
@Override
default void addReturnValueHandlers(List returnValueHandlers);
/** 配置消息转换器,会覆盖Spring默认的消息转换器 */
@Override
default void configureMessageConverters(List> converters);
/** 在Spring默认的消息转换器基础上,扩展自定义消息转换器 */
@Override
default void extendMessageConverters(List> converters);
/** 配置异常处理器 */
@Override
default void configureHandlerExceptionResolvers(List exceptionResolvers);
/** 扩展默认异常处理器 */
@Override
default void extendHandlerExceptionResolvers(List exceptionResolvers);
/** */
@Override
default Validator getValidator();
/** */
@Override
default MessageCodesResolver getMessageCodesResolver();
在这里我的ServletWebConfig
只实现了部分方法
@Configuration
@EnableWebMvc // 启用WebMVC
@ComponentScan("cn.virens.web.controller") // 扫描controller所在的包
@Import(value = { SpringBeetlConfig.class })// beetl 配置(源码:https://gitee.com/virens/multi_spring/blob/master/src/main/java/cn/virens/config/SpringBeetlConfig.java)
public class ServletWebConfig implements WebMvcConfigurer {
/**
* 内容协商器配置。将后缀为json的配置为json处理,后缀为jspx的为页面处理
*/
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.defaultContentType(MediaType.APPLICATION_JSON_UTF8);
configurer.mediaType("jspx", MediaType.TEXT_HTML);
configurer.mediaType("json", MediaType.APPLICATION_JSON_UTF8);
}
/**
* 添加视图解析器
*/
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
// 配置Beetl 视图
BeetlSpringViewResolver beetlSpringViewResolver = new BeetlSpringViewResolver();
beetlSpringViewResolver.setContentType("text/html;charset=UTF-8");
beetlSpringViewResolver.setSuffix(".html");
beetlSpringViewResolver.setOrder(2);
// 配置FastJson 视图
FastJsonJsonView fastJsonJsonView = new FastJsonJsonView();
fastJsonJsonView.getFastJsonConfig().setCharset(Charset.forName("UTF-8"));
fastJsonJsonView.getFastJsonConfig().setDateFormat(CalendarUtil.YMD_HMS); // 所有时间格式都为yyyy-MM-dd HH:mm:ss
// 注册视图
registry.viewResolver(beetlSpringViewResolver);
registry.enableContentNegotiation(fastJsonJsonView);
}
/**
* 添加消息转换器
*/
@Override
public void configureMessageConverters(List> converters) {
FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter();
fastJsonHttpMessageConverter.getFastJsonConfig().setCharset(Charset.forName("UTF-8"));
fastJsonHttpMessageConverter.getFastJsonConfig().setDateFormat(CalendarUtil.YMD_HMS); // 所有时间格式都为yyyy-MM-dd HH:mm:ss
// 将消息转换器转给Spring进行管理
converters.add(new ByteArrayHttpMessageConverter());
converters.add(new ResourceHttpMessageConverter());
converters.add(new SourceHttpMessageConverter<>());
converters.add(new StringHttpMessageConverter(Charset.forName("UTF-8")));
converters.add(fastJsonHttpMessageConverter);
}
/**
* 添加异常统一处理
*/
@Override
public void configureHandlerExceptionResolvers(List exceptionResolvers) {
// 我自己实现的(源码:https://gitee.com/virens/multi_spring/blob/master/src/main/java/cn/virens/web/components/spring/VirExceptionResolver.java)
VirExceptionResolver exceptionResolver = new VirExceptionResolver();
exceptionResolver.addExceptionMessage(UnauthorizedException.class, "未授权");
exceptionResolver.addExceptionMessage(UnauthenticatedException.class, "未登录");
exceptionResolver.addExceptionMessage(NoHandlerFoundException.class, "接口不存在");
exceptionResolver.addExceptionMessage(DataIntegrityViolationException.class, "请检查约束");
exceptionResolver.addExceptionMessage(MaxUploadSizeExceededException.class, "文件超出大小限制");
exceptionResolver.addExceptionMappings(UnauthorizedException.class, "/error/401");
exceptionResolver.addExceptionMappings(UnauthenticatedException.class, "/error/401");
exceptionResolver.addExceptionMappings(HostUnauthorizedException.class, "/error/401");
exceptionResolver.addExceptionMappings(NoHandlerFoundException.class, "/error/404");
exceptionResolver.setDefaultErrorView("/error/500");
exceptionResolver.setDefaultStatusCode(500);
exceptionResolver.setExceptionAttribute("ex");
exceptionResolver.afterPropertiesSet();
exceptionResolvers.add(exceptionResolver);
}
/**
* 格式转换器
*/
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new DateConverter());
}
/**
* 添加静态资源路径
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/error/**").addResourceLocations("/error/");
registry.addResourceHandler("/assets/**").addResourceLocations("/assets/");
registry.addResourceHandler("/upload/**").addResourceLocations("/upload/");
registry.addResourceHandler("/favicon.ico").addResourceLocations("/favicon.ico");
}
/**
* 跨域配置
*/
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**").allowedMethods("GET", "POST", "OPTIONS").allowedHeaders("X-Auth-Token", "Content-Type").exposedHeaders("X-Auth-Token").allowCredentials(true).maxAge(86400);
}
/** 文件上传配置 */
@Bean
public CommonsMultipartResolver multipartResolver() {
CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
multipartResolver.setMaxUploadSize(10 * 1024 * 1024);
multipartResolver.setDefaultEncoding("UTF-8");
return multipartResolver;
}
/** 注入配置文件 */
@Bean("propertyPlaceholderConfigurer")
public static PropertyPlaceholderConfigurer propertyPlaceholderConfigurer(@Qualifier("propertiesBean") Properties properties) throws IOException {
PropertyPlaceholderConfigurer configurer = new PropertyPlaceholderConfigurer();
configurer.setProperties(properties);
return configurer;
}
}