• -------------------------------------------------------------
  • ====================================

从SpringMVC迁移到Springboot

springcloud dewbay 5年前 (2019-04-12) 1704次浏览 已收录 0个评论 扫描二维码

在将 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 配置中,我们需要去做以下几件事情:

  1. 在 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。

  1. 创建一个 MybatisConfig 文件,用 java 的方式取代 xml:
/**
 * Created by WuTaoyu on 2017/12/7.
 */
@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 是示范如何添加序列。

  1. 对所有 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>
  1. 关于 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 WuTaoyu on 2017/12/8.
 */
@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/d13a9a1e2aaa
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。


露水湾 , 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权
转载请注明原文链接:从SpringMVC迁移到Springboot
喜欢 (0)
[]
分享 (0)
关于作者:
发表我的评论
取消评论

表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址