SpringMvc改装为SpringBoot的问题总结

#SpringMvc改造SpringBoot问题汇总

特殊说明

## 原来项目环境
1. spring-webmvc-4.0.4.RELEASE
2. mybatis-spring-1.2.2
    ↓↓↓↓↓↓
    ↓↓↓↓↓↓
    ↓↓↓↓↓↓
3. JSP页面项目
## 改造环境信息
1. spring-webmvc-5.0.7.RELEASE
2. mybatis-spring-1.3.2; mybatis-spring-boot-starter-1.3.2
3. spring-boot-starter-2.0.2.RELEASE

首先需要SpringBoot的Jar包支持

    
    <parent>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-parentartifactId>
        <version>2.0.2.RELEASEversion>
    parent>
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starterartifactId>
    dependency>
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-aopartifactId>
    dependency>
    <dependency>
      <groupId>org.springframework.bootgroupId>
      <artifactId>spring-boot-starter-testartifactId>
      <scope>testscope>
    dependency>
    <dependency>
      <groupId>org.springframework.bootgroupId>
      <artifactId>spring-boot-starter-webartifactId>
    dependency>


    
    
    <dependency>
      <groupId>jstlgroupId>
      <artifactId>jstlartifactId>
      <version>1.2version>
    dependency>
    
    
    <dependency>
      <groupId>org.apache.tomcat.embedgroupId>
      <artifactId>tomcat-embed-jasperartifactId>
      
      
    dependency>

    
    <dependency>
      <groupId>org.springframework.bootgroupId>
      <artifactId>spring-boot-starter-tomcatartifactId>
      
      
    dependency>

使用外置Tomcat特殊说明


## 说明:需要继承SpringBootServletInitializer然后实现configure方法就可以了,示例如下



@EnableCat
@EnablePay
@EnableApolloConfig
@EnableRedisCache
@EnableRedisLock
@EnableElasticJob
@EnableGlobalTransaction
@EnableTransactionManagement
@SpringBootApplication
@ServletComponentScan(basePackages = {"com.hhz", "com.zhiyi"})
public class HhzAdminApplication extends SpringBootServletInitializer {

  public static void main(String[] args) {
    SpringApplication.run(HhzAdminApplication.class, args);
  }

  /**
   * @Description: 使用外置TOMCAT
   * @Param: builder
   * @return: org.springframework.boot.builder.SpringApplicationBuilder
   * @Author: peikunkun
   * @Date: 2020/6/8 0008 上午 9:54
   */
  @Override
  protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
    return builder.sources(HhzAdminApplication.class);
  }
}

logback的日志配置





  <contextName>logbackcontextName>

  
  

  
  
  
  
  
  
  

  
  
    
    
      <level>INFOlevel>
    filter>
    <encoder>
      <Pattern>${CONSOLE_LOG_PATTERN}Pattern>
      
      <charset>UTF-8charset>
    encoder>
  appender>


  
    
    <file>${log.path}/log.logfile>
    
    <encoder>
      <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%npattern>
      <charset>UTF-8charset>
    encoder>
    
    
      
      <fileNamePattern>${log.path}/%d{yyyy-MM-dd}/log-%i.logfileNamePattern>
      
        <maxFileSize>50MBmaxFileSize>
      timeBasedFileNamingAndTriggeringPolicy>
      
      <maxHistory>15maxHistory>
    rollingPolicy>
  appender>

  

  
  
    
      
    root>
  springProfile>


  
  
    
      
    root>
  springProfile>
configuration>

改装之后出现的问题

WebMvcConfigurationSupport,WebMvcConfigurerAdapter,WebMvcConfigurer配置多个不生效的问题的问题

-> 问题描述
> 在代码中继承WebMvcConfigurationSupport配置了jsp资源解析配置了,继承WebMvcConfigurerAdapter这个配置了跨域了,继承WebMvcConfigurer这个配置了静态资源管理了

-> 简单说明
> WebMvcConfigurationSupport:更丰富的配置,he 另外两个没有关系,内部有基础的具体实现,我们可重写方法实现自定义配置
> WebMvcConfigurer:是Spring内部的一种配置方式,采用JavaBean的形式来代替传统的xml配置文件形式进行针对框架个性化定制。基于java-based方式的spring mvc配置,需要创建一个配置类并实现WebMvcConfigurer 接口
> WebMvcConfigurerAdapter :WebMvcConfigurer的默认实现,在SpringBoot2.0及Spring5.0中WebMvcConfigurerAdapter已被废弃 。官方推荐直接实现WebMvcConfigurer(推荐)或者直接继承WebMvcConfigurationSupport(配置齐全)
  • WebMvcConfigurerAdapter的继承结构
    SpringMvc改装为SpringBoot的问题总结_第1张图片
  • 问题关键点
    SpringMvc改装为SpringBoot的问题总结_第2张图片

@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
意思是项目中没有WebMvcConfigurationSupport类型的bean时,自动配置类才会生效;如果继承 WebMvcConfigurationSupport,则需要自己再重写相应的方法;

所以一般我们在项目中可以使用实现WebMvcConfigurer来配置我们的WebMvc(也可以继承WebMvcConfigurerAdapter[这个已经被废弃了在新版中,里面没有什么具体实现]),或者继承类WebMvcConfigurationSupport来重写特定的方法来实现自己的配置,但是两个不可冲突使用(混用),否则会导致一方配置失效;

SpringBoot启动嵌入式Tomcat时出现jar包扫描找不到问题解决

springboot启动嵌入式tomcat报错找不到jar包,关键字:FileNotFoundException,derbyLocale_cs.jar,StandardJarScanner.scan…

因为Tomcat在扫描包路径的时候感觉,扫描路径有问题,追踪源码在TomcatEmbeddedServletContainerFactory#prepareContext()有个tldSkipPatterns的东西,可以告诉Tomcat不扫描指定Jar包的配置

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.http.LegacyCookieProcessor;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @description: tomcat容器扫描包路径配置问题
 * @author: peikunkun
 * @create: 2020-06-11 09:32
 **/
@Slf4j
@Configuration
public class TomcatContainerConfig {

  /**
   * 跳过tomcat的扫描配置
   */
  @Value("${com.pkk.tomcat.tldSkipPatterns:}")
  private String[] tldSkipPatterns;

  /**
   * @Description: tomcat的后置处理器
   * @return: org.springframework.beans.factory.config.BeanPostProcessor
   * @Author: peikunkun
   * @Date: 2020/6/11 0011 上午 9:38
   */
  @Bean
  public BeanPostProcessor tomcatContainerPostProcessor() {
    List notEmptyTldSkipPatterns = Arrays.stream(tldSkipPatterns)
        .filter(tldSkipPattern -> !tldSkipPattern.trim().isEmpty())
        .collect(Collectors.toList());
    log.info("[Tomcat container scan]-[Postprocessing configuration]-Exclude jar package[{}]", notEmptyTldSkipPatterns);
    return new BeanPostProcessor() {
      @Override
      public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
      }

      @Override
      public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (beanName.equals("tomcatServletWebServerFactory") && bean instanceof TomcatServletWebServerFactory) {
          TomcatServletWebServerFactory factory = (TomcatServletWebServerFactory) bean;
          if (!notEmptyTldSkipPatterns.isEmpty()) {
            factory.addTldSkipPatterns(notEmptyTldSkipPatterns.toArray(new String[0]));
          }
        }
        return bean;
      }
    };
  }
}



#然后在配置文件中配置就可以了
com.pkk.tomcat.tldSkipPatterns = mchange-commons-java-0.2.10.jar,jaxb-api.jar,activation.jar,jsr173_1.0_api.jar,jaxb1-impl.jar

Tomcat8.5前与后Cookie的处理机制改变,造成解析异常的处理

>异常信息:An invalid domain [.xxx.com] was specified for this cookie
>异常分析:tomcat8.5之后,其对cookie的处理机制就变的不同了
>解决方法:其实就两种处理方式,一种Rfc6265CookieProcessor,一种是LegacyCookieProcessor,而8.5之后默认使用Rfc6265CookieProcessor;Rfc6265CookieProcessor的domain域只能以字母或数字开头,LegacyCookieProcessor是以 . 开头;



/**
   * @Description: 一种Rfc6265CookieProcessor,一种是LegacyCookieProcessor,而8.5之后默认使用前者,这两种在用法上可以自行百度,前面一种的domain域只能以字母或数字开头,而后者则是以
   * . 开头,所以有两种方法
   * 

* 解析cookie的方式不同 * @return: org.springframework.boot.web.server.WebServerFactoryCustomizer * @Author: peikunkun * @Date: 2020/6/16 0016 下午 1:22 */ @Bean public WebServerFactoryCustomizer cookieProcessorCustomizer() { return (factory) -> factory.addContextCustomizers( //不以 . 开头的用这个 //(context) -> context.setCookieProcessor(new Rfc6265CookieProcessor())); //以.开头用这个 (context) -> context.setCookieProcessor(new LegacyCookieProcessor())); }

Tomcat的外置内置的不同打包方式

  • jar打包方式[内置Tomcat],内置Tomcat上面也讲过

jar



 
    
      org.apache.tomcat.embed
      tomcat-embed-jasper
      
      
    

    
    
      org.springframework.boot
      spring-boot-starter-tomcat
      
      
    



  
    
      
        
          org.springframework.boot
          spring-boot-maven-plugin
          
          1.4.2.RELEASE
          
            true
          
          
            
              
                repackage
              
            
          
        

        
        
          maven-war-plugin
          ${maven-war-plugin.version}
          
            
            false
          
        
      
    


    
    
      
        org.apache.maven.plugins
        maven-compiler-plugin
        ${maven-compiler-plugin.version}
        
          1.8
          1.8
          UTF-8
          
          
            ${project.basedir}/src/main/lib
          
        
      
    

    
    
      
        src/main/resources
        true
        
          **/*
        
      
      
      
        src/main/webapp
        META-INF/resources
        
          **/**
        
      
    
  

war打包方式[外部Tomcat]


war


 
    
      org.apache.tomcat.embed
      tomcat-embed-jasper
      
      provided
    

    
    
      org.springframework.boot
      spring-boot-starter-tomcat
      
      provided
    




    
        
        
            org.apache.maven.plugins
            maven-surefire-plugin
            2.4.2
            
                true
            
        
    



//修改为Tomcat外置容器启动的配置
@SpringBootApplication
public class HhzAdminApplicationextends SpringBootServletInitializer {

    public static void main(String[] args) {
        SpringApplication.run(HhzAdminApplication.class, args);
    }
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
        return builder.sources(HhzAdminApplication.class);
    }
}

阿里云的健康Http/Https健康检测导致Nginx一会出现502

100.116.186.46 - - [17/Jun/2020:11:22:14 +0800] "HEAD / HTTP/1.0" 404 0 "-" "-"
100.116.186.68 - - [17/Jun/2020:11:22:14 +0800] "HEAD / HTTP/1.0" 404 0 "-" "-"
100.116.186.24 - - [17/Jun/2020:11:22:14 +0800] "HEAD / HTTP/1.0" 404 0 "-" "-"
100.116.186.2 - - [17/Jun/2020:11:22:14 +0800] "HEAD / HTTP/1.0" 404 0 "-" "-"
100.116.186.25 - - [17/Jun/2020:11:22:14 +0800] "HEAD / HTTP/1.0" 404 0 "-" "-"
100.116.186.101 - - [17/Jun/2020:11:22:14 +0800] "HEAD / HTTP/1.0" 404 0 "-" "-"
100.116.186.73 - - [17/Jun/2020:11:22:14 +0800] "HEAD / HTTP/1.0" 404 0 "-" "-"
100.116.186.83 - - [17/Jun/2020:11:22:14 +0800] "HEAD / HTTP/1.0" 404 0 "-" "-"
100.116.186.35 - - [17/Jun/2020:11:22:14 +0800] "HEAD / HTTP/1.0" 404 0 "-" "-"
100.116.186.100 - - [17/Jun/2020:11:22:14 +0800] "HEAD / HTTP/1.0" 404 0 "-" "-"
100.116.186.39 - - [17/Jun/2020:11:22:14 +0800] "HEAD / HTTP/1.0" 404 0 "-" "-"
100.116.186.97 - - [17/Jun/2020:11:22:14 +0800] "HEAD / HTTP/1.0" 404 0 "-" "-"
100.116.186.92 - - [17/Jun/2020:11:22:14 +0800] "HEAD / HTTP/1.0" 404 0 "-" "-"
100.116.186.23 - - [17/Jun/2020:11:22:14 +0800] "HEAD / HTTP/1.0" 404 0 "-" "-"
100.116.186.9 - - [17/Jun/2020:11:22:14 +0800] "HEAD / HTTP/1.0" 404 0 "-" "-"
100.116.186.125 - - [17/Jun/2020:11:22:14 +0800] "HEAD / HTTP/1.0" 404 0 "-" "-"
100.116.186.69 - - [17/Jun/2020:11:22:14 +0800] "HEAD / HTTP/1.0" 404 0 "-" "-"
100.116.186.91 - - [17/Jun/2020:11:22:14 +0800] "HEAD / HTTP/1.0" 404 0 "-" "-"
100.116.186.105 - - [17/Jun/2020:11:22:14 +0800] "HEAD / HTTP/1.0" 404 0 "-" "-"
100.116.186.112 - - [17/Jun/2020:11:22:14 +0800] "HEAD / HTTP/1.0" 404 0 "-" "-"
100.116.186.10 - - [17/Jun/2020:11:22:14 +0800] "HEAD / HTTP/1.0" 404 0 "-" "-"
100.116.186.121 - - [17/Jun/2020:11:22:14 +0800] "HEAD / HTTP/1.0" 404 0 "-" "-"
100.116.186.21 - - [17/Jun/2020:11:22:14 +0800] "HEAD / HTTP/1.0" 404 0 "-" "-"
100.116.186.87 - - [17/Jun/2020:11:22:14 +0800] "HEAD / HTTP/1.0" 404 0 "-" "-"
100.116.186.13 - - [17/Jun/2020:11:22:14 +0800] "HEAD / HTTP/1.0" 404 0 "-" "-"
100.116.186.71 - - [17/Jun/2020:11:22:14 +0800] "HEAD / HTTP/1.0" 404 0 "-" "-"
100.116.186.83 - - [17/Jun/2020:11:22:14 +0800] "HEAD / HTTP/1.0" 404 0 "-" "-"
100.116.186.120 - - [17/Jun/2020:11:22:14 +0800] "HEAD / HTTP/1.0" 404 0 "-" "-"
100.116.186.64 - - [17/Jun/2020:11:22:14 +0800] "HEAD / HTTP/1.0" 404 0 "-" "-"
100.116.186.46 - - [17/Jun/2020:11:22:14 +0800] "HEAD / HTTP/1.0" 404 0 "-" "-"
100.116.186.67 - - [17/Jun/2020:11:22:14 +0800] "HEAD / HTTP/1.0" 404 0 "-" "-"
100.116.186.75 - - [17/Jun/2020:11:22:14 +0800] "HEAD / HTTP/1.0" 404 0 "-" "-"
100.116.186.45 - - [17/Jun/2020:11:22:14 +0800] "HEAD / HTTP/1.0" 404 0 "-" "-"
100.116.186.24 - - [17/Jun/2020:11:22:14 +0800] "HEAD / HTTP/1.0" 404 0 "-" "-"
100.116.186.116 - - [17/Jun/2020:11:22:14 +0800] "HEAD / HTTP/1.0" 404 0 "-" "-"
100.116.186.25 - - [17/Jun/2020:11:22:14 +0800] "HEAD / HTTP/1.0" 404 0 "-" "-"

改造完成之后不上线上,一会请求就变成502了,打印nginx的请求日志,发现大量的HEAD请求,并且返回404
原因分析:经过一番排查,发现是阿里云的健康Http/Https检测,发送head请求(路径为“/”),然后根据返回的状态进行验证服务的状态是否正常
健康云检测的文档:
SpringMvc改装为SpringBoot的问题总结_第3张图片

是因为SpringBoot的路径“/”这边我没有配置,然后请求返回404和与设定的状态不符,阿里云负载认为服务有问题,就不再往负载上操作了,就直接返回502了

 /**
   * @Description: 健康检查
   * @Param: request
   * @return: void
   * @Author: peikunkun
   * @Date: 2020/6/17 0017 下午 10:02
   */
  @RequestMapping("/")
  @ResponseBody
  public void healthCheck(HttpServletRequest request) {
    log.debug(request.getRemoteAddr() + "--->HEALTH CHECK");
  }

异常的页面的错误,比如404,502,500状态码的处理

import javax.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

/**
 * @description: 异常控制处理器
 * @author: peikunkun
 * @create: 2020-06-17 16:30
 **/
@Slf4j
@Controller
public class ExceptionHandleController implements ErrorController {



  /**
   * 错误页面路径
   */
  public static final String ERROR_PATH = "/error";

  /**
   * Returns the path of the error page.
   *
   * @return the error path
   */
  @Override
  public String getErrorPath() {
    return ERROR_PATH;
  }


  /**
   * @Description: 重写错误页面
   * @Param: request
   * @return: java.lang.String
   * @Author: peikunkun
   * @Date: 2020/6/17 0017 下午 4:32
   */
  @RequestMapping("/error")
  public String handleError(HttpServletRequest request) {
    //获取statusCode:401,404,500
    Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
    if (null == statusCode) {
      statusCode = 500;
    }

    String redirect = "";
    switch (statusCode) {
      case 401:
      case 404:
      case 403:
      case 500:
      default:
        redirect= "/badpage/index/noparam";
    }
    return "redirect:" + redirect;
  }
}

web端基础设置,静态资源配置,jsp解析配置->参考示例

/**
 * @description: 过滤器的配置
 * @author: peikunkun
 * @create: 2020-06-08 17:58
 **/
@Slf4j
public class WebMvcResolveConfig extends WebMvcConfigurationSupport {

  /**
   * @Description: jsp视图解析器
   * @return: org.springframework.web.servlet.view.InternalResourceViewResolver
   * @Author: peikunkun
   * @Date: 2020/6/9 0009 上午 9:54
   */
  @Bean
  public InternalResourceViewResolver viewResolver() {
    log.info("[Resource Configuretion]-[Configure JSP resolution resource configuration]");
    InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
    viewResolver.setPrefix("/WEB-INF/jsp/");
    viewResolver.setSuffix(".jsp");
    return viewResolver;
  }


  /**
   * @Description: Override this method to add resource handlers for serving static resources.
   * 

* 修改支持我们的静态资源信息 * @Param: registry * @return: void * @Author: peikunkun * @Date: 2020/6/9 0009 上午 9:58 */ @Override protected void addResourceHandlers(ResourceHandlerRegistry registry) { log.info("[Resource Configuration]-[Configure static / profile resource path configuration]"); //请求路径 registry.addResourceHandler("/**") //在项目中的资源路径 .addResourceLocations("classpath:/META-INF/resources/") .addResourceLocations("classpath:/resources/") .addResourceLocations("classpath:/static/") .addResourceLocations("classpath:/public/"); super.addResourceHandlers(registry); } } ------------------------ @Slf4j @Configuration public class WebConfigurationHandleConfig extends WebMvcResolveConfig { @Bean public LoginHandlerInterceptor loginHandlerInterceptor() { return new LoginHandlerInterceptor(); } /** * Override this method to add Spring MVC interceptors for pre- and post-processing of controller invocation. * * @param registry * @see InterceptorRegistry */ @Override protected void addInterceptors(InterceptorRegistry registry) { super.addInterceptors(registry); log.info("[Resource Configuretion]-[Resource configuration filter]"); //拦截匹配PATH请求 registry.addInterceptor(loginHandlerInterceptor()) .addPathPatterns("/**/**") .excludePathPatterns("/static/**"); } /** * @Description: 配置默认的静态资源处理器[SpringMvc会默认的识别] * @Param: configurer * @return: void * @Author: peikunkun * @Date: 2020/6/17 0017 下午 7:54 */ @Override public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { configurer.enable("default"); } }

扩展知识

  1. WebMvcConfigurer接口方法说明

public interface WebMvcConfigurer {
    void configurePathMatch(PathMatchConfigurer var1);
    //这个方法是专门用来配置内容裁决的一些参数的(/* 是否通过请求Url的扩展名来决定media type */configurer.favorPathExtension(true)  /* 不检查Accept请求头 */.ignoreAcceptHeader(true).parameterName("mediaType")/* 设置默认的media yype */.defaultContentType(MediaType.TEXT_HTML) /* 请求以.html结尾的会被当成MediaType.TEXT_HTML*/.mediaType("html", MediaType.TEXT_HTML)/* 请求以.json结尾的会被当成MediaType.APPLICATION_JSON*/.mediaType("json", MediaType.APPLICATION_JSON);)
    void configureContentNegotiation(ContentNegotiationConfigurer var1);
    void configureAsyncSupport(AsyncSupportConfigurer var1);
    /* 默认静态资源处理器 (这个Handler也是用来处理静态文件的,它会尝试映射/。当DispatcherServelt映射/时(/ 和/ 是有区别的),并且没有找到合适的Handler来处理请求时,就会交给DefaultServletHttpRequestHandler 来处理。注意:这里的静态资源是放置在web根目录下,而非WEB-INF 下)*/
    void configureDefaultServletHandling(DefaultServletHandlerConfigurer var1);
    //类型转换器和格式化器
    void addFormatters(FormatterRegistry var1);
    /* 拦截器配置addPathPatterns("/")对所有请求都拦截.excludePathPatterns("/index.html","/","/user/login","/static/");排除指定拦截*/
    void addInterceptors(InterceptorRegistry var1);
   /*静态资源处理如registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");*/
    void addResourceHandlers(ResourceHandlerRegistry var1);
    //跨域
    void addCorsMappings(CorsRegistry var1);
   /* 视图跳转控制器 */
    void addViewControllers(ViewControllerRegistry var1);
   /* 配置视图解析器 */
    void configureViewResolvers(ViewResolverRegistry var1);
    void addArgumentResolvers(List var1);
    void addReturnValueHandlers(List var1);
    //配置消息转换器
    void configureMessageConverters(List> var1);
    void extendMessageConverters(List> var1);
    void configureHandlerExceptionResolvers(List var1);
    void extendHandlerExceptionResolvers(List var1);
    Validator getValidator();
    MessageCodesResolver getMessageCodesResolver();
}

你可能感兴趣的:(SpringBoot的学习,SpringMVC的学习,Spring框架)