浅谈Java项目打包方式

大家都知道在Java里面开发一个web服务非常繁琐,首先需要各种框架,各种配置,完事之后,需要打成一个war包,最后需要一个servlet容器,Tomcat或者Jetty,Jboss,来运行发布,同样的事情,你会发现在其他的语言中,是非常简单的,比如python里面的Django或者tornado,ruby里面的rails等,随着近年来微服务越来越流行,一个简单,强大,灵活,易配置,易开发的web服务迫在眉睫,而它就是Spring Boot,统一了Java web开发的各个需要的框架,提供了大而全的功能支持。 这里就不在具体介绍了,有需要的朋友,可以看散仙写过的几篇关于它的文章http://qindongliang.iteye.com/blog/2205633。 Spring Boot为什么容易开发微服务?当然它也不仅仅限于微服务,它还可以与Dubbo集成,实现企业级服务化,因为Spring Boot内嵌了servlet容器,发布服务时,仅仅需要一个jar包,所有有关的依赖全部在这个jar包中,所以不需要你额外下载一个tomcat或者jetty来运行服务,这个jar包,拷贝到任何含有JDK的环境的机器上,都可以任意运行,除此之外,因为仅仅只有一个jar包,所以与Jekins和Docker的集成都非常方便,这种方式以后会逐渐流行,而以war包+外置容器的发布方式散仙预测以后会逐渐没落,这里就不在具体介绍这种打包方式了。 接着上面谈,由于所有的东西,都在这个jar包中,当你依赖越来越多时,这个jar包的体积,就会上升的厉害,在我们的一个应用场景中,集成了dubbo,redis,solr,hadoop,hbase,最后一个jar包体积,快接近100M了,不过,体积大到是次要的,只要服务能正常运行,可以忽略这个小缺点,之后启动,停止脚本也非常简洁: 启动脚本:

Shell代码

  1. #!/bin/bash
  2. JAVA_HOME=/tool/server/jdk
  3. CLASSPATH=.:$JAVA_HOME
  4. PATH=$JAVA_HOME/bin:$PATH
  5. export JAVA_HOME CLASSPATH PATH
  6. nohup java -jar work.jar &>task.log & echo $! >pid&

停止脚本:

Shell代码

  1. kill -9 `cat pid`

上面散仙谈到的spring boot能够封装所有的依赖进入一个jar包中,前提是需要提前配置好各个运行参数,然后编译打包,运行这个jar,假如在已经发布到服务器上,我想改一些参数配置,应该如何操作呢? 这时候就会发现面对一个jar,你无法干任何事,除非回到maven工程中,改里面的配置参数,然后重新打包,接着用Jenkins发布到docker里面,假如这时候,你上传到服务器的网速非常慢,几十kb/秒,偏偏你又有一个100M的jar,还不得发布到猴年马月,这里说的不夸张,是实际开发中可能遇到的一种情况。 那么如何比较好的处理这种问题呢?其实也不难,在打包的时候,分离工程,形成一个基本的目录如下:

Java代码

  1. bin/ //存放处理脚本
  2. lib/ //存放jar包
  3. conf/ //存放配置文件
  4. logs/ //存放log

看到上面的这个目录,大伙是不是有种回到了Ant的年代的感觉呢,其实不然在Maven中,我们可以使用maven-assembly-plugin插件,来完成更优雅的打包 方式,仅需要项目中,改动下面两个部分即可完成: (1)在pom文件中,添加如下插件:

Xml代码

  1. <build>
  2. <plugins>
  3. <plugin>
  4. <groupId>org.apache.maven.plugins</groupId>
  5. <artifactId>maven-assembly-plugin</artifactId>
  6. <configuration>
  7. <!--<appendAssemblyId>true</appendAssemblyId>-->
  8. <descriptors> <!--描述文件路径-->
  9. <descriptor>src/main/assemble/package.xml</descriptor>
  10. </descriptors>
  11. </configuration>
  12. <executions>
  13. <execution>
  14. <id>make-zip</id>
  15. <!-- 绑定到package生命周期阶段上 -->
  16. <phase>package</phase>
  17. <goals>
  18. <!-- 绑定到package生命周期阶段上 -->
  19. <goal>single</goal>
  20. </goals>
  21. </execution>
  22. </executions>
  23. </plugin>
  24. </plugins>
  25. </build>

(2) 在main下面新建一个assemble文件夹,并在该文件夹下新建一个package.xml文件,内容如下:

Xml代码

  1. <?xml version="1.0"?>
  2. <assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
  5. <!--项目标识,设置的话,生成后的zip文件会加上此后缀-->
  6. <id></id>
  7. <!--打包格式-->
  8. <formats>
  9. <format>zip</format>
  10. </formats>
  11. <!--是否包含根目录文件夹-->
  12. <includeBaseDirectory>true</includeBaseDirectory>
  13. <fileSets>
  14. <!--<fileSet>-->
  15. <!--<directory>${project.basedir}\src\main\resources</directory>-->
  16. <!--<outputDirectory>\</outputDirectory>-->
  17. <!--<includes>-->
  18. <!--<include>some/path</include>-->
  19. <!--</includes>-->
  20. <!--<excludes>-->
  21. <!--<exclude>some/path1</exclude>-->
  22. <!--</excludes>-->
  23. <!--</fileSet>-->
  24. <!--自定义文件描述集-->
  25. <fileSet>
  26. <!--自定义脚本目录打包-->
  27. <directory>${project.basedir}\src\main\bin</directory>
  28. <outputDirectory>\bin</outputDirectory>
  29. <includes>
  30. <include>*.*</include>
  31. </includes>
  32. <!--设置权限-->
  33. <fileMode>0755</fileMode>
  34. </fileSet>
  35. <!--<fileSet>-->
  36. <!--<!&ndash;外部配置文件打包&ndash;>-->
  37. <!--<directory>${project.basedir}\src\main\config</directory>-->
  38. <!--<outputDirectory>\config</outputDirectory>-->
  39. <!--<includes>-->
  40. <!--<include>*.*</include>-->
  41. <!--</includes>-->
  42. <!--<fileMode>0644</fileMode>-->
  43. <!--</fileSet>-->
  44. </fileSets>
  45. <dependencySets>
  46. <dependencySet>
  47. <useProjectArtifact>true</useProjectArtifact>
  48. <outputDirectory>lib</outputDirectory>
  49. <!-- 将scope为runtime的依赖包打包到lib目录下。 -->
  50. <scope>runtime</scope>
  51. </dependencySet>
  52. </dependencySets>
  53. </assembly>

当然这种启动,停止脚本,稍微复杂点,需要把所有的jar包和配置文件cp到一个JVM里面,然后执行: 启动脚本:

Shell代码

  1. cs=`echo /ROOT/tmp/z_check_hbase/lib/*jar | sed 's/ /:/g'`
  2. #配置文件的目录
  3. conf="$cdir/conf:"
  4. #追加进入cp中
  5. cs=$cs$conf
  6. #打印
  7. echo $cs
  8. #执行
  9. nohup java -cp $cs com.tools.HbaseDaoImpl &>/dev/null & echo $! >pid&

停止脚本:

Shell代码

  1. kill -9 `cat pid`

总结: 对比spring boot中的单一jar的打包方式,这种方式,则将jar包和配置分离,我们可以随时改配置参数,然后重新启动,这样做灵活性大大提高了,而且在远程传入服务器时,除了第一次需要传所有的依赖文件,以后,改动代码后,只需要传主jar即可,因为依赖的jar基本都不会变,而当使用单一的jar时,任何改动都需要上传整个jar。这在jar包体积比较大时,网速比较慢时,是非常耗时的,但由于分离了配置文件和jar,复杂性会稍微提高,当然这算不了什么,因为我们可以用zip或者tar.gz将它压缩成一个整体,然后可以部署到任意机器上。

在Java里面,除了Spring Boot外,另外一个比较给力的Web服务框架就是Scala的Play2了,目前最新版本是2.5,采用sbt管理依赖,引入Netty实现高性能http服务,不再维持会话状态,如果你需要会话状态,就自己采用redis或者memcache等来实现,它打包后会自动生成压缩包,压缩包里面包含了分离的lib和conf以及win和linux部署脚本,这一点与我们上面说的第二种方式,大致上是一致的,对上线发布非常友好,当然这里并没有绝对的好坏之分: 单一jar的打包方式总体来说对微服务比较友好 分离jar的打包方式总体来说对企业级大型服务比较友好 当然你也可以混搭,只要是适合自己场景的方式,就是最好,最优雅的方式。

原文发布于微信公众号 - 我是攻城师(woshigcs)

原文发表时间:2016-06-20

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏向治洪

即时通讯软件openfire+spark+smack

所以我基本上分为三篇文章来介绍此类软件的开发: 第一篇是关于XMPP 协议是啥,IM 是啥以及一个比较有名的开源实现,该开源实现包括三个部分(Spark、Sma...

3905
来自专栏zhisheng

日志工具现状调研

  针对这类问题,对当前java比较流行的一些日志工具进行了调研,以期能够在未来的开发使用中做到全组代码风格统一,日志写得好对于我们开发调试,线上问题追踪等都有...

1472
来自专栏杨建荣的学习笔记

有趣的linux命令总结(78天)

linux命令可以简化我们工作中的许多任务。关于Linux这个主题已经考虑很久了,也还是在不断的完善中,在自己的实验和各种资料的整理中,认为还是一些不错的命令。...

2825
来自专栏坚毅的PHP

ImageMagick and JMagick install on Mac OSX

接的遗留代码,在本地运行,有jmagick-6.4.0.jar 但是出现错误: javax.servlet.ServletException: java.lan...

4176
来自专栏编程坑太多

『中级篇』play with docker 的使用(44)

Marcos 和 Jonathan 还带来了另一个炫酷的功能就是可以在 PWD 实例中通过拖放文件的方式将 Dockerfile 直接上传到 PWD 窗口。

561
来自专栏大数据架构师专家

namp 渗透测试-安装篇

nmap是一个网络连接端扫描软件,用来扫描网上电脑开放的网络连接端。确定哪些服务运行在哪些连接端,并且推断计算机运行哪个操作系统。

1043
来自专栏用户2442861的专栏

使用IntelliJ IDEA开发SpringMVC网站(一)开发环境

访问GitHub下载最新源码:https://github.com/gaussic/SpringMVCDemo

5621
来自专栏Java学习之路

01 Spring框架 基本介绍

相信学习java,并且走Web道路的道友都应该知道Spring的大名,它的地位相信也不需要我在这里多说什么,接下来的文章就Spring的配置和使用来进行一些讲解...

3387
来自专栏difcareer的技术笔记

breakpad: Native crash 日志收集工具前言正题breakpad工作原理项目集成

现在大部分应用都会有Java层的崩溃日志收集机制,一般就是程序crash后,展示一个上报界面,用户点击就上传了。 但是Native程序crash了,很少有做处...

1062
来自专栏阿杜的世界

Java Web技术经验总结(二)

1513

扫码关注云+社区