专栏首页物流IT圈传统SpringMVC到Springboot迁移之路

传统SpringMVC到Springboot迁移之路

最近在把公司的老项目迁移到springboot,遇到了很多坑,包括jar包的升级,代码重构,以及全方位的测试。具体过程参照了简书上的一篇文章。

在将SpringMVC项目转移到Springboot上的过程中,主要做了以下的事情

一、Profile配置

二、全局变量从properties文件读入

三、数据源与Mybatis配置

四、日志文件配置

五、WebConfig配置(包括原有的web.xml和spring-mvc.xml)

六、去掉多余的bean注入

本篇文章除了介绍做了些什么和怎么做之外,会多很多多余的废话,关于对原理的一些探讨,知其然也要知其所以然。

一、Profile配置

在传统的Spring项目中,多个profile的配置方式首先是在pom.xml文件中写入多个profile,再通过启动项目前先执行一个maven文件来预加载选定的profile环境。加载完之后,执行项目的时候,会根据已加载的Environment,来决定去将哪个.properties文件load到全局变量中。

而在Springboot中对多个profile的管理就非常简单了。

可以在jar包用命令行运行时选择profile

java -jar example.jar --spring.profiles.active=test

或者在application.properties这个全局配置中配置

在application.properties中添加spring.profiles.active=test

以上两种方法均可启动“test"这个profile,前者在执行上的优先级要高于后者。(顺便一提,在Springboot里面,这两种方式本质上都是用“外部化配置”的方式,来对Environment进行编辑和替换)

此外每个独立的profiles的配置方式为以"application-xxx.properties"格式,针对每个不同环境,例如:

application-pro.properties 表示预演环境

application-dev.properties 表示开发环境

application-test.properties 表示测试环境

当我们需要测试是否正常载入了profile的时候,可以在对应的.properties文件中写入

server.port=9080

在启动的时候就可以看到,是否已经启动了这个端口。

在这里可以顺便提一下Springboot加载配置文件的顺序,home目录下的devtools全局设置属性( ~/.spring-boot-devtools.properties ,前提是devtools激活)。

测试用例上的@TestPropertySource注解。

测试用例上的@SpringBootTest#properties注解。

命令行参数

来自 SPRING_APPLICATION_JSON 的属性(环境变量或系统属性中内嵌的内联JSON)。

ServletConfig 初始化参数。

ServletContext 初始化参数。

来自于 java:comp/env 的JNDI属性。

Java系统属性(System.getProperties())。

操作系统环境变量。

RandomValuePropertySource,只包含 random.* 中的属性。

没有打进jar包的Profile-specific应用属性( application-{profile}.properties 和YAML变量)。

打进jar包中的Profile-specific应用属性( application-{profile}.properties 和YAML变量)。

没有打进jar包的应用配置( application.properties 和YAML变量)。

打进jar包中的应用配置( application.properties 和YAML变量)。

@Configuration 类上的 @PropertySource 注解。

默认属性(使用 SpringApplication.setDefaultProperties 指定)。

二、全局变量从properties文件读入

在上一面一小节写了针对不同环境的properties配置,这里会写关于如果将这些属性写入到全局变量中,方便后面其他地方直接调用。

/**
* 全局变量
*/
public class Global {
public static String examplePath;
@Value("${example_path}")
  public void setExamplePath(String example) {
    Global.examplePath = examplePath;
  }
}

通过这样子,我们便将.properties文件中的

example_path=http://localhost:9090

这个属性读到了全局变量中。

三、数据源与Mybatis配置

在传统的Spring项目中,用Mybatis连接数据库

首先要创建一个名为datasource的bean,然后将这个datasource装配到SqlSessionFactory中,最后再将SqlSessionFactory装配到MapperScannerConfigurer中,这一切都是在xml配置文件中配置的,比较繁琐。在Springboot中会尽量去避免这样子的xml配置。

Mybatis现在已经为Springboot提供了支持,我们只需要添加MyBatis-Spring-Boot-Starter这个依赖,它就会为我们去做好以下的事情:

自动检测已有的datasource,创建一个SqlSessionFactoryBean的实例SqlSessionFactory,并将datasource装配进去,创建一个SqlSessionTemplate的实例,并将SqlSessionFactory装配进去,自动扫描你的mapper,将它们连接到SqlSessionTemplate,并将它们注册到Spring的上下文,以便将它们注入到其他的bean中。

所以,在Springboot的Mybatis配置中,我们需要去做以下几件事情:

在application-{profile}.properties中填入数据库信息,例如:

spring.datasource.url=jdbc:oracle:thin:@//localhost:1234/example
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=oracle.jdbc.driver.OracleDriver
spring.datasource.maxActive=10
spring.datasource.maxIdle=5
spring.datasource.maxWait=-1

通过这种方式,我们便在Spring上下文中注册了datasource这个bean。

创建一个MybatisConfig文件,用java的方式取代xml:

/**
* Created by caijuzhou on 2018/1/24.
*/
@Configuration
@EnableTransactionManagement
@MapperScan("com.example.db.dao")
public class MybatisConfig {
  @Autowired
  private DataSource dataSource;
  @Bean(name = "sqlSessionFactory")
  public SqlSessionFactory sqlSessionFactoryBean() {
 SqlSessionFactoryBean sqlsession = new SqlSessionFactoryBean();
    sqlsession.setDataSource(dataSource);
    try {
      //添加XML目录
ResourcePatternResolver resolver=new PathMatchingResourcePatternResolver();
  sqlsession.setMapperLocations(resolver.getResources("classpath:mapping/*.xml"));
      return sqlsession.getObject();
    } catch (Exception e) {
      e.printStackTrace();
      throw new RuntimeException(e);
    }
  }
  @Bean
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
    return new SqlSessionTemplate(sqlSessionFactory);
  }
  @Bean
  public PlatformTransactionManager annotationDrivenTransactionManager() {
    return new DataSourceTransactionManager(dataSource);
  }
  @Bean(name = "exampleSequence")
  public OracleSequenceMaxValueIncrementer exampleSequenceBean(){
    OracleSequenceMaxValueIncrementer exampleSequence = new OracleSequenceMaxValueIncrementer();
    exampleSequence.setIncrementerName("EXAMPLE_SEQ");
    exampleSequence.setDataSource(dataSource);
    return exampleSequence;
  }
}

@MapperScan是扫描这个包下面的mapper。

另外这里mapper.xml的位置,是在resource文件夹下面建了一个mapping文件夹,放在下面。

这里的作用跟XML比较类似,是将传统的xml表达方式用.java文件来描述出来,本质上还是将datasource一步步注入。

由于示例用的是oracle数据库,所以最后一个exampleSequence是示范如何添加序列。

对所有mapper的interface注解@Mapper

例如:
@Mapper
public interface UserMapper {
...
}

四、日志文件配置

Logback支持用properties的方式外部化配置,但是对于比较细的配置来说,还是要沿用xml配置。

为了让xml文件从.properties文件读取一些路径之类可能需要经常修改的静态配置,需要在logback-spring.xml中配置

<property resource="application.properties" />
  <property name="log.root.level" value="${log.root.level}" />
  <property name="log.path" value="${log.path}" />
  <property name="log.moduleName" value="${log.module}" />

这样子就可以将application.properties文件中的

log.path=/home/logs/example
log.root.level=INFO
log.module=example

读入到logback-spring.xml中,然后再去调用。

五、WebConfig配置

WebConfig的主要作用是替代web.xml和spring-mvc.xml进行一些基础配置。

1、关于web.xml

传统的Spring项目都有配置一个web.xml文件,这个文件的作用是:当我们把war包放入应用容器(例如tomcat)中运行时,容器会根据web.xml去加载filter(过滤器)、servlet、error-page、welcome-file-list、listener(监听器)、context-param(上下文参数)、resource-ref(资源配置)等配置。

包括ContextLoaderListener这个监听器,就是在这里加载进去,用于在启动容器的时候,自动装配ApplicationContext的配置信息。

<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

这个ApplicationContext是Spring IOC的核心(继承自BeanFactory),所有单例的Bean会在这个时候就被实例化。

以及,SpringMVC中很重要的一个DispatcherServlet也是在这里加载进去,并制定根据哪个xml文件来配置DispatcherServlet。

<servlet>
    <servlet-name>SpringMVC</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:spring-mvc.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
    <!--<async-supported>true</async-supported>-->
</servlet>

2、关于spring-mvc.xml

spring-mvc.xml是SpringMVC的配置文件,在这里可以配置我们引入的、需要定制化的bean,例如ViewResolver、multipartResolver、HTTP消息转换器、自定义的拦截器等等。

以上都与Springboot无关,主要是为了知其然也知其所以然,如果不感兴趣的可以不看。

再讲回Springboot的配置。Springboot有一个说法叫“约定优于配置”,就是尽量用约定的方式,而不是特地去针对性地配置(需要特殊配置的时候再去配置)。

引入spring-boot-starter-web这个“开箱即用”的依赖之后,spring-boot-starter-web下包含了一个spring-boot-autoconfigure。

有了这个依赖之后,就可以使用@EnableAutoCongiguration注解。这个注解就会根据引入的依赖来猜测你需要的Spring配置并帮你配置好。因为已经引入了spring-boot-starter-web的话,这个注解就会将web相关的配置配置好。

另外,@SpringBootApplication这个注解中已经包含了@EnableAutoCongiguration注解。所以只要在启动类ExampleServerApplication上注解@SpringBootApplication就可以自动把web配置给配置好了。

当然,我们可能还有一些特殊的配置,这时候就可以创建一个WebConfig去定制

/**
* Created by caijuzhou on 2018/1/24.
*/
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
  @Override
  public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
    converters.add(marshallingHttpMessageConverter());
  }


  public MarshallingHttpMessageConverter marshallingHttpMessageConverter(){
    MarshallingHttpMessageConverter marshallingHttpMessageConverter = new MarshallingHttpMessageConverter();
    List<MediaType> mediaTypes = new ArrayList<MediaType>();
    mediaTypes.add(MediaType.TEXT_XML);
    mediaTypes.add(MediaType.APPLICATION_XML);
    XStreamMarshaller xStreamMarshaller=new XStreamMarshaller();
    marshallingHttpMessageConverter.setSupportedMediaTypes(mediaTypes);
    marshallingHttpMessageConverter.setMarshaller(xStreamMarshaller);
    marshallingHttpMessageConverter.setUnmarshaller(xStreamMarshaller);
    return marshallingHttpMessageConverter;
  }
  //配置文件上传
  @Bean(name = {"multipartResolver"})
  public MultipartResolver multipartResolver(){
    CommonsMultipartResolver commonsMultipartResolver=new CommonsMultipartResolver();
    commonsMultipartResolver.setDefaultEncoding("utf-8");
    commonsMultipartResolver.setMaxUploadSize(10485760000L);
    commonsMultipartResolver.setMaxInMemorySize(40960);
    return commonsMultipartResolver;
  }
  //异常处理
  @Bean
  public ExceptionHandler exceptionResolver(){
    ExceptionHandler exceptionHandler = new ExceptionHandler();
    return exceptionHandler;
  }
  //拦截器
  @Override
  public void addInterceptors(InterceptorRegistry registry){
    registry.addInterceptor(new LogInterceptor()).addPathPatterns("/**");
    super.addInterceptors(registry);
  }
}

我写的这个示例文件里面做了几件事情:

引入一个XML的Http消息转换器

引入multipartResolver

引入自定义的异常处理器

引入自定义拦截器

六、去掉多余的bean注入

这个算是一个题外话,但也是我实际遇到的问题之一。

在实际运行的Springboot项目的时候,我发现了一些在传统Spring项目中没有报错的问题,就是多余的bean注入。

在传统Spring项目中,这是没有报错的,但是在Springboot项目中就报错了。我猜测是因为要注入bean的类方法名取的比较精简的时候,与Springboot本身自动配置的一些bean重复了,就会报错。

所以,把有些不需要注入的bean去掉吧。

链接:https://www.jianshu.com/p/016759099e35

本文分享自微信公众号 - 物流IT圈(exiter18),作者:SedroZhou

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-01-20

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 利用TensorFlow.js构建一个神经网络

    神经网络(Neural Network)是深度学习的基础,基本概念包括:神经元,层,反向传播等等。如果细讲我估计没有五到十篇文章那是讲不完的。简单说它模拟了大脑...

    物流IT圈
  • 使用BTrace性能跟踪

    BTrace是一个开源项目,开始于2007年,名声鹊起得益于2008年的JavaOne会议。

    物流IT圈
  • C10K 问题引发的技术变革

    服务器应用领域很古老很出名的一个问题,大意是说单台服务器要同时支持并发 10K 量级的连接,这些连接可能是保持存活状态的。

    物流IT圈
  • 设计模式之中介者模式(mediator模式)引入中介者模式中介者模式的实例中介者模式分析

    大家想象一下有十个人要共同完成一个工作,他们要互相合作和沟通,并且根据对方的通知可能要改变自己的状态,但这通常会带来很多问题,流程过于复杂,使得每个人不仅要专注...

    desperate633
  • JVM-周阳讲解

    用户5927264
  • Spring 中的自动装配,如果遇到多个实例如何处理?

    标记了@Autowired 注解的字段/方法,会由 Spring 容器自动的赋值一个实例化的对象。@Autowired 总是采用 byType 的方式实现自动装...

    水货程序员
  • 唯品会特卖秒杀系统的架构设计和实战全集(艾编程Java架构师视频教程)

    【学完本节课你将掌握哪些点】 1. 电商平台秒杀系统的由来; 2. 单机十万级别qps的秒杀系统的架构和设计; 3. 如何使用docker快速搭建中间件服...

    艾编程
  • python 2.7后的字符串赋值

        在使用python的时候,常常会遇到给一个变量赋值一长串的字符串。而在这一长串的字符串中又往往夹杂其他变量,而在python最常用的网页处理环境中,如果...

    py3study
  • 探索|比VR/AR更进一步的脑机技术,会给我们带来什么?

    作为一项前沿的技术,VR/AR为人类带来了无限可能,包括虚实结合的方式、超越现实的体验,以及帮助人类更好地理解世界(通过将数据可视化)等。不过,VR/AR技术仍...

    VRPinea
  • python爬虫爬取2020年中国大学排名

    本文的文字及图片来源于网络,仅供学习、交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理。

    松鼠爱吃饼干

扫码关注云+社区

领取腾讯云代金券