Bootstrap

maven打造可执行war包(超详细)

在开发java Web时,有时我们会使用嵌入式jetty来运行,项目完成后,如果能够直接运行war包从而启动jetty来运行war包那就非常完美了,本文将讲解如何在项目中整合jetty 9,并构造可执行的war包(打包前和打包后都能随时启动)。

1.首先添加jetty 9的依赖(本文暂时只用到了jetty的以下依赖,读者根据自己的项目需要增加)

<dependency>
    <groupId>org.eclipse.jetty</groupId>
	<artifactId>jetty-server</artifactId>
	<version>9.2.7.v20150116</version>
</dependency>
<dependency>
    <groupId>org.eclipse.jetty</groupId>
    <artifactId>jetty-webapp</artifactId>
    <version>9.2.7.v20150116</version>
</dependency>

2.项目中使用jetty 9。

首先我封装了自己的JettyServer

public class EmbeddedServer {
    //public static final Logger logger = LoggerFactory.getLogger(EmbeddedServer.class);
   // private static final int DEFAULT_BUFFER_SIZE = 16192;

    protected final Server server = new Server();

    
    public EmbeddedServer(int port,String path) throws IOException{
    	this(port,path,false,null);
    }
    /**
     * use war to start
     * @param port
     * @param isWar
     * @param warPath
     * @throws IOException
     */
    public EmbeddedServer(int port,boolean isWar,String warPath) throws IOException{
    	this(port,null,isWar,warPath);
    }
    private EmbeddedServer(int port, String path,boolean isWar,String warPath) throws IOException {
        Connector connector = getConnector(port);
        server.addConnector(connector);
        WebAppContext application = getWebAppContext(path,isWar,warPath);
        server.setHandler(application);
        server.setStopAtShutdown(true);
    }

    protected WebAppContext getWebAppContext(String path,boolean isWar,String warPath) {
    	WebAppContext application;
    	if(isWar){
    		application=new WebAppContext();
    		application.setWar(warPath);
    		return application;
        }else{
            application = new WebAppContext(path, "/");
            application.setConfigurationDiscovered(true);
            application.setParentLoaderPriority(true);
            application.setClassLoader(Thread.currentThread().getContextClassLoader());
            return application;
        }
    }

    protected Connector getConnector(int port) throws IOException {
        HttpConfiguration http_config = new HttpConfiguration();
        // this is to enable large header sizes when Kerberos is enabled with AD
        //final int bufferSize = getBufferSize();
        //http_config.setResponseHeaderSize(bufferSize);
        //http_config.setRequestHeaderSize(bufferSize);

        ServerConnector connector = new ServerConnector(server, new HttpConnectionFactory(http_config));
        connector.setPort(port);
        connector.setHost("0.0.0.0");
        server.addConnector(connector);
        return connector;
    }

    /*protected Integer getBufferSize() {
        try {
            Configuration configuration = ApplicationProperties.get();
            return configuration.getInt("sysimple.jetty.request.buffer.size", DEFAULT_BUFFER_SIZE);
        } catch (Exception e) {
            // do nothing
        }

        return DEFAULT_BUFFER_SIZE;
    }*/

    public void start() throws Exception {
        server.start();  
        //logger.info("********************************************************");
        //logger.info("The SySimple Has Started !!!");
        server.join();
    }

    public void stop() {
        try {
            server.stop();
        } catch (Exception e) {
            //logger.warn("Error during shutdown", e);
        }
    }

}

接着可以使用封装好的EmbeddedServer来启动war

public class StartWeb{
	private static EmbeddedServer embeddedServer;
	public static void main(String[] args){
	//Start web server
	int port=3000try{
        	if(args.length==0){
        	    //该方式能够在开发时快速启动
            	embeddedServer=new EmbeddedServer(port, "src/main/webapp");
            }else{
                //传入war包的路径,该方法能够在打包完成后启动该war包
            	embeddedServer=new EmbeddedServer(port, true, args[0]);
            }
        	embeddedServer.start();
        }catch(Exception e){
        	System.exit(0);
        }
    }
}

注意:打包后如果需要启动war包,需要使用如下的这种批处理命令来启动:

以批处理命令(start.bat)和server.war在同级目录下为例:(以下是start.bat的内容)

@echo off
set bat_dir=%~dp0
java -jar  %bat_dir%/web.war %bat_dir%/web.war

读者可以考虑在代码中得到war包的路径,这样可以在启动时省去传参。

  1. 下面是最重要的:使用Maven构建可执行war包

总的来说可执行war包是将war包的结构仿照jar包的结构进行改变,第一个是需要在manifest中标记出主方法,第二个是编译后的代码(包,而非.class)必须放在war包的最外层,最后要能够找到项目的依赖。

①标记主方法

通过maven-war-plugin在manifest中标记主方法入口

<plugin>  
            <groupId>org.apache.maven.plugins</groupId>  
            <artifactId>maven-war-plugin</artifactId>  
            <configuration>
                <archive>
                    <manifest>
                        <mainClass>org.bit.linc.web.commons.StartWeb</mainClass>
                    </manifest>
                </archive>
            </configuration>
         </plugin>

②拷贝(也可以移动)web的所有的代码到war包最外层(使用maven-antrun-plugin)

<plugin>
                <groupId>org.apache.maven.plugins</groupId>  
                <artifactId>maven-antrun-plugin</artifactId>  
                <executions>
                    <execution>  
                        <id>main-class-placement</id>  
                        <phase>prepare-package</phase>  
                        <configuration>  
                            <target>
                                <copy todir="${project.build.directory}/${project.artifactId}/">  
                                    <fileset dir="${project.build.directory}/classes/">  
                                        <include name="**/*.*" />
                                    </fileset>  
                                </copy>
                            </target>  
                        </configuration>  
                        <goals>
                            <goal>run</goal>  
                        </goals>
                    </execution>  
                </executions>  
            </plugin>

③ 标记所有依赖的位置(将代码拷贝到war最外层后,会出现依赖的类都找不到的情况,因此需要让war包能够查找到这些依赖)

将maven-war-plugin更改为如下内容:

<plugin>  
            <groupId>org.apache.maven.plugins</groupId>  
            <artifactId>maven-war-plugin</artifactId>  
            <configuration>
                <archive>
                    <manifest>
                        <mainClass>org.bit.linc.web.commons.StartWeb</mainClass>
                        <addClasspath>true</addClasspath>
                        <classpathPrefix>WEB-INF/lib</classpathPrefix>
                    </manifest>
                </archive>
            </configuration>
         </plugin>

现在可以构建可执行war包了。

以笔者的项目为例: 构建的war包中META-INF/MANIFEST.MF会变成如下内容:

Manifest-Version: 1.0
Built-By: wubo
Build-Jdk: 1.7.0_17
Class-Path: WEB-INF/lib/commons-0.0.2.jar WEB-INF/lib/commons-configur
 ation-1.8.jar WEB-INF/lib/commons-lang-2.6.jar WEB-INF/lib/commons-lo
 gging-1.1.1.jar WEB-INF/lib/slf4j-api-1.7.7.jar WEB-INF/lib/slf4j-log
 4j12-1.7.7.jar WEB-INF/lib/log4j-1.2.17.jar WEB-INF/lib/plugins-0.0.2
 .jar WEB-INF/lib/clusters-0.0.2.jar WEB-INF/lib/monitors-0.0.2.jar WE
 B-INF/lib/jetty-server-9.2.7.v20150116.jar WEB-INF/lib/javax.servlet-
 api-3.1.0.jar WEB-INF/lib/jetty-http-9.2.7.v20150116.jar WEB-INF/lib/
 jetty-util-9.2.7.v20150116.jar WEB-INF/lib/jetty-io-9.2.7.v20150116.j
 ar WEB-INF/lib/jetty-webapp-9.2.7.v20150116.jar WEB-INF/lib/jetty-xml
 -9.2.7.v20150116.jar WEB-INF/lib/jetty-servlet-9.2.7.v20150116.jar WE
 B-INF/lib/jetty-security-9.2.7.v20150116.jar WEB-INF/lib/gson-2.3.1.j
 ar
Created-By: Apache Maven 3.3.9
Main-Class: org.bit.linc.web.commons.StartWeb
Archiver-Version: Plexus Archiver

其中的Class-Path和Main-Class均已经改变。

;