#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
<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>
## 说明:需要继承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);
}
}
<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配置了jsp资源解析配置了,继承WebMvcConfigurerAdapter这个配置了跨域了,继承WebMvcConfigurer这个配置了静态资源管理了
-> 简单说明
> WebMvcConfigurationSupport:更丰富的配置,he 另外两个没有关系,内部有基础的具体实现,我们可重写方法实现自定义配置
> WebMvcConfigurer:是Spring内部的一种配置方式,采用JavaBean的形式来代替传统的xml配置文件形式进行针对框架个性化定制。基于java-based方式的spring mvc配置,需要创建一个配置类并实现WebMvcConfigurer 接口
> WebMvcConfigurerAdapter :WebMvcConfigurer的默认实现,在SpringBoot2.0及Spring5.0中WebMvcConfigurerAdapter已被废弃 。官方推荐直接实现WebMvcConfigurer(推荐)或者直接继承WebMvcConfigurationSupport(配置齐全)
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
意思是项目中没有WebMvcConfigurationSupport类型的bean时,自动配置类才会生效;如果继承 WebMvcConfigurationSupport,则需要自己再重写相应的方法;
所以一般我们在项目中可以使用实现WebMvcConfigurer来配置我们的WebMvc(也可以继承WebMvcConfigurerAdapter[这个已经被废弃了在新版中,里面没有什么具体实现]),或者继承类WebMvcConfigurationSupport来重写特定的方法来实现自己的配置,但是两个不可冲突使用(混用),否则会导致一方配置失效;
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
>异常信息: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()));
}
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
UTF-8
${project.basedir}/src/main/lib
src/main/resources
true
**/*
src/main/webapp
META-INF/resources
**/**
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);
}
}
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请求(路径为“/”),然后根据返回的状态进行验证服务的状态是否正常
健康云检测的文档:
是因为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");
}
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;
}
}
/**
* @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");
}
}
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();
}