一次springMVC项目改造为springBoot项目的经历
情景:事情发生在即将离职前二周,接到任务,因为其余项目都已经改造为boot项目,然后目前只剩一个比较大的MVC项目,还没改造完,为了使以后docker部署更加便捷,所以接下去需要把这个MVC项目改造为boot项目;
开工:创建一个新的boot项目用来替代原来的MVC项目,接下去的重点就是MVC的一些相关配置,根据其他项目配置好相关的bean,例如rabbit,redis等,然后再将MVC项目中的dubbo-consumer.xml文件用properties文件进行配置,这里我boot集成dubbo用的jar包是
<dependency>
<groupId>io.dubbo.springboot</groupId>
<artifactId>spring-boot-starter-dubbo</artifactId>
<version>1.0.0</version>
</dependency>
具体如何集成可以参考之前的文章;
这里遇到的第一个问题:启动以后老是报错,报错内容为找不到提供者,这个没问题,因为我提供者是没有启动,但是我MVC项目也是这样启动,但是没有报错;
寻找问题,发现MVC的xml文件配置里有一句
<dubbo:consumer filter="HystrixFilter,MDCFilter,DubboExceptionFilter,-exception" timeout="10000"
check="false"/>
这句的作用是配置了自定义的拦截器,还有超时时间,最后一个是重点,意思是启动的时候不检查提供者是否已经启动(虽然很绕,但是意思就是这个意思);
那么boot里面是如何配置的呢?
看过之前写的文章可以知道,boot集成dubbo的时候,相关配置我是在applicaiton.properties文件里面编写的,如下:
spring.dubbo.scan=**.**.**.**//扫描的dubbo包路径
spring.dubbo.module.default=false
spring.dubbo.consumer.filter=MDCFilter,DubboExceptionFilter,-exception
那么关于消费者的检查如何关闭呢,我刚开始也不知道怎么关闭,那么很简单查看源码,前车之鉴,上面filter可以这样配置,我就点进去查看源码里面有没有check这个字段,答案是有的,如下:
public abstract class AbstractReferenceConfig extends AbstractInterfaceConfig {
private static final long serialVersionUID = -2786526984373031126L;
protected Boolean check;//这个应该就是那个是否关闭检查的属性,filter属性的话可以在进入AbstractInterfaceConfig 查看
protected Boolean init;
protected String generic;
protected Boolean injvm;
protected Boolean lazy;
protected String reconnect;
protected Boolean sticky;
protected Boolean stubevent;
protected String version;
protected String group;
public AbstractReferenceConfig() {
}
}
找到解决方法就简单了,然后在配置文件中在加一句
spring.dubbo.consumer.check=false
启动项目后发现正常启动;关于dubbo相关的配置问题就到这里结束了;
然后就是配置MVC相关的东西了;
这里主要涉及到一个接口WebMvcConfigurer.class,自定义一个类然后实现该接口,代码如下:
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Resource
private AuthInterceptor authInterceptor;
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
}
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
}
@Override
public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
}
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
}
@Override
public void addFormatters(FormatterRegistry registry) {
}
//这个方法是添加自定义的拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authInterceptor).addPathPatterns("/**");
}
//这个方法是对一些静态资源的配置
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**/*.html").addResourceLocations("/**/");
}
@Override
public void addCorsMappings(CorsRegistry registry) {
}
@Override
public void addViewControllers(ViewControllerRegistry registry) {
}
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
}
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(new CurrentUserMethodArgumentResolver());
}
@Override
public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) {
}
//这个方法是添加消息的转化器,例如空值转化,json序列化字段等功能
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
}
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
}
@Override
public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
}
@Override
public void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
}
@Override
public Validator getValidator() {
return null;
}
@Override
public MessageCodesResolver getMessageCodesResolver() {
return null;
}
}
这里用到的不多,具体就那么几个东西,需要注意的地方是添加自定义拦截器的时候,加入你的拦截器里面需要注入spring对象,记得必须要加拦截器交给spring管理,有二种方法,第一种就是我上面所写的;第二种伪代码如下:
@bean
public MyInterceptor myInterceptor(){
return new MyInterceptor();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(myInterceptor()).addPathPatterns("/**");
}
关于filter的配置如下:
@Bean
public FilterRegistrationBean ***FilterRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean(new ***Filter());
registration.addUrlPatterns("/*"); //
registration.setName("***Filter");
registration.setOrder(1);
return registration;
}
@Bean
public FilterRegistrationBean sssFilterRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean(new sssFilter());
registration.addUrlPatterns("/*"); //
registration.setName("sssFilter");
registration.setOrder(2);
return registration;
}
注意这个bean必须配置在启动类里面,否则可能不生效;(我也没试过在别的地方配置,所以也不确定,嘿嘿😄);
一般情况下集成到这里差不多就已经结束了,一切都可以正常使用了;
接下来的问题一般人不太会遇到,可能我比较特殊吧,谁让我遇到了呢;
这个问题就是关于aop的,在MVC项目中一切都是正常的,但是在boot项目中,我启动后请求,发现能够正常进入aop切面,但是运行其中的代码后,我发现无法获取到该方法上面的注解,同事也获取不到该方法的参数名称,那么问题来了,这个到底是什么原因呢?
接下来开始百度,(不要问我为什么不google,我试过google,查出来的结果跟百度相差不大,而且比较穷,没钱翻墙)看看博客,大概看了一上午的博客吧,基本对我的情况都是没什么用的,突然看到一篇文章,出处忘记了,这个作者也是蛮有趣,因为这个问题思考了一晚上没能安心睡,他说到了aop的jdk代理和cglib代理,灵光一闪,我也很可能是这个问题,然后我启动项目测试,发现MVC项目中代理的目标类是该接口的实现类,而boot项目中代理的目标类确是个接口,原因找到了,就在这个地方,那么继续百度boot项目aop代理方式或者boot如何强制使用cglib代理,一找一大堆,并了解到,spring-boot2.0版本一下默认走的是jdk动态代理,而2.0版本以上默认走的是cglib代理(这里我是有疑问的,虽然我用的版本是1.5.4.RELEASE,走的是jdk,但是我同学用的是1.5.9.RELEASE,他跑起来却是正常的),如何强制走cglib呢,在配置文件中添加:
spring.aop.proxy-target-class=true
然后启动项目,一切正常,项目改造到此结束,如果改造过程中还遇到什么问题的话,希望大家一起分享,看过觉得有用的可以点个喜欢收藏一下(QQ:1107156537)