配置内嵌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 StrartWebApplicationInitializerextends 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 WebMvcConfigimplements WebMvcConfigurer{
}
配置拦截器
/**
* 自定义一个拦截器,实现HandlerInterceptor
*/
@Component
public class CustomInterceptorimplements 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 CustomFilterimplements 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("过滤器销毁");
}
}
配置视图解析器
@Override
public void configureViewResolvers(ViewResolverRegistry registry){
registry.jsp("/WEB_INF/",".jsp");
}
配置ViewController
@Override
public void addViewControllers(ViewControllerRegistry registry){
//定义一个controller,访问路径是/index.do,跳转的视图是index.jsp
registry.addViewController("/index.do").setViewName("index");
}
配置MessageConverters
MappingJackson2HttpMessageConverter
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.9</version>
</dependency>
-
在上述的MVC配置类中重写如下方法:
-
设置日期的格式化格式是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
方法中完成的。
异常处理器执行的顺序
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 CustomExceptionextends 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 AdvanceConfigextends DelegatingWebMvcConfiguration{
}
https://chenjiabing666.github.io/2019/07/31/Springmvc注解版开发/