是时候闭环Java应用了

TemekaMader 3年前
   <p>你曾经因为部署/上线而痛苦吗?你曾经因为要去运维那改配置而烦恼吗? 在我接触过的一些部署/上线方式中,曾碰到过以下一些问题:</p>    <p>1、程序代码和依赖都是人工上传到服务器,不是通过工具进行部署和发布;</p>    <p>2、目录结构没有规范,jar启动时通过-classpath任意指定;</p>    <p>3、fat jar,把程序代码、配置文件和依赖jar都打包到一个jar中,改配置文件太费劲;</p>    <p>4、不管是非web应用还是web应用都部署到web容器环境,如Tomcat;</p>    <p>5、web应用还需要先在服务器上安装好环境(如装Tomcat)才能部署,想升级版本或者换个容器太难了;</p>    <p>6、线上参数修改还需要找运维,痛苦。</p>    <p>还有如没有自动部署平台,回滚到上一个版本那可真是天方夜谈;增量包而非全量包,无法自由在在的回滚;前端代码直接覆盖而非版本化,难快速回滚,出问题要清理CDN,痛苦;ngx_lua项目时不按照项目的方式部署,在服务器上随意修改代码,导致某些服务器忘记修改或者版本不一致,排查问题太痛苦。</p>    <p>还有很多部署中不好的方式,但是本文只关注闭环Java应用带来的好处。首先介绍下应该如何部署应用,然后介绍下什么是闭环Java应用,它的好处和如何搭建。</p>    <h2><strong>应该如何部署应用</strong></h2>    <p><img src="https://simg.open-open.com/show/691fff5740e2d6a57ceb7b3108661cc8.jpg"></p>    <p>项目</p>    <p>项目中应该包括了所有要执行的代码、启停脚本,比如非web应用</p>    <p><img src="https://simg.open-open.com/show/6e8f0823ff5c660a52b4c11cf247bf98.jpg"></p>    <p>web应用</p>    <p><img src="https://simg.open-open.com/show/a63908181581bd4781dd8ea97ad97a51.png"></p>    <p>打包应用后,会按照相应的目录结构构建。如果项目使用maven,可以使用maven-assembly-plugin进行按照相应的目录结构构件。</p>    <p>即项目、打包的应用要按照统一的风格来实施。</p>    <p>自动部署系统</p>    <p>自动部署系统负责打包应用(比如执行mvn相应的命令即可)、抽包(从指定目录抽取要部署的代码,如target/nonweb-example-package目录)、部署代码(发布代码,将代码同步到宿主机器)、启停应用(配置指定的启停脚本并调用)。</p>    <p>自动部署除了这些功能外,应该还有如发布历史管理(回滚)、分组管理(如不同机房不同的配置文件)、配置管理(如要修改启动/停止脚本、修改配置文件[不同机房不同的配置]、参数管理[如jvm参数等])等。</p>    <p>宿主机器</p>    <p>即代码部署到的机器,它应该只安装最小化环境,如只需要装JDK即可,像Tomcat是不需要安装的,由应用决定使用哪个容器。</p>    <p>通过增加自动部署系统可以更好的进行项目的统一发布、管理和回滚。</p>    <h2>闭环Java应用</h2>    <p>闭环Java应用指Java代码、容器、配置文件、启停脚本等都在同一处维护,修改配置文件、修改环境参数、更改容器类型等都不需要到宿主机器上进行更改。 宿主机器只提供基本运行环境,如仅部署JDK环境即可,不需要部署如Tomcat容器,需要什么容器,都是在Java应用中指定。</p>    <p>这样的好处是配置文件修改、JVM参数修改、容器的选择都可以在Java应用中配置,形成闭环。</p>    <p>闭环Java应用的目的主要是让Java应用能自启动,这样程序的控制权就在我们手里,而不是运维手里。而我们更懂我们的程序。</p>    <p>随着微服务概念的流行,spring boot也受到大家的热捧。spring boot能帮助我们快速构建基于spring的应用;其能方便创建自启动应用、可以嵌入各种容器(如Tomcat、Jetty)、提供了一些starter pom用于简化配置文件、自动化配置(只需要引入相关的pom,就自动获得了某些功能)等。</p>    <p>在介绍spring boot之前,我们看下在以前是怎么构建闭环Java应用。</p>    <p><strong>从零构建非web应用</strong></p>    <p>项目结构</p>    <p><img src="https://simg.open-open.com/show/916dcf8adebbbf51480fdf3e5e280a58.jpg"> 本示例演示了构建一个非web应用 RPC服务生产者(如Dubbo服务),还可以构建如Worker类型的应用,他们本身不需要web容器,作为普通的java应用启动即可。</p>    <p>maven依赖(pom.xml)</p>    <p>需要自己添加如spring-core、spring-context等相关依赖,此处就不展示了。</p>    <p>打包配置(pom.xml)</p>    <p>nonweb-example\pom.xml</p>    <pre>  <code class="language-java"><<strong>plugin> <<strong>groupId>org.apache.maven.plugins</<strong>groupId> <<strong>artifactId>maven-assembly-plugin</<strong>artifactId> <<strong>version>2.6</<strong>version> <<strong>configuration> <<strong>descriptor>src/assembly/assembly.xml</<strong>descriptor> <<strong>finalName>${project.build.finalName}</<strong>finalName> </<strong>configuration> <<strong>executions> <<strong>execution> <<strong>phase>package</<strong>phase> <<strong>goals> <<strong>goal>directory</<strong>goal> </<strong>goals> </<strong>execution> </<strong>executions> </<strong>plugin></strong></strong></strong></strong></strong></strong></strong></strong></strong></strong></strong></strong></strong></strong></strong></strong></strong></strong></strong></strong></strong></strong></strong></strong></code></pre>    <p>使用maven-assembly-plugin进行打包;打包配置如下:</p>    <pre>  <code class="language-java"><<strong>id</strong>>package</<strong>id</strong>>  <<strong>formats</strong>>      <<strong>format</strong>>dir</<strong>format</strong>>  </<strong>formats</strong>>  <<strong>includeBaseDirectory</strong>>false</<strong>includeBaseDirectory</strong>>  <<strong>fileSets</strong>>      <em><!-- </em><em>可执行文件 --> </em><<strong>fileSet</strong>>          <<strong>directory</strong>>src/bin</<strong>directory</strong>>          <<strong>outputDirectory</strong>>bin</<strong>outputDirectory</strong>>          <<strong>includes</strong>>              <<strong>include</strong>>*.bat</<strong>include</strong>>          </<strong>includes</strong>>          <<strong>lineEnding</strong>>dos</<strong>lineEnding</strong>>      </<strong>fileSet</strong>>      <<strong>fileSet</strong>>          <<strong>directory</strong>>src/bin</<strong>directory</strong>>          <<strong>outputDirectory</strong>>bin</<strong>outputDirectory</strong>>          <<strong>includes</strong>>              <<strong>include</strong>>*.sh</<strong>include</strong>>          </<strong>includes</strong>>          <<strong>lineEnding</strong>>unix</<strong>lineEnding</strong>>          <<strong>fileMode</strong>>0755</<strong>fileMode</strong>>      </<strong>fileSet</strong>>      <em><!-- classes --> </em><<strong>fileSet</strong>>          <<strong>directory</strong>>${project.build.directory}/classes</<strong>directory</strong>>          <<strong>outputDirectory</strong>>classes</<strong>outputDirectory</strong>>      </<strong>fileSet</strong>>  </<strong>fileSets</strong>>  <em><!-- </em><em>依赖jar包 --> </em><<strong>dependencySets</strong>>      <<strong>dependencySet</strong>>          <<strong>outputDirectory</strong>>lib</<strong>outputDirectory</strong>>          <<strong>excludes</strong>>              <<strong>exclude</strong>>com.jd:nonweb-example</<strong>exclude</strong>>          </<strong>excludes</strong>>      </<strong>dependencySet</strong>>  </<strong>dependencySets</strong>></code></pre>    <p>主要有三组配置:</p>    <p>formats:打包格式,此处使用的是dir,还可以是zip、rar等;</p>    <p>fileSet:拷贝文件,本示例主要有bin文件、classes文件需要拷贝;</p>    <p>dependencySets:依赖jar,拷贝到lib目录;</p>    <p>执行mvn package后形成了将得到如下结构:</p>    <p>将该目录通过自动部署抽包并部署到宿主机器即可。然后自动部署系统执行bin下的启停脚本执行即可。</p>    <p><img src="https://simg.open-open.com/show/a5b14841a28751c7cab3732f4e627e01.png"></p>    <p> </p>    <p>启动类</p>    <pre>  <code class="language-java"><strong>public class </strong>Bootstrap {    <strong>public static void </strong>main(String[] args) <strong>throws </strong>Exception {        ClassPathXmlApplicationContext ctx = <strong>new </strong>ClassPathXmlApplicationContext(<strong>"classpath:spring-config.xml"</strong>);        ctx.registerShutdownHook();        Thread.<em>currentThread</em>().join();    }  }</code></pre>    <p>本示例没有使用Java Config方式构建,直接加载spring配置文件启动Java应用。</p>    <p>启动脚本</p>    <pre>  <code class="language-java">#!/bin/sh  echo -------------------------------------------  echo start server  echo -------------------------------------------  # 设置项目代码路径  export CODE_HOME="/export/App/nonweb-example-startup-package"  #日志路径  export LOG_PATH="/export/Logs/nonweb.example.jd.local"  mkdir -p $LOG_PATH  # 设置依赖路径  export CLASSPATH="$CODE_HOME/classes:$CODE_HOME/lib/*"  # java可执行文件位置  export _EXECJAVA="$JAVA_HOME/bin/java"  # JVM启动参数  export JAVA_OPTS="-server -Xms128m -Xmx256m -Xss256k -XX:MaxDirectMemorySize=128m"  # 启动类  export MAIN_CLASS=com.jd.nonweb.example.startup.Bootstrap    $_EXECJAVA $JAVA_OPTS -classpath $CLASSPATH $MAIN_CLASS &  tail -f $LOG_PATH/stdout.log</code></pre>    <p>配置项目代码路径、日志路径、依赖路径、java执行文件路径、JVM启动参数、启动类。</p>    <p>停止脚本</p>    <pre>  <code class="language-java">#日志路径  export LOG_PATH="/export/Logs/nonweb.example.jd.local"  mkdir -p $LOG_PATH  # 启动类  export MAIN_CLASS=com.jd.nonweb.example.startup.Bootstrap    echo -------------------------------------------  echo stop server    #所有相关进程  PIDs=`jps -l | grep $MAIN_CLASS | awk '{print $1}'`  #停止进程  if [ -n "$PIDs" ]; then    for PID in $PIDs; do        kill $PID        echo "kill $PID"    done  fi    #等待50秒  for i in 1 10; do    PIDs=`jps -l | grep $MAIN_CLASS | awk '{print $1}'`    if [ ! -n "$PIDs" ]; then      echo "stop server success"      echo -------------------------------------------      break    fi    echo "sleep 5s"    sleep 5  done    #如果等待50秒还没有停止完,直接杀掉  PIDs=`jps -l | grep $MAIN_CLASS | awk '{print $1}'`  if [ -n "$PIDs" ]; then    for PID in $PIDs; do        kill -9 $PID        echo "kill -9 $PID"    done  fi  tail -fn200 $LOG_PATH/stdout.log</code></pre>    <p>到此一个闭环非web应用就构建完了,启停脚本、启动类、项目代码都是统一在一处维护,并使用maven-assembly-plugin将这些打包在一起,通过自动部署发布并执行,达到了闭环的目的。</p>    <p>从零构建web应用</p>    <p>项目结构</p>    <p><img src="https://simg.open-open.com/show/1f5029df546684c638bbba41db40cc9b.png"></p>    <p>maven依赖(pom.xml)</p>    <p>需要自己添加如spring-core、spring-context、spring-web、spring-webmvc、velocity等相关依赖,此处就不展示了。</p>    <p>打包配置(pom.xml)</p>    <p>web-example\pom.xml</p>    <pre>  <code class="language-java"><<strong>plugin</strong>>      <<strong>groupId</strong>>org.apache.maven.plugins</<strong>groupId</strong>>      <<strong>artifactId</strong>>maven-assembly-plugin</<strong>artifactId</strong>>      <<strong>version</strong>>2.6</<strong>version</strong>>      <<strong>configuration</strong>>          <<strong>descriptor</strong>>src/assembly/assembly.xml</<strong>descriptor</strong>>          <<strong>finalName</strong>>${project.build.finalName}</<strong>finalName</strong>>      </<strong>configuration</strong>>      <<strong>executions</strong>>          <<strong>execution</strong>>              <<strong>phase</strong>>package</<strong>phase</strong>>              <<strong>goals</strong>>                  <<strong>goal</strong>>directory</<strong>goal</strong>>              </<strong>goals</strong>>          </<strong>execution</strong>>      </<strong>executions</strong>>  </<strong>plugin</strong>>  </code></pre>    <p>使用maven-assembly-plugin进行打包;打包配置如下:</p>    <p>< <strong> id </strong> > package </ <strong> id </strong> > < <strong> formats </strong> >     < <strong> format </strong> > dir </ <strong> format </strong> > </ <strong> formats </strong> > < <strong> includeBaseDirectory </strong> > false </ <strong> includeBaseDirectory </strong> > < <strong> fileSets </strong> >     < <strong> fileSet </strong> >         < <strong> directory </strong> > src/bin </ <strong> directory </strong> >         < <strong> outputDirectory </strong> > bin </ <strong> outputDirectory </strong> >         < <strong> includes </strong> >             < <strong> include </strong> > *.sh </ <strong> include </strong> >         </ <strong> includes </strong> >         < <strong> lineEnding </strong> > unix </ <strong> lineEnding </strong> >         < <strong> fileMode </strong> > 0755 </ <strong> fileMode </strong> >     </ <strong> fileSet </strong> >     <em> </em></p>    <p><em><!-- WEB-INF --></em></p>    <p> </p>    <p> </p>    <p>< <strong> fileSet </strong> >         < <strong> directory </strong> > src/main/webapp </ <strong> directory </strong> >         < <strong> outputDirectory </strong> ></ <strong> outputDirectory </strong> >     </ <strong> fileSet </strong> >     <em> </em></p>    <p><em><!-- classes --></em></p>    <p> </p>    <p> </p>    <p>< <strong> fileSet </strong> >         < <strong> directory </strong> > ${project.build.directory}/classes </ <strong> directory </strong> >         < <strong> outputDirectory </strong> > WEB-INF/classes </ <strong> outputDirectory </strong> >     </ <strong> fileSet </strong> > </ <strong> fileSets </strong> ><br> <em><!-- </em> <em> </em></p>    <p><em>依赖jar包 --></em></p>    <p>< <strong> dependencySets </strong> >     < <strong> dependencySet </strong> >         < <strong> outputDirectory </strong> > WEB-INF/lib </ <strong> outputDirectory </strong> >         < <strong> excludes </strong> >             < <strong> exclude </strong> > com.jd:web-example </ <strong> exclude </strong> >         </ <strong> excludes </strong> >     </ <strong> dependencySet </strong> > </ <strong> dependencySets </strong> ></p>    <p>主要有三组配置:</p>    <p>formats:打包格式,此处使用的是dir,还可以是zip、rar等;</p>    <p>fileSet:拷贝文件,本示例主要有bin文件、classes文件、webapp文件需要拷贝;</p>    <p>dependencySets:依赖jar,拷贝到WEB-INF\lib目录;</p>    <p>执行mvn package后形成了将得到如下结构:</p>    <p><img src="https://simg.open-open.com/show/9cef143776eefbe9fb797ebfbaac2802.png"></p>    <p>打包的目录结构和普通web结构完全一样;将该目录通过自动部署抽包并发布到宿主机器即可。然后自动部署系统执行bin下的启停脚本执行即可。</p>    <p>启动类</p>    <p>public class</p>    <p>TomcatBootstrap {</p>    <p> </p>    <p><strong>private static final </strong> Logger <strong> <em> LOG </em> </strong> = LoggerFactory. <em>getLogger</em> (TomcatBootstrap. <strong> class </strong></p>    <p>);</p>    <p> </p>    <p><strong>public static void </strong> main(String[] args) <strong> throws </strong></p>    <p>Exception{</p>    <p> </p>    <p><em>// </em> <em> </em></p>    <p><em>提升性能(https://wiki.apache.org/tomcat/HowTo/FasterStartUp)</em></p>    <p> </p>    <p> </p>    <p>System. <em>setProperty</em> ( "tomcat.util.scan.StandardJarScanFilter.jarsToSkip" , <strong> "*.jar" </strong></p>    <p>);</p>    <p> </p>    <p> </p>    <p><em>//System.setProperty("securerandom.source","file:/dev/./urandom");</em></p>    <p> </p>    <p> </p>    <p><strong>int </strong> port =Integer. <em>parseInt</em> (System. <em>getProperty</em> ( <strong> "server.port" </strong> , <strong> "8080" </strong></p>    <p>));</p>    <p>String contextPath = System. <em>getProperty</em> (</p>    <p><strong>"server.contextPath" </strong> , <strong> "" </strong></p>    <p>);</p>    <p>String docBase = System. <em>getProperty</em> (</p>    <p><strong>"server.docBase" </strong></p>    <p>, <em>getDefaultDocBase</em> ());</p>    <p> </p>    <p><strong><em>LOG </em> </strong> .info( "server port : {}, context path : {},doc base : {}"</p>    <p>,port, contextPath, docBase);</p>    <p>Tomcat tomcat = <em>createTomcat</em> (port,contextPath, docBase);</p>    <p>tomcat.start();</p>    <p>Runtime. <em>getRuntime</em> ().addShutdownHook(</p>    <p><strong>new </strong></p>    <p>Thread() {</p>    <p> </p>    <p>@Override</p>    <p> </p>    <p><strong>public void </strong></p>    <p>run(){</p>    <p> </p>    <p><strong>try </strong></p>    <p>{</p>    <p> </p>    <p>tomcat</p>    <p>.stop();</p>    <p>}</p>    <p><strong>catch </strong></p>    <p>(LifecycleException e) {</p>    <p> </p>    <p><strong><em>LOG </em> </strong> .error( <strong> "stoptomcat error." </strong></p>    <p>, e);</p>    <p>}</p>    <p>}</p>    <p>});</p>    <p>tomcat.getServer().await();</p>    <p>}</p>    <p><strong> private static </strong></p>    <p>String getDefaultDocBase() {</p>    <p>File classpathDir =</p>    <p><strong>new </strong> File(Thread. <em>currentThread</em> ().getContextClassLoader().getResource( <strong> "." </strong></p>    <p>).getFile());</p>    <p>File projectDir =classpathDir.getParentFile().getParentFile();</p>    <p> </p>    <p><strong>return new </strong> File(projectDir, <strong> "src/main/webapp" </strong></p>    <p>).getPath();</p>    <p>}</p>    <p><strong>private static </strong> Tomcat createTomcat( <strong> int </strong> port,String contextPath, String docBase) <strong> throws </strong></p>    <p>Exception{</p>    <p>String tmpdir = System. <em>getProperty</em> (</p>    <p><strong>"java.io.tmpdir" </strong></p>    <p>);</p>    <p>Tomcat tomcat =</p>    <p><strong>new </strong></p>    <p>Tomcat();</p>    <p>tomcat.setBaseDir(tmpdir);</p>    <p>tomcat.getHost().setAppBase(tmpdir);</p>    <p>tomcat.getHost().setAutoDeploy(</p>    <p><strong>false </strong></p>    <p>);</p>    <p>tomcat.getHost().setDeployOnStartup(</p>    <p><strong>false </strong></p>    <p>);</p>    <p>tomcat.getEngine().setBackgroundProcessorDelay(-</p>    <p>1</p>    <p>);</p>    <p>tomcat.setConnector( <em>newNioConnector</em> ());</p>    <p>tomcat.getConnector().setPort(port);</p>    <p>tomcat.getService().addConnector(tomcat.getConnector());</p>    <p>Context context =tomcat.addWebapp(contextPath, docBase);</p>    <p>StandardServer server =(StandardServer) tomcat.getServer();</p>    <p> </p>    <p> </p>    <p><em>//APR library loader. Documentation at /docs/apr.html</em></p>    <p> </p>    <p> </p>    <p>server.addLifecycleListener( <strong> new </strong></p>    <p>AprLifecycleListener());</p>    <p> </p>    <p> </p>    <p><em>//Prevent memory leaks due to use of particularjava/javax APIs</em></p>    <p> </p>    <p> </p>    <p>server.addLifecycleListener( <strong> new </strong></p>    <p>JreMemoryLeakPreventionListener());</p>    <p> </p>    <p><strong>return </strong></p>    <p>tomcat;</p>    <p>}</p>    <p> </p>    <p><em>// </em> <em> </em></p>    <p><em>在这里调整参数优化</em></p>    <p> </p>    <p> </p>    <p><strong>private static </strong></p>    <p>Connector newNioConnector() {</p>    <p>Connector connector =</p>    <p><strong>new </strong> Connector( <strong> "org.apache.coyote.http11.Http11NioProtocol" </strong></p>    <p>);</p>    <p>Http11NioProtocol protocol =(Http11NioProtocol) connector.getProtocolHandler();</p>    <p> </p>    <p><strong>return </strong></p>    <p>connector;</p>    <p>}</p>    <p>}</p>    <p>通过嵌入Tomcat容器启动,这种方式的确定是需要先写Tomcat的启动代码,优点也很明显:以后Tomcat的控制权在我们手中,可以随时进行切换或者优化,不需要改线上的配置文件。</p>    <p>启动脚本</p>    <p>#!/bin/sh</p>    <p>echo -------------------------------------------</p>    <p>echo start server</p>    <p>echo -------------------------------------------</p>    <p>#</p>    <p>设置项目代码路径</p>    <p>export CODE_HOME="/export/App/web-example-web-package"</p>    <p>#日志路径</p>    <p>export LOG_PATH="/export/Logs/web.example.jd.local"</p>    <p>mkdir -p $LOG_PATH</p>    <p># 设置依赖路径</p>    <p>export CLASSPATH="$CODE_HOME/WEB-INF/classes:$CODE_HOME/WEB-INF/lib/*"</p>    <p># java可执行文件位置</p>    <p>export _EXECJAVA="$JAVA_HOME/bin/java"</p>    <p># JVM启动参数</p>    <p>export JAVA_OPTS="-server -Xms128m -Xmx256m -Xss256k-XX:MaxDirectMemorySize=128m"</p>    <p># 服务端端口、上下文、项目根配置</p>    <p>export SERVER_INFO="-Dserver.port=8090 -Dserver.contextPath=-Dserver.docBase=$CODE_HOME"</p>    <p># 启动类</p>    <p>export MAIN_CLASS=com.jd.web.example.startup.TomcatBootstrap</p>    <p>$_EXECJAVA $JAVA_OPTS -classpath $CLASSPATH $SERVER_INFO $MAIN_CLASS &</p>    <p>tail -f $LOG_PATH/stdout.log</p>    <p>配置项目代码路径、日志路径、依赖路径、java执行文件路径、JVM启动参数、启动类;相当于非web应用,多了web服务器端口、上下文、项目根路径配置。</p>    <p>停止脚本</p>    <p>和非web的类似就不再重复了。</p>    <p>到此一个闭环web应用就构建完了,启停脚本、启动类、项目代码都是统一在一处维护,并使用maven-assembly-plugin将这些打包在一起,通过自动部署发布并执行。达到了闭环的目的。</p>    <p>Spring Boot构建非web/web应用</p>    <p>项目结构<br> <img src="https://simg.open-open.com/show/f3a98d584ee3fa9a553b9eb6501fbdc0.png"></p>    <p>maven依赖(pom.xml)</p>    <p>spring-boot-example/pom.xml继承spring-boot-starter-parent</p>    <pre>  <code class="language-java"><<strong>parent</strong>>      <<strong>groupId</strong>>org.springframework.boot</<strong>groupId</strong>>      <<strong>artifactId</strong>>spring-boot-starter-parent</<strong>artifactId</strong>>      <<strong>version</strong>>1.4.1.BUILD-SNAPSHOT</<strong>version</strong>>  </<strong>parent</strong>></code></pre>    <p>spring-boot-starter-parent中是一些通用配置,如JDK编码、依赖管理(它又继承了spring-boot-dependencies,这里边定义了所有依赖);</p>    <p>依赖</p>    <pre>  <code class="language-java"><<strong>dependency> <<strong>groupId>org.springframework.boot</<strong>groupId> <<strong>artifactId>spring-boot-starter</<strong>artifactId> </<strong>dependency> <<strong>dependency> <<strong>groupId>org.springframework.boot</<strong>groupId> <<strong>artifactId>spring-boot-starter-web</<strong>artifactId> </<strong>dependency> <<strong>dependency> <<strong>groupId>org.springframework.boot</<strong>groupId> <<strong>artifactId>spring-boot-starter-velocity</<strong>artifactId> </<strong>dependency> <<strong>dependency> <<strong>groupId>org.springframework.boot</<strong>groupId> <<strong>artifactId>spring-boot-starter-log4j2</<strong>artifactId> </<strong>dependency></strong></strong></strong></strong></strong></strong></strong></strong></strong></strong></strong></strong></strong></strong></strong></strong></strong></strong></strong></strong></strong></strong></strong></strong></code></pre>    <p>spring-boot-starter是最小化的spring boot环境(spring-core、spring-context等); spring-boot-starter-web是spring mvc环境,并使用Tomcat作为web容器;spring-boot-starter-velocity将自动将模板引擎配置为velocity。此处可以看到starter的好处了,需要什么功能只需要引入一个starter,相关的依赖自动添加,而且会自动配置使用该特性。</p>    <p>打包配置(pom.xml)</p>    <p>spring-boot-example-web\pom.xml添加如下maven插件:</p>    <pre>  <code class="language-java"><<strong>plugin</strong>>      <<strong>groupId</strong>>org.springframework.boot</<strong>groupId</strong>>      <<strong>artifactId</strong>>spring-boot-maven-plugin</<strong>artifactId</strong>>  </<strong>plugin</strong>>    执行mvn package时将得到如下fat jar:  </code></pre>    <p><img src="https://simg.open-open.com/show/c176e441c6f230fab779b3aad7ee7177.png"></p>    <p>启动类</p>    <pre>  <code class="language-java"><strong>package </strong>com.jd.springboot.example.web.startup;  <strong>import </strong>org.springframework.boot.SpringApplication;  <strong>import </strong>org.springframework.boot.autoconfigure.SpringBootApplication;  <strong>import </strong>org.springframework.context.annotation.ImportResource;  @SpringBootApplication(scanBasePackages = <strong>"com.jd.springboot.example"</strong>)  @ImportResource(<strong>"classpath:spring-config.xml"</strong>)  <strong>public class </strong>Bootstrap {    <strong>public static void </strong>main(String[] args) {        SpringApplication.<em>run</em>(Bootstrap.<strong>class</strong>, args);    }  }</code></pre>    <p>@SpringBootApplication指定了要扫描的包、可以使用@ImportResource引入xml配置文件。然后可以直接作为普通java应用启动即可,此时自动使用tomcat作为web容器启动。</p>    <p>运行 jar -jar spring-boot-example-1.0-SNAPSHOT.jar即可启动(META-INF\MANIFEST.MF指定了Main-Class)。</p>    <p>个人不太喜欢fat jar的方式。可以使用maven-assembly-plugin配合来打包Java应用。项目结构如下所示:</p>    <p><img src="https://simg.open-open.com/show/8acf667284d52fada287e7f1d1563805.png"></p>    <p>项目结构和之前的区别是多了assembly和bin。</p>    <p>打包配置(pom.xml)</p>    <p>spring-boot-example-web\pom.xml将如下maven插件</p>    <pre>  <code class="language-java"><<strong>plugin</strong>>      <<strong>groupId</strong>>org.springframework.boot</<strong>groupId</strong>>      <<strong>artifactId</strong>>spring-boot-maven-plugin</<strong>artifactId</strong>>  </<strong>plugin</strong>>  </code></pre>    <p>更改为assembly插件</p>    <pre>  <code class="language-java"><<strong>plugin> <<strong>groupId>org.apache.maven.plugins</<strong>groupId> <<strong>artifactId>maven-assembly-plugin</<strong>artifactId> <<strong>version>2.6</<strong>version> <<strong>configuration> <<strong>descriptor>src/assembly/assembly.xml</<strong>descriptor> <<strong>finalName>${project.build.finalName}</<strong>finalName> </<strong>configuration> <<strong>executions> <<strong>execution> <<strong>phase>package</<strong>phase> <<strong>goals> <<strong>goal>directory</<strong>goal> </<strong>goals> </<strong>execution> </<strong>executions> </<strong>plugin></strong></strong></strong></strong></strong></strong></strong></strong></strong></strong></strong></strong></strong></strong></strong></strong></strong></strong></strong></strong></strong></strong></strong></strong></code></pre>    <p>assembly.xml和“从零构建非web应用”的类似,就不贴配置了。</p>    <p>执行mvn package时将得到如下打包:</p>    <p><img src="https://simg.open-open.com/show/7a28c448ffaf7bfd779795d87cb4dd87.png"></p>    <p>启停脚本也是类似的,在此也不贴配置了。到此基于spring boot的非fat jar方式的自启动Java应用就构建好了。</p>    <p>总结</p>    <p>从零构建非web应用/web应用需要我们查找相关依赖并配置,还需要进行一些配置(Spring配置、容器配置),如果构建一个新的项目还是相对较慢的,但是在公司内大家应该都有自己的“starter pom”,因此实际构建也不会很慢。而如果没有一些项目的积累,使用spring boot可以非常容易而且快速的就能搭建出想要的项目。使用spring boot后:容易添加依赖、启动类不用自己创建、享受到自动配置的好处等;而自带的spring-boot-maven-plugin会生成fat jar,不过可以配合maven-assembly-plugin来实现之前的方式的。</p>    <p>另外因笔者所在公司使用Docker容器,一个宿主机器只部署一个JVM示例,示例中的启停脚本不用考虑单机多JVM实例问题。</p>    <p>创建闭环Java应用,可以更容易的进行如JVM参数调优、修改容器配置文件、非web应用不需要部署到Tomcat容器中;这是笔者想进行闭环Java应用的主要目的。</p>    <p> </p>    <p>来自:http://jinnianshilongnian.iteye.com/blog/2317830</p>    <p> </p>    <p><span style="background:rgb(189, 8, 28) url("data:image/svg+xml; border-radius:2px; border:medium none; color:rgb(255, 255, 255); cursor:pointer; display:none; font:bold 11px/20px "Helvetica Neue",Helvetica,sans-serif; opacity:1; padding:0px 4px 0px 0px; position:absolute; text-align:center; text-indent:20px; width:auto; z-index:8675309">Save</span></p>