都说springboot是新形势的主流框架工具,然而我的工作中并没有真正用到springboot;

  都说springboot里面并没有什么新技术,不过是组合了现有的组件而已,但是自己却说不出来;

  都说springboot让开发更简单,然而对于刚转换过来使用的时候总会发现各种不适应;

  网上查过许多的教程,下载过demo来玩,却无法用于实战,着实可惜。

  最近有个项目终于用springboot来开发了,一切从0开始,刚好可以练练手。来谈谈几点经验吧!(注:本文非教程,请当闲聊谈资)

1. 入门?

  springboot 的入门demo在spring官网可以直接下载,可以使用 maven 开发,https://start.spring.io/ 下载下来,运行main()方法就可以启动服务了。

  一个简单的helloworld就ok了,是不是超简单?再也不用复杂的搭建过程了。(不过说实话,这个过程相当于我之前有一套有一套代码模板,然后改改名字就成了新项目代码一样,没什么了不起)

  不过,有空的话还是有必要看一下完整点的入门demo教程: https://spring.io/guides/gs/rest-service/  (手动搭建服务很快这是真的)

2. 如何接入各常用组件及配置?

  这个需求是很强烈的,一个空白的框架是没有啥用的,因为我们必定要基于: 数据库、缓存、zk、mq、日志、mongo、页面模板等等。。。
  所以,如何配置?
  三个步骤:
    1. 引入组件依赖 dependency;
    2. 在 bootstrap-xx.properties 文件中加入配置属性;
    3. 在配置java文件中,new出相应实例或框架自己初始化实例以备用;
  就单是这点来说,其实springboot和spring的xml配置方式步骤是一样一样的,三步式导入。不过显然java代码写得更复杂和难找,xml更直观!(这里先忽略dependecy依赖的个数对比)

3. 如何做到加载动态配置?

  在使用xml配置的方式时,我们可以使用 spring 的 org.springframework.beans.factory.config.PropertyPlaceholderConfigurer

  组件,去加载一个配置中心的值,从而实现替换各种连接的作用,使其脱离代码的硬编码;

    <!-- spring的属性加载器,加载properties文件中的属性 -->
<bean class="com.xx.zk.property.PropertyPlaceholderConfigurer">
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
<property name="ignoreResourceNotFound" value="true" />
<property name="locations">
<list>
<value>classpath*:/spring/conf.properties</value>
</list>
</property>
</bean>

  那么,在springboot中是怎么做的呢? springboot 提供了多种配置文件共存的方式,比如: bootstrap-prod.properties, bootstrap-dev.properties, 用于区分测试环境和生产环境的配置而不互相影响;

  其大致原理为,环境准备好时,会触发监听器,然后加载相应配置文件:

    // - org.springframework.boot.context.event.EventPublishingRunListener
@Override
public void contextLoaded(ConfigurableApplicationContext context) {
for (ApplicationListener<?> listener : this.application.getListeners()) {
if (listener instanceof ApplicationContextAware) {
((ApplicationContextAware) listener).setApplicationContext(context);
}
context.addApplicationListener(listener);
}
// 加载 bootstrap.properties, bootstrap-dev.properties...
// 在 ConfigFileApplicationListener 的 ApplicationPreparedEvent 事件中触发
this.initialMulticaster.multicastEvent(
new ApplicationPreparedEvent(this.application, this.args, context));
}

  如果要使用配置中心,可以直接使用 spring-cloud-config 组件,配置即可,不过说实话这种配置中心着实难用,有能力的话都应自行定制开发一个统一配置中心(毕竟配置中心也是个技术活);spring cloud config 使用可以参考这篇博文: http://blog.51cto.com/zero01/2171735 ,或者查看官网教程!

4. 如何注册 beans ?

  1. 和spring一样,直接使用 @Service, @Controller, @Component... 注解直接注册简单的 bean;

  2. 对于一些复合bean组件,需要单独配置,如数据库连接:

    如 spring 中druid连接池的xml配置是这样的:

    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
destroy-method="close">
<property name="url" value="${jdbc.url}" />
<property name="driverClassName" value="${jdbc.driver}" />
<property name="maxActive" value="${pool.maxPoolSize}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<property name="removeAbandoned" value="true" />
<property name="removeAbandonedTimeout" value="${pool.removeAbandonedTimeout}" />
<property name="maxWait" value="${pool.maxWait}" />
<property name="timeBetweenEvictionRunsMillis" value="${pool.timeBetweenEvictionRunsMillis}" />
<property name="minEvictableIdleTimeMillis" value="${pool.minEvictableIdleTimeMillis}" />
<property name="validationQuery" value="${pool.validationQuery} " />
<property name="testWhileIdle" value="true" />
<property name="testOnBorrow" value="false" />
<property name="testOnReturn" value="false" />
</bean>

  而在 springboot 中,则是使用 java 代码直接创建:

    @Bean(name = "druidDataSource")
public DruidDataSource druidDataSource(){
DruidDataSource ds = new DruidDataSource();
ds.setUrl(config.getJdbcUrl());
ds.setDriverClassName(config.getDriverName());
ds.setMaxActive(config.getMaxPoolSize());
ds.setUsername(config.getJdbcUserName());
ds.setPassword(config.getJdbcPwd());
ds.setRemoveAbandoned(true);
ds.setMaxWait(config.getJdbcMaxWait());
ds.setTimeBetweenEvictionRunsMillis(config.getTimeBetweenEvictionRunsMillis());
ds.setMinEvictableIdleTimeMillis(config.getMinEvictableIdleTimeMillis());
ds.setValidationQuery(config.getValidationQuery());
ds.setTestWhileIdle(true);
ds.setTestOnBorrow(false);
ds.setTestOnReturn(false);
return ds;
}

  3. 还有一种特殊的加载方式,值得注意,就是使用了 @Bean 注解,但是其直接new了一对象返回:

    @Bean(name = "directHelloService")
public HelloService directHelloService(){
HelloService service = new HelloService();
return service;
}

  这个有什么问题呢?因为我们的 service 一般都会依赖于其他的服务,所以,往往都会有依赖注入的过程,但是你使用了一个new创建,则没有了依赖注入问题了。因此,当你想直接使用这个服务的时候,很可能就会拿到一些空对象;

  那怎么办?三个办法:

    1. 没事就不要直接new有依赖的对象了;
    2. 如果实在要new,需要在new的对象上添加注解 @DependsOn 注解标明需要依赖的组件,这样,在使用的时候就会再次去检测依赖,从而完成依赖注入了;
    3. 自己手动完成依赖注入;
    4. 将加载动作委托给springContext, 比如使用 getBean("xxx") 的方式获取,使其回归spring的自动依赖注入过程;(没有试验过)

  单从这一点来讲,想完全摆脱 xml 束缚的 springboot, 还是显得有些力不从心! 另外,使用 java 配置文件的另一个不好的地方是,配置文件散落在各处,很不直观!

5. 日志如何记录?

  日志是必备工具。所以 springboot 默认集成了 logback 的日志组件,所以,我们要做的只是,配置好打印属性就好了;如在 bootstrap.properties 文件中添加如下:

logging.config=classpath:logback.xml

  意思就是说,你将配置写入到 resources/logback.xml 中,其中的配置规则同理自不必细说;

6. 如何自定义 RequestMappingHandlerMapping ?

  做一个web应用时,对webmvc的定制化配置是一定的。因为我们的包路径查找,可能会有自己一些特定的规则,所以需要自定义 RequestMappingHandlerMapping。这在 spring 中,则只需要注册一个requestMappingBean就可以了(要先排除系统自动扫描 @Controller 注解),如:

    <bean name='requestMappingHandlerMapping'
class='com.xxx.cust.URLRequestMappingHandlerMapping'>
<property name="interceptors">
<list>
<!-- 添加会话拦截器 -->
<bean class="com.xxx.interceptor.SessionInterceptor">
</bean>
</list>
</property>
</bean>

  而在springboot中,好像就不是那么回事了,它变成是这样的,先继承一个 WebMvcConfigurationSupport 的基础组件,然后自定义各种配置如:

@Configuration
public class SpringMVCConfig extends WebMvcConfigurationSupport {
@Resource
private SessionInterceptor sessionInterceptor; @Override
public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(sessionInterceptor)
.excludePathPatterns(
"/error");
} /**
* 添加自定义的Converters和Formatters.
*/
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new StringToDateConverter("yyyy-MM-dd HH:mm:ss"));
} /**
* Protected method for plugging in a custom subclass of
* {@link RequestMappingHandlerMapping}.
* @since 4.0
*/
protected RequestMappingHandlerMapping createRequestMappingHandlerMapping() {
return new RequestMappingHandlerMapping();
} /**
* Protected method for plugging in a custom subclass of
* {@link RequestMappingHandlerAdapter}.
* @since 4.3
*/
protected RequestMappingHandlerAdapter createRequestMappingHandlerAdapter() {
return new RequestMappingHandlerAdapter();
} }

  如上配置的依据是,在其父类 WebMvcConfigurationSupport 中,会创建一个 requestMappingHandlerMapping 的 bean, 而创建的过程,将方法暴露给了一个可供继承覆写的 createRequestMappingHandlerMapping 的方法,从而达到自定义 RequestMappingHandlerMapping 的目的。同理于 RequestMappingHandlerAdapter 。

  而对于其他的各种自定义组件的接入,则按照文档说明来即可。对于一些通用的组件,一般都会有 xxx-starter 提供,从而可以避免n多的依赖配置,这也是springboot的一重要开发优势吧。毕竟,spring里面,你需要知道的太多了!

  综上,咱们就可以规规矩矩地写业务代码了。总体的步骤就是:写配置变量到properties文件,使用 @Configuration 读取配置;实例化 bean 以供使用;

  至于 xml 和 properties 的习惯问题,咱们就先不说了。

7. 最后,还有一个关键问题,打包部署?

  springboot 往往是直接启动一个 main() 方法来运行的,和 基于web容器的应用是不一样的。(内嵌容器)

  在spring中,我们一般是通过maven打一个war包,然后部署到tomcat中。而在 springboot 中,则不一定要这么干了(甚至是不建议这么干),所以需要打一个 jar 包。

  打jar包部署有两个问题:

    1. jar包中的其他第三方依赖怎么办?

    2. 部署维护交给谁?

  一、针对第三方的jar包依赖问题,我们可以有两个解决方法:1. 将第三方的jar包打包进项目的jar包中; 2. 将依赖的jar包放抽离出来放到一个独立的lib库文件夹中,启动应用时再指定加载位置;各有优劣,一个是会导致jar包体积变大,一个是会导致开发维护困难(这是个大问题)。当然我们应该会选将其打包到一个jar中,一点体积是不会难倒我们的。3. 其实我觉得还有一种打包方式,就是将所有可能用到的class文件,全部解压出来打包到最终的jar包中,这样既做到体积小,又做到代码维护容易,但是可能会有些难度,因为你很难确定哪些class文件是不用的,所以一般也不敢排除(白干了);

    打jar包的依赖,可以参照如下插件配置:

    <build>
<resources>
<resource>
<directory>src/main/java</directory>
</resource>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>com.xxx.service.StartApplication</mainClass>
<addClasspath>true</addClasspath>
<classpathPrefix>lib/</classpathPrefix>
</manifest>
</archive>
<classesDirectory>
</classesDirectory>
</configuration>
</plugin>
</plugins>
</build>

  注意:错误的配置可能导致依赖包嵌入有问题,或者切换环境不成功!

  二、针对部署维护的问题,则依赖于你想运行的环境,如果你想使用原来的 tomcat 这种web容器运行服务,则无需另外担心维护问题,因为tomcat已经有了这些设备。而如果你使用jar包运行,则需要自行编写维护脚本了,其实功能也不外乎几个:

    1. 启动;

    2. 停止;

    3. 查看状态;

    4. springboot 需要的功能,就是支持动态修改配置属性,从而使测试环境与生产环境隔离;

#!/bin/sh

## project info
SERVICE_DIR=/www/xxx
SERVICE_NAME=myproject-1.0.-SNAPSHOT
SPRING_PROFILES_ACTIVE=prod ## java env
JAVA_HOME=/usr/java/jdk1..0_101
pidfile="/opt/springboot/xxx.pid"
JAVA_OPTS="$JAVA_OPTS -server -Xms512m -Xmx2048m -Dfile.encoding=UTF-8 -Xloggc:/opt/springboot/logs/xxx_gc.log -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/opt/springboot/logs/" case "$1" in
start)
pid=`ps -ef | grep -w "${SERVICE_NAME}" | grep -w "java"| grep -v "grep" | awk '{print $2}'`
if [ "${pid}" = "" ]; then
if [ "$2" != "" ]; then
SPRING_PROFILES_ACTIVE=$
fi
echo " - Starting ${SERVICE_NAME} ... "
echo " - Using JAVA_HOME: $JAVA_HOME ..."
echo " - Using Environment: spring.profiles.active=${SPRING_PROFILES_ACTIVE}"
exec nohup ${JAVA_HOME}/bin/java ${JAVA_OPTS} -jar ${SERVICE_DIR}/${SERVICE_NAME}\.jar --spring.profiles.active=${SPRING_PROFILES_ACTIVE} >/dev/null >& &
echo "$!" > ${pidfile};
echo " - Congraduations!!! Started project [${SERVICE_DIR}/${SERVICE_NAME}.jar] success, pid=$! ."
else
echo "- Oops!!! ${SERVICE_NAME} is alreaddy started @pid=${pid}, kill it ?"
fi
;; stop)
pid=`ps -ef | grep -w "${SERVICE_NAME}" | grep -w "java" | grep -v "grep" | awk '{print $2}'`
rm -rf ${pidfile};
if [ "${pid}" = "" ]; then
echo " - ${SERVICE_NAME} is Already stopped."
else
echo " - Stopping ${SERVICE_NAME} by kill -15 ${pid} ...";
kill - ${pid}
sleep
pid2=`ps -ef | grep -w "${SERVICE_NAME}" | grep -w "java" | grep -v "grep" | awk '{print $2}'`
if [ "${pid2}" = "" ]; then
echo " - ${SERVICE_NAME} stopped success !!! "
else
kill - ${pid2}
echo " - Stop Failed! ${SERVICE_NAME} stop error, force kill ${pid2} !!!"
fi
fi
;; restart)
$ stop
sleep
$ start $
;;
status)
pid=`ps -ef | grep -w "${SERVICE_NAME}" | grep -w "java" | grep -v "grep" | awk '{print $2}'`;
if [ "${pid}" = "" ]; then
echo " - Oops!!! ${SERVICE_NAME} is Already stopped."
else
# echo -e " - ${SERVICE_NAME} is ruuning, \033[36m pid=${pid} \033[0m .";
echo -e " - ${SERVICE_NAME} is ruuning, pid=${pid} .";
echo -e " - ${SERVICE_NAME} 's server port is: `netstat -tunlp | grep "${pid}/" | awk '{print $1 " " $4;}'`.";
echo -e " - ${SERVICE_NAME} up info:`ps -eo pid,lstart,etime,cmd | grep ${SERVICE_NAME} | grep -v "grep" | awk '{print "startTime:"$2" "$3" "$4" "$5" "$6", uptime:" $7}'`.";
echo -e " - Current MEMORY Usage: `free -h | grep "Mem:" | awk '{print "total: "$2", used: "$3".";}'`.";
echo -e " - Current CPU Usage: `top -bn 1 -i -c | sed -n '3p'` "; fi
;;
*)
echo " - Wrong command!!! Usage: $0 [start|stop|restart|status] [dev|test|prod]"
;;
esac

  运行方式如下:

springboot_xxx [start|stop|restart|status] [dev|test|prod]

  以上,就是一些关于 springboot 使用的一些实践历程,对比 spring 和 springboot 的差异,总体来说,思路并没有变化,基本上只是习惯上的变化。

springboot 实战之一站式开发体验的更多相关文章

  1. Jetpack Compse 实战 —— 全新的开发体验

    公众号回复 Compose 获取安装包 项目地址: Wanandroid-Compose 经过前段时间的 Android Dev Summit ,相信你已经大概了解了 Jetpack Compose ...

  2. 千锋很火的SpringBoot实战开发教程视频

    springboot是什么? Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程.该框架使用了特定的方式来进行配置,从而使开发人员 ...

  3. springboot实战开发全套教程,让开发像搭积木一样简单!Github星标已上10W+!

    前言 先说一下,这份教程在github上面星标已上10W,下面我会一一给大家举例出来全部内容,原链接后面我会发出来!首先我讲一下接下来我们会讲到的知识和技术,对比讲解了多种同类技术的使用手日区别,大家 ...

  4. 小D课堂-SpringBoot 2.x微信支付在线教育网站项目实战_1-1.SpringBoot整合微信支付开发在线教育视频站点介绍

    笔记 第一章项目介绍和前期准备 1.SpringBoot整合微信支付开发在线教育视频站点介绍     简介: 课程介绍,和小D课堂在线教育项目搭建开发 1.课程大纲介绍         2.微信支付项 ...

  5. SpringBoot实战 之 异常处理篇

    在互联网时代,我们所开发的应用大多是直面用户的,程序中的任何一点小疏忽都可能导致用户的流失,而程序出现异常往往又是不可避免的,那该如何减少程序异常对用户体验的影响呢?其实方法很简单,对异常进行捕获,然 ...

  6. SpringBoot实战之异常处理篇

    在互联网时代,我们所开发的应用大多是直面用户的,程序中的任何一点小疏忽都可能导致用户的流失,而程序出现异常往往又是不可避免的,那该如何减少程序异常对用户体验的影响呢?其实方法很简单,对异常进行捕获,然 ...

  7. Docker深入浅出系列 | 单机Nginx+Springboot实战

    目录 Nginx+Springboot实战 前期准备 实战目标 实战步骤 创建Docker网络 搭建Mysql容器 搭建额度服务集群 搭建Nginx服务 验证额度服务 附录 Nginx+Springb ...

  8. [置顶] 炎炎夏日,给你一次极爽的开发体验!——统一开发环境功能升级优化,正式上线V2.0!

    作为中国移动应用运行托管平台(MM应用引擎)的开发部署工具,统一开发环境(UDE)在原HTML5跨平台开发功能基础上优化升级,新增跨平台编译(Android/iOS)和云端托管服务,正式上线2.0版本 ...

  9. 【Android开发VR实战】三.开发一个寻宝类VR游戏TreasureHunt

    转载请注明出处:http://blog.csdn.net/linglongxin24/article/details/53939303 本文出自[DylanAndroid的博客] [Android开发 ...

随机推荐

  1. Vuejs自定义select2指令

    在做select2插件的时候遇到一些坑,最终解决如下: Vue.directive('select2', { inserted: function (el, binding, vnode) { var ...

  2. python设计模式---结构型之代理模式

    主要想着nginx:) from abc import ABCMeta, abstractmethod # 结构型设计模式---代理模式 class Actor: def __init__(self) ...

  3. 16 道嵌入式C语言面试题

    1. 用预处理指令#define 声明一个常数,用以表明 1 年中有多少秒(忽略闰年问题) #define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL 我在这想看到 ...

  4. Redis持久化persistence

    一.前言 由于Redis的数据都存放在内存中,如果没有配置持久化,redis重启后数据就全丢失了,于是需要开启redis的持久化功能,将数据保存到磁盘上,当redis重启后,可以从磁盘中恢复数据. R ...

  5. oracle中nvarchar2查询结果显示总是少一位

    问题: 有个表的字段是nvarchar2(32),但是在plsql中查询显示结果发现一直少一位. 修改方法: 在plsql里的首选项-连接里有个选项: 在oci8上强制使用oci7, 把这个勾上就ok ...

  6. 2018-2019 20165235 网络对抗技术 Exp0:kali的安装

    2018-2019 20165235 网络对抗技术 Exp0:kali的安装 安装kali 在官网上https://www.kali.org/下载kali 下载之后进行解压 打开VMware-> ...

  7. js初学练手:Csdn Ads Cleaner

    最新版本在这里哒:https://greasyfork.org/zh-CN/scripts/376621-csdn-ads-cleaner 隔壁csdn的广告太猖獗啦!写个js管管它 需配合Tempe ...

  8. String.matches()的用法

    https://blog.csdn.net/victoryckl/article/details/6930409

  9. MyBatis(四)多参数处理问题

    这里总结了关于多参数传递时,MyBatis接收的三种方式. (1)接口中编写方法 public Emp getEmpByParams(Integer id,String lastNmae); publ ...

  10. Scanner,Random,匿名对象-------------------java基础学习第七天

    1.API 2.Scanner 功能:通过键盘输入数据到程序中. 引用类型的一般使用步骤: 导包 Import 包路径.类名称 只有java.lang 包写的类不需要导包,其他都需要 2.创建 类名称 ...