Spring工程转成Spring Boot工程
来源:互联网 发布:yum安装kernel devel 编辑:程序博客网 时间:2024/06/16 18:21
Spring工程转成Spring Boot工程
最近小研究了一下SpringBoot,将平时用到的框架工程由纯Spring替换成SpringBoot。本文就是记录这次变动过程中的一些知识点和坑。
什么是SpringBoot
本人比较菜,没法用一句话给SpringBoot下一个定义,或者一句话说出SpringBoot的特点。以下是我对SpringBoot的一些理解。
- 其实SpringBoot的基础就是Spring,它在Spring的基础上做了很多其它的工作,如取消了applicationContext.xml配置项的使用,通过application.properties或者application.yml,以及Java Configuration方式来配置。如果是web工程可以不用web.xml(这个其实是Serverlet 3.0的特性)
- 内嵌了web容器,可以通过打成jar包,然后通过java -jar的方式启动工程。
- 各种starter工程帮我们做了很多事情。比如你想开发一个web工程,可以通过引入spring-boot-starter-web工程来进行配置。这样你就不需要配置很多,比如对MessageConverter的支持等等一些跟web相关的配置。(在替换过程中确实体会到了这个好处,很方便)正如很多教程里说的,这种starter工程还帮助我们屏蔽了不同lib包版本不兼容的问题,你只需要指定你想要用的SpringBoot版本,SpringBoot自己会帮你处理这些依赖。
替换历程
初始工程
想要使用SpringBoot其实很简单,可以去Spring Boot网站上,通过网站生成你想要的SpringBoot初始工程,在这里你可以选择构建工具Maven或者Gradle,SpringBoot的版本,工程相关信息,以及需要的SpringBoot starter。
有了工程,就可以开始迁移了。比如我替换的是一个Web工程,所以我引入了spring-boot-starter-web。如果你看spring-boot-starter-web的pom文件,你会发现它包含了spring web,spring mvc,jackson等一些列跟web开发相关的spring依赖包。这些它都帮我们干了,很方便。
数据库
接下来替换的是数据库,我使用的spring的jdbcTemplate进行数据库访问。数据库用的是MySQL数据库。首先引入另外一个starter–spring-boot-starter-jdbc,让工程对jdbc有支持,这里面就有spring-jdbc的依赖包。我们就可以在工程里使用JDBCTemplate了。然后需要配置数据源。按照原来Spring的方式,我们可以在配置文件中加上这一段:
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"/> </bean> <!--配置数据源 --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName"> <value>com.mysql.jdbc.Driver</value> </property> <property name="url"> <value>jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8 </value> </property> <property name="username"> <value>root</value> </property> <property name="password"> <value>11111111</value> </property> </bean></beans>
这么一大段配置文件,其实就干了三件事:
- 生成一个dataSource
- 生成一个jdbcTemplate
- 开启事物
但是使用SpringBoot之后不需要配置这么多了。jdbcTemplate在前面已经说过了,只要引入了starter-jdbc就可以直接使用了。那么我们还缺少dataSource和事务。
dataSource很好配置,我使用的是application.yml。
spring: datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8 username: root password: 11111111
只要上面这一段,就配置好数据源了。是不是很简单。
那么事务呢?开启事务很简单,需要一个注解@EnableTransactionManagement
这个注解可以跟@SpringBootApplication
放在一起,表示开启事务管理。然后在具体使用事务的地方使用@Transactional
即可。是不是依然很方便?
JNDI
在我们的项目工程中,使用到了JNDI去配置数据源。而且由于jndi的读取方式被写死在了底层依赖包里,我们没法修改,为了能够方便启动项目,还需要对jndi进行处理。(正常工作run Application.java就可以启动项目。而如果不处理JNDI,就得打包成war,然后放入外部的web容器里,再启动外部容器)
Spring Boot是自带web容器的。(web容器在哪我也不知道)这样导致我们不太好配置JNDI数据源。但是Spring Boot为内部的web容器提供了一个加入JNDI的方式。
@Bean public TomcatEmbeddedServletContainerFactory tomcatFactory() { return new TomcatEmbeddedServletContainerFactory() { @Override protected TomcatEmbeddedServletContainer getTomcatEmbeddedServletContainer( Tomcat tomcat) { tomcat.enableNaming(); return super.getTomcatEmbeddedServletContainer(tomcat); } @Override protected void postProcessContext(Context context) { ContextResource resource = new ContextResource(); resource.setName("cache/memcached"); resource.setType(DataSource.class.getName()); resource.setProperty("auth", "Container"); resource.setProperty("type", "com.letvpicture.common.memcached.client.MemcacheClient"); resource.setProperty("factory", "com.letvpicture.common.memcached.client.MemcacheClientFactory"); resource.setProperty("poolSize", "40"); resource.setProperty("serverAddresses", "127.0.0.1:11211"); context.getNamingResources().addResource(resource); resource = new ContextResource(); resource.setName("data/mongodb"); resource.setType(DataSource.class.getName()); resource.setProperty("auth", "Container"); resource.setProperty("type", "com.letvpicture.common.mongo.client.MongoDataBaseClient"); resource.setProperty("factory", "com.letvpicture.common.mongo.client.MongoClientFactory"); resource.setProperty("closeMethod", "shutdown"); resource.setProperty("host", "localhost"); resource.setProperty("port", "27017"); resource.setProperty("authDb", "wz"); resource.setProperty("username", "admin"); resource.setProperty("password", "111"); resource.setProperty("minPoolSize", "10"); resource.setProperty("maxPoolSize", "100"); resource.setProperty("maxWaitTime", "10000"); context.getNamingResources().addResource(resource); } }; } @Bean(destroyMethod="") public DataSource jndiDataSource() throws IllegalArgumentException, NamingException { JndiObjectFactoryBean bean = new JndiObjectFactoryBean(); bean.setJndiName("java:comp/env/jdbc/membership"); bean.setProxyInterface(DataSource.class); bean.setLookupOnStartup(false); bean.afterPropertiesSet(); return (DataSource)bean.getObject(); }
正如上面代码所示,你可以自己向Embedded容器里写入JNDI数据源。
- 通过@Bean注解,可以向Spring的Bean容器里注入Bean实体。TomcatEmbeddedServletContainerFactory用于装载一些信息。
- 内嵌容器默认是不开启JNDI功能的,所以需要通过
tomcat.enableNaming();
来开启。 - 在postProcessContext方法中写入自己需要的JNDI信息。我们的项目用了MongoDB和Memcached两个JNDI数据源。
- jndiDataSource是为数据库提供数据源的。所以这里必须返回的是数据库的数据源。
写入了这些代码,就可以正常使用MongoDB和Memcached数据源了。
Profile
在企业应用中,Profile很常见,在开发,测试和生产环境需要应用不同的配置。在使用SpringBoot的时候这个配置很简单。
spring: profiles: active: dev---spring: profiles: dev---spring: profiles: production---spring: profiles: test
SpringBoot通过---
来区分区域,最上面是通用配置以及表明默认使用的是哪个Profile。
Zookeeper的启动
正如上一节看到的,原来写在applicationContext.xml里的配置,都可以用@Bean的方式配置进Bean容器中(当然类上也需要一个@Component或者其它的注解,让Spring框架可以识别它)。我们的ZK的启动是有一个单独的启动类,它监听了ApplicationEvent,然后通过Profile中配置的serverAddresses属性来链接ZK。最初我的想法是这很简单嘛。
@ConfigurationProperties(prefix = "zkConfig") @Bean public SpringConfiguratorLauncher springConfiguratorLauncher() { SpringConfiguratorLauncher launcher = new SpringConfiguratorLauncher(); return launcher; }
spring: profiles: active: dev---spring: profiles: dev datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://10.140.64.113:3306/membership_dadi?useUnicode=true&characterEncoding=UTF-8 username: developer password: Admin9@membership_dadizkConfig: serverAddresses: localhost:2181
SpringConfiguratorLauncher里拥有启动zk的代码。然后通过@configuratoinProperties注解在SpringConfiguratorLauncher返回的时候注入serverAddresses
属性。
- @ConfigurationProperties是SpringBoot提供的属性注入注解,它可以读取yml文件中配置的属性。但是想要使用它,需要一个依赖。
<!-- 支持 @ConfigurationProperties 注解 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency>
- prefix是一个域的区分,如spring一样。对于zk的配置,我们定义的前缀是zkConfig。
- 为了能正确注入属性,属性名必须和SpringConfiguratorLauncher中的成员变量名一样。
正当我满心认为这很easy的时候。发现zk启动失败了,告诉我serverAddresses为null。这是为啥?!网上的属性注入就是这么写的啊。查了很久我才发现,其实属性注入成功了,但是它注入的时机是在zk启动之后。我一脸懵逼。仔细看了看代码,发现zk的启动是通过监听AppliationEvent,而@ConfigurationProperties的注入功能是在这之后的。那么,怎么解决呢?
仔细查阅了资料,发现yml中属性值的读取其实是SpringBoot启动的第一步,也就是说,肯定早于Event的监听响应,程序里是可以读到具体的Profiles值的。那么我们怎么获取呢?
public class CustomConfigListener implements ApplicationListener<ApplicationEvent> { @Override public void onApplicationEvent(ApplicationEvent event) { if (event instanceof ApplicationEnvironmentPreparedEvent) { for(PropertySource<?> source : ((ApplicationEnvironmentPreparedEvent) event).getEnvironment().getPropertySources()){ if(source.getName().equals("applicationConfigurationProperties")){ if (source instanceof EnumerablePropertySource) { for(String name : ((EnumerablePropertySource) source).getPropertyNames()){ System.out.println(name+" :: "+ ((EnumerablePropertySource) source).getProperty(name)); if("zkConfig.serverAddresses".equals(name)){ Config.ZK_SERVER_ADDRESS = (((EnumerablePropertySource) source).getProperty(name).toString()); } } } } } } }}
我自定义了一个Listener,监听了Spring的启动事件,在响应代码中,将zkConfig.serverAddresses
缓存了下来。然后改动了SpringConfiguratorLauncher注入,将SpringConfiguratorLauncher中的属性从缓存中读取,并且在返回SpringConfiguratorLauncher实例之前就设置好。
AOP
我们的项目中拥有自己的换成方案,需要开启AOP功能。Spring工程开启AOP功能需要这样:
<aop:aspectj-autoproxy proxy-target-class="true"/>
只需要一行配置,就可以开启AOP配置。
在Spring Boot中,首先我们需要一个starter:
<!-- 添加对AOP的支持 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
然后在配置中
spring: profiles: active: dev---spring: profiles: dev aop: auto: true
在spring下配置aop的auto属性为true。
web.xml
本文最开始就说了,Spring Boot里没有web.xml文件,那么我们的Filter怎么配置呢?
在SpringBoot中这部分配置很简单,只要@Bean中注入的是一个Filter
或者FilterRegistrationBean
(SpringBoot对filter的封装,基本属性配置都有)。SpringBoot在启动的时候会自动识别这些Bean,然后加入到web的启动过程中。
@Bean public FilterRegistrationBean encodingFilter() { CharacterEncodingFilter encodingFilter = new CharacterEncodingFilter("UTF-8", true); FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(); filterRegistrationBean.setFilter(encodingFilter); List<String> list = new ArrayList<>(); list.add("/*"); filterRegistrationBean.setUrlPatterns(list); return filterRegistrationBean; }
那么没有web.xml,DispatchSeverlet也没法配置了。怎么办呢?其实你什么都不用做,SpringBoot在启动过程中已经帮你处理好了。
Spring MVC
跟Spring MVC相关的配置还有很多,如资源文件位置,视图解析器配置,拦截器,MessageConverter还有参数解析器。那么他们怎么配置呢?
Spring Boot提供了一个抽象类WebMvcConfigurerAdapter
从名字就可以看出,它是为WebMvc配置准备的,里面有很多抽象方法
//// Source code recreated from a .class file by IntelliJ IDEA// (powered by Fernflower decompiler)//package org.springframework.web.servlet.config.annotation;import java.util.List;import org.springframework.format.FormatterRegistry;import org.springframework.http.converter.HttpMessageConverter;import org.springframework.validation.MessageCodesResolver;import org.springframework.validation.Validator;import org.springframework.web.method.support.HandlerMethodArgumentResolver;import org.springframework.web.method.support.HandlerMethodReturnValueHandler;import org.springframework.web.servlet.HandlerExceptionResolver;import org.springframework.web.servlet.config.annotation.AsyncSupportConfigurer;import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;import org.springframework.web.servlet.config.annotation.CorsRegistry;import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;import org.springframework.web.servlet.config.annotation.InterceptorRegistry;import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;public abstract class WebMvcConfigurerAdapter implements WebMvcConfigurer { public WebMvcConfigurerAdapter() { } public void configurePathMatch(PathMatchConfigurer configurer) { } public void configureContentNegotiation(ContentNegotiationConfigurer configurer) { } public void configureAsyncSupport(AsyncSupportConfigurer configurer) { } public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { } public void addFormatters(FormatterRegistry registry) { } public void addInterceptors(InterceptorRegistry registry) { } public void addResourceHandlers(ResourceHandlerRegistry registry) { } public void addCorsMappings(CorsRegistry registry) { } public void addViewControllers(ViewControllerRegistry registry) { } public void configureViewResolvers(ViewResolverRegistry registry) { } public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) { } public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) { } public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { } public void extendMessageConverters(List<HttpMessageConverter<?>> converters) { } public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) { } public void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) { } public Validator getValidator() { return null; } public MessageCodesResolver getMessageCodesResolver() { return null; }}
基本上你想配置的里面都有,如我的工程配置
@EnableWebMvc@Componentpublic class MvcConfiguration extends WebMvcConfigurerAdapter { @Autowired private ActDynamicConfig actDynamicConfig; @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/js/**") .addResourceLocations("/WEB-INF/js/"); registry.addResourceHandler("/css/**") .addResourceLocations("/WEB-INF/css/"); registry.addResourceHandler("/img/**") .addResourceLocations("/WEB-INF/img/"); } @Override public void configureViewResolvers(ViewResolverRegistry registry) { InternalResourceViewResolver viewResolver = new InternalResourceViewResolver("/WEB-INF/jsp/", ".jsp"); registry.viewResolver(viewResolver); } @Override public void addInterceptors(InterceptorRegistry registry) { CSRFInterceptor csrfInterceptor = new CSRFInterceptor("", ""); InterceptorRegistration interceptorRegistration = registry.addInterceptor(csrfInterceptor); interceptorRegistration.addPathPatterns("/**"); interceptorRegistration.excludePathPatterns("/test/**"); } @Override public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { converters.add(new AdvanceFastJsonHttpMessageConverter()); } @Override public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) { argumentResolvers.add(new UidWebArgumentResolver()); }}
需要注意的是,需要使用注解@EnableWebMvc来开启这个配置的使用。
部署
终于来到了最后一步,我们的线上项目是通过war包部署的,而SpringBoot默认使用Application run就可以了。那么怎么将SpringBoot工程打包成war包呢?
首先还是老问题,我们没有web.xml,没有这个启动文件,web容器怎么启动呢?SpringBoot提供了一个抽象类SpringBootServletInitializer。它封装了对Serverlet 3.0的支持。我们需要继承这个类。
public class ActServletInitializer extends SpringBootServletInitializer { protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) { return builder.sources(Application.class); }}
其实看代码,这里最终还是使用了启动用的Application类。只是从run main方式,变成了web容器去运行它。
然后是构建工具的支持。
<build> <plugins> <!-- maven打包的时候告诉maven不需要web.xml,否则会报找不到web.xml错误 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-war-plugin</artifactId> <version>2.4</version> <configuration> <failOnMissingWebXml>false</failOnMissingWebXml> </configuration> </plugin> </plugins> <finalName>project name</finalName> </build>
同时需要注意,因为我们平时都是用内嵌的tomcat(或者其它web容器),我们在构建的时候不需要将他们打入war包
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-jasper</artifactId> <scope>provided</scope> </dependency>
最后运行命令clean package spring-boot:repackage -Dmaven.test.skip
即可以打出一个可部署的war包。
- Spring工程转成Spring Boot工程
- 打包Spring Boot工程
- 配置spring boot工程
- spring boot 工程配置文件
- 创建Spring boot工程
- Spring Boot 工程创建
- Spring Boot工程结构(推荐)
- Spring Boot工程结构推荐
- Spring Boot工程结构推荐
- Spring Boot工程结构推荐
- Spring Boot工程结构推荐
- Spring Boot工程结构(推荐)
- 快速创建spring boot工程
- spring boot工程实践入门
- 【Spring Boot教程】快速搭建spring boot项目工程
- Spring Boot(2)新建Spring Boot工程
- spring boot 第一篇 构建第一个spring boot工程
- spring boot 工程如何生成 gradle wrapper
- const关键字的用法
- 属性封装
- 自制Markdown简明语法教程
- struts1简介+工作原理
- Python网络爬虫(3)正则表达式
- Spring工程转成Spring Boot工程
- 线性表的顺序存储
- buffer_Cache
- Android windows+chrome 查看Realm数据库
- hamming distance二进制数异或有几个1
- iSkysoft.iTransfer.v4.1.1.MacOSX.Cracked-ARN
- python多线程解析
- 线性表的链式存储
- 1,Qt开发环境的搭建