在将SpringMVC项目转移到Springboot上的过程中,主要做了以下的事情
本篇文章除了介绍做了些什么和怎么做之外,会多很多多余的废话,关于对原理的一些探讨,知其然也要知其所以然。
在传统的Spring项目中,多个profile的配置方式首先是在pom.xml文件中写入多个profile,再通过启动项目前先执行一个maven文件来预加载选定的profile环境。加载完之后,执行项目的时候,会根据已加载的Environment,来决定去将哪个.properties文件load到全局变量中。
而在Springboot中对多个profile的管理就非常简单了。
java -jar example.jar --spring.profiles.active=test
在application.properties中添加spring.profiles.active=test
以上两种方法均可启动“test"这个profile,前者在执行上的优先级要高于后者。
(顺便一提,在Springboot里面,这两种方式本质上都是用“外部化配置”的方式,来对Environment进行编辑和替换)
另外, 每个独立的profiles的配置方式为以"application-xxx.properties"格式 ,针对每个不同环境,例如:
当我们需要测试是否正常载入了profile的时候,可以在对应的.properties文件中写入
server.port=9080
在启动的时候就可以看到,是否已经启动了这个端口。
在这里可以顺便提一下Springboot加载配置文件的顺序
在上一面一小节写了针对不同环境的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
这个属性读到了全局变量中。
在传统的Spring项目中,用Mybatis连接数据库
这一切都是在xml配置文件中配置的,比较繁琐。 在Springboot中会尽量去避免这样子的xml配置。
Mybatis现在已经为Springboot提供了支持,我们只需要添加 MyBatis-Spring-Boot-Starter 这个依赖,它就会为我们去做好以下的事情:
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。
/** * 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; } }
这里的作用跟XML比较类似,是将传统的xml表达方式用.java文件来描述出来,本质上还是将datasource一步步注入。
由于示例用的是oracle数据库,所以最后一个exampleSequence是示范如何添加序列。
例如:
@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的主要作用是替代web.xml和spring-mvc.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>
spring-mvc.xml是SpringMVC的配置文件,在这里可以配置我们引入的、需要定制化的bean,例如ViewResolver、multipartResolver、HTTP消息转换器、自定义的拦截器等等。
再讲回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); } }
我写的这个示例文件里面做了几件事情:
这个算是一个题外话,但也是我实际遇到的问题之一。
在实际运行的Springboot项目的时候,我发现了一些在传统Spring项目中没有报错的问题,就是多余的bean注入。
在传统Spring项目中,这是没有报错的,但是在Springboot项目中就报错了。我猜测是因为要注入bean的类方法名取的比较精简的时候,与Springboot本身自动配置的一些bean重复了,就会报错。
所以,把有些不需要注入的bean去掉吧。