都说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. Windows 2008 R2防火墙,允许被ping的设置方法

    这篇文章主要介绍了Windows 2008 R2防火墙,允许被ping的设置方法,需要的朋友可以参考下   1.准备 1)原因 出于安全因素考虑,在Windows 2008 R2上是不允许从外部对其P ...

  2. .net core ef 通过dbfirst方式连接sql server数据库

    1. 创建基于.net core 的项目(过程略) 2. 使用nuget添加引用 Microsoft.EntityFrameworkCore Microsoft.EntityFrameworkCore ...

  3. requests补充

    HTTP/1.1 协议规定的 HTTP 请求方法有 OPTIONS.GET.HEAD.POST.PUT.DELETE.TRACE.CONNECT 这几种.其中,POST 一般用来向服务端提交数据,本文 ...

  4. [python][matlab]使用python调用matlab程序

    问题引入 在做实验的时候,需要用到python和matlab工具来进行不同的处理,比如在run神经网络的时候,需要使用pytorch框架得到网络的各个参数,在得到参数后需要使用matlab进行聚类规划 ...

  5. SQL Server 数据库中的异常信息与编号

    SQL Server 数据库中的系统表提供了强大的元数据信息,其中 dbo.sysmessages 表中存储了数据库执行命令过程中的所有消息. SELECT * FROM master.dbo.sys ...

  6. net core体系-Standard-1概述

    前言 早上起来.NET社区沸腾了,期待已久的.NET Core 2.0终于发布!根据个人经验,微软的产品一般在2.0时会趋于成熟,所以一个新的.Net开发时代已经来临!未来属于.NET Core. . ...

  7. python @property的用法及含义全面解析

    在接触python时最开始接触的代码,取长方形的长和宽,定义一个长方形类,然后设置长方形的长宽属性,通过实例化的方式调用长和宽,像如下代码一样. class Rectangle(object): de ...

  8. centos7部署openstack-ocata

    1.前言 本文旨在记录本人的一个实验过程,因为其中有一些坑,方便以后回顾查询. 其中限于篇幅(大部分是配置部分)有些内容省略掉了,官网都有,各位如果是安装部署的话可以参考官网,不建议使用本文. 以下是 ...

  9. RSP小组——团队冲刺博客三

    RSP小组--团队冲刺博客三 冲刺日期:2018年12月12日 各成员今日(12.12)完成的任务 马瑞蕃页面布局 李闻洲音乐代码的实现 赵乾宸,找bug,处理bug,使游戏滑动,消除实现 蒋子行会议 ...

  10. 二叉查找树的C++实现

    #include <iostream> #include <algorithm> #include <stack> using namespace std; /// ...