转载

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 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 方法中完成的。

异常处理器执行的顺序

  • 异常处理器的执行是有顺序的,优先级高的执行完之后,如果有对应的处理,那么后续的就不再执行。
  • 异常处理器的执行顺序如下:
    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 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注解版开发/
正文到此结束
Loading...