文章目录
1. 项目搭建
1.1. 搭建原理
1.2. springMVC版本
1.3. 配置内嵌tomcat
1.4. 配置DispatcherServlet初始化器
1.5. 主配置文件
1.6. MVC配置类
2. 配置拦截器
3. 配置过滤器
4. 配置视图解析器
5. 配置ViewController
6. 配置MessageConverters
6.1. 注解版
7. 异常处理器
7.1. 异常处理器执行的顺序
7.2. SimpleMappingExceptionResolver
7.3. DefaultHandlerExceptionResolver
7.4. ResponseStatusExceptionResolver
7.5. ExceptionHandlerExceptionResolver
8. 配置跨域请求
8.1. 使用注解
8.2. 全局配置
9. 配置静态资源解析
10. 高级配置
项目搭建
搭建原理
springMVC版本
- 此次使用的版本是Spring 5.1.8
配置内嵌tomcat
- 为了简化开发,使用tomcat插件实现web项目的运行,只需要在pom.xml中配置一个插件即可,如下:
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.1</version>
<configuration>
<port>8080</port>
<path>/</path>
<uriEncoding>UTF-8</uriEncoding>
</configuration>
</plugin>
</plugins>
- 之后在IDEA右侧的maven处可以看见tomcat7这个插件了,点击run即可运行
配置DispatcherServlet初始化器
- 配置的方式有多种,但是根据Spring文档推荐的方式如下:
import cn.tedu.demo.config.AppConfig;
import cn.tedu.demo.config.WebMvcConfig;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
/**
* 配置DispatcherServlet初始化器,在容器启动的时候会加载初始化
* 入口就是/org/springframework/spring-web/5.1.8.RELEASE/spring-web-5.1.8.RELEASE.jar!/META-INF/services/javax.servlet.ServletContainerInitializer
* web容器在启动的时候会加载META-INF/service下的文件
*/
public class StrartWebApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
/**
* 配置主配置类,主配置类的作用就是配置业务所需要的各种Bean,比如dao,service
* @return
*/
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[]{AppConfig.class};
}
/**
* 配置MVC所需的配置类,该配置类的作用就是扫描controller,配置mvc的各种组件,比如视图解析器,拦截器等
* @return
*/
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{WebMvcConfig.class};
}
/**
* 配置servletMapping,相当于在DispatcherServlet中配置的url
* @return
*/
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
主配置文件
- 主配置文件主要的作用就是配置业务需求的Bean,比如dao,service层的
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.stereotype.Controller;
/**
* 业务逻辑的配置类,扫描所有的业务Bean,比如dao,service,排除所有的controller
*/
@Configuration
@ComponentScan(basePackages = {"cn.tedu.demo"},excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class})})
public class AppConfig {
}
MVC配置类
- MVC配置类主要的作用就是扫描Controller,配置各种组件,比如视图解析器,拦截器等等
- 重要的两点如下:
- 使用
@EnableWebMvc
注解开启MVC功能,相当于xml文件中的<mvc:annotation-driven/>
- 配置类需要实现
WebMvcConfigurer
,该接口下有各种方法,开发者可以实现其中的方法完成相关组件的生成
- 使用
import cn.tedu.demo.interceptor.CustomInterceptor;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.stereotype.Controller;
import org.springframework.web.servlet.config.annotation.*;
/**
* MVC的配置类,扫描所有的controller,排除所有的业务类
* @EnableWebMvc 注解开启mvc功能
* @ComponentScan 注解中的属性useDefaultFilters(默认是true,扫描全部的Bean),这里我们定义了只扫描controller,因此要设置该属性为false,否则不起作用,排除Bean则不需要
*/
@EnableWebMvc
@Configuration
@ComponentScan(basePackages = {"cn.tedu.demo"},includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class})},useDefaultFilters = false)
public class WebMvcConfig implements WebMvcConfigurer {
}
配置拦截器
- 自定义一个拦截器,如下:
/**
* 自定义一个拦截器,实现HandlerInterceptor
*/
@Component
public class CustomInterceptor implements HandlerInterceptor {
/**
* 在拦截器方法之前执行
* @param request request
* @param response response
* @param handler 拦截的handler
* @return 如果返回false,后续的拦截器和拦截的handler不执行
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("在之前执行");
return true;
}
}
- 在配置类设置自定义的拦截器,使得起作用
@Override
public void addInterceptors(InterceptorRegistry registry) {
//创建
CustomInterceptor customInterceptor = new CustomInterceptor();
//添加自定义的拦截器
registry.addInterceptor(customInterceptor).addPathPatterns("/**");
}
- 自定义的拦截器的真实实现类其实是
MappedInterceptor
,在源码中获取处理器执行链的时候会将其添加到执行链中。
配置过滤器
- 过滤器不属于SpringMVC,而是属于Servlet中的组件,因此配置过滤器使用的并不是MVC的配置,但是在Servlet3.0中也是提供了注解版的Servlet和Filter的生成方式,我们使用注解生成一个Filter,如下:
/**
* 自定义过滤器
*/
@WebFilter(filterName = "customFilter",urlPatterns = "/*")
public class CustomFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("过滤器初始化");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("过滤器执行");
chain.doFilter(request,response);
}
@Override
public void destroy() {
System.out.println("过滤器销毁");
}
}
配置视图解析器
代码语言:javascript复制@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.jsp("/WEB_INF/",".jsp");
}
配置ViewController
代码语言:javascript复制@Override
public void addViewControllers(ViewControllerRegistry registry) {
//定义一个controller,访问路径是/index.do,跳转的视图是index.jsp
registry.addViewController("/index.do").setViewName("index");
}
配置MessageConverters
- 消息转换器用于对Request和Response的消息进行处理,比如将Response中的消息转换为指定JSON字符串的形式
- 默认的消息转换器对于日期的类型的转换是时间戳,即是返回的JSON字符串的日期类型是时间戳,接收的日期类型参数也只能是时间戳
- 如何配置消息转换器,只需要重写springmvc配置类中的方法即可。
- 我们使用的是
MappingJackson2HttpMessageConverter
这类转换器,但是其中依赖的是ObjectMapper,因此我们比如引入依赖,如下:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.9</version>
</dependency>
- 在上述的MVC配置类中重写如下方法:
- 设置日期的格式化格式是yyyy-MM-dd,此时返回和接收的格式就是
yyyy-MM-dd
- 在配置类中配置的消息转换器属于全局配置,所有的消息都会遵循这种配置。
- 设置日期的格式化格式是yyyy-MM-dd,此时返回和接收的格式就是
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder()
.indentOutput(true)
//指定格式化的日期,这里只是举例,不建议在此处全局配置
.dateFormat(new SimpleDateFormat("yyyy-MM-dd"))
//设置时区,默认是UTC,需要修改成北京时间
.timeZone("GMT 8");
converters.add(new MappingJackson2HttpMessageConverter(builder.build()));
}
注解版
- 在实际的项目中这种方式太鸡肋,实际的需求有实际的变化,因此我们最好能够寻找一种灵活的处理方式,类似注解的方式。
- 在
jackson-databind
中提供了许多的注解,可以供我们使用,可以覆盖全局配置,和全局配置形成一种互补的作用。 @JsonFormat
:日期格式化注解,如下:
//timeZone如果在全局配置过,可以不写
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT 8")
private Date birthDay;
@JsonIgnore
:在返回的JSON字符串中不显示
@JsonIgnore
private String name;
- 其他的注解请参考https://blog.51cto.com/7308310/2310930?source=dra
异常处理器
- springMvc处理异常有三种方式,分别为:
ExceptionHandlerExceptionResolver
:通过调用或 类中的@ExceptionHandler
方法来解决异常,可以结合@ControllerAdvice
DefaultHandlerExceptionResolver
:对一些特殊的异常进行处理ResponseStatusExceptionResolver
:使用@ResponseStatus
解析异常,并根据注解中的值将它们映射到HTTP状态代码SimpleMappingExceptionResolver
:异常和视图的映射,可以自定义指定的异常对应的视图
- 原理:主要的解析逻辑都是在
doResolveException
方法中完成的。
异常处理器执行的顺序
- 异常处理器的执行是有顺序的,优先级高的执行完之后,如果有对应的处理,那么后续的就不再执行。
- 异常处理器的执行顺序如下:
ExceptionHandlerExceptionResolver
DefaultHandlerExceptionResolver
ResponseStatusExceptionResolver
SimpleMappingExceptionResolver
- 四种异常处理器的顺序执行可以形成一种互补的配置。
SimpleMappingExceptionResolver
- 在配置中配置即可
@Bean
public SimpleMappingExceptionResolver simpleMappingExceptionResolver(){
SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver();
//设置默认的视图,如果有的异常没有指定处理,那么使用默认的视图
resolver.setDefaultErrorView("index");
//设置排除的异常
// resolver.setExcludedExceptions();
//指定异常视图映射
Properties properties=new Properties();
properties.put(RuntimeException.class.getName(),"error");
resolver.setExceptionMappings(properties);
return resolver;
}
DefaultHandlerExceptionResolver
- 此类异常解析器只能针对一些特殊的异常进行处理,如下:
Exception | HTTP Status Code |
---|---|
HttpRequestMethodNotSupportedException | 405 (SC_METHOD_NOT_ALLOWED) |
HttpMediaTypeNotSupportedException | 415 (SC_UNSUPPORTED_MEDIA_TYPE) |
HttpMediaTypeNotAcceptableException | 406 (SC_NOT_ACCEPTABLE) |
MissingPathVariableException | 500 (SC_INTERNAL_SERVER_ERROR) |
MissingServletRequestParameterException | 400 (SC_BAD_REQUEST) |
ServletRequestBindingException | 400 (SC_BAD_REQUEST) |
ConversionNotSupportedException | 500 (SC_INTERNAL_SERVER_ERROR) |
TypeMismatchException | 400 (SC_BAD_REQUEST) |
HttpMessageNotReadableException | 400 (SC_BAD_REQUEST) |
HttpMessageNotWritableException | 500 (SC_INTERNAL_SERVER_ERROR) |
MethodArgumentNotValidException | 400 (SC_BAD_REQUEST) |
MissingServletRequestPartException | 400 (SC_BAD_REQUEST) |
BindException | 400 (SC_BAD_REQUEST) |
NoHandlerFoundException | 404 (SC_NOT_FOUND) |
AsyncRequestTimeoutException | 503 (SC_SERVICE_UNAVAILABLE) |
- 不需要声明,默认存在
ResponseStatusExceptionResolver
- 在自定义的异常类上标注
@ResponseStatus
注解,当抛出此种异常的时候,将会响应定义的状态码和提示语
@ResponseStatus(code = HttpStatus.FORBIDDEN,reason = "没有权限")
public class CustomException extends RuntimeException {
}
ExceptionHandlerExceptionResolver
- 集合
@ControllerAdvice
和@RestControllerAdvice
使用 - 方法中能够自动赋值的参数和返回值的类型都在Spring文档上有详细的记载,参考
https://docs.spring.io/spring/docs/5.1.8.RELEASE/spring-framework-reference/web.html#mvc-ann-exceptionhandler-args
- 详细的使用如下:
@ControllerAdvice
public class ExceptionController {
/**
*
*处理FileNotFoundException,返回JSOn数据
*/
@ExceptionHandler(value = ArrayIndexOutOfBoundsException.class)
@ResponseBody
public Object handleFileNotFoundException(Exception ex, HttpServletRequest request, HandlerMethod method){
System.out.println(request.getRequestURI());
System.out.println(method);
System.out.println(ex);
return "index";
}
@ExceptionHandler(value = Exception.class)
public Object handleException(Exception ex, HttpServletRequest request, HandlerMethod method){
System.out.println(request.getRequestURI());
System.out.println(method);
System.out.println(ex);
return "index";
}
}
配置跨域请求
使用注解
- 使用注解
@CrossOrigin
,可以标注在Controller上,也可以标注在方法上,如下:
@CrossOrigin
@PostMapping("/getObj")
public Object getObject(@RequestBody AdminReq req){
System.out.println(req);
return new Admin("陈加兵",22,new Date(),new Date());
}
- 该注解中可以配置各种属性,这里不再细讲,在下面的全局配置中会涉及到。
全局配置
- 全局配置就是在MVC的配置文件中重写方法即可,如下:
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
//允许的源
.allowedOrigins("https://domain2.com")
//允许请求跨域的请求类型
.allowedMethods("PUT", "DELETE")
//允许的请求头
.allowedHeaders("header1", "header2", "header3")
//暴露的请求头
.exposedHeaders("header1", "header2")
//允许携带cookie等用户信息,这样才能实现登录
.allowCredentials(true).maxAge(3600);
}
配置静态资源解析
- springmvc中的DispatcherServlet如果设置了拦截的请求是
/
,那么也会拦截静态资源,但是我们可以在配置文件中配置,如下:
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
//拦截的请求
registry.addResourceHandler("/resources/**")
//资源的位置
.addResourceLocations("/public", "classpath:/static/")
//缓存的时间,单位秒
.setCachePeriod(31556926);
}
- 该配置会在ioc中注册一个
ResourceHttpRequestHandler
,封装在SimpleUrlHandlermapping中。
高级配置
@EnableMvc
注解其实就是注入了一个配置类DelegatingWebMvcConfiguration
,那么我们可以将自定义的配置类实现该类即可完成MVC的高级功能,此时就不需要使用该注解了,如下:
@Configuration
@ComponentScan(basePackages = {"cn.tedu.demo"},includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class, Component.class})},useDefaultFilters = false)
public class AdvanceConfig extends DelegatingWebMvcConfiguration {
}