Java: How do I build standalone distributions of M

2019-02-16 19:56发布

问题:

I often encounter distributions of Java applications or libraries which use Maven as their build tool.

Some of them, sadly, don't provide standalone (or redistributable) jars.

Is it possible to build Maven-based applications in such a way, that the build result contains all dependencies and can be redistributed to work out-of-the box?

I tried to build Jackrabbit's OCM module. For some very "intelligent" reasons there is no downloadable standalone version.
So I built Jackrabbit with Maven (the source package of Jackrabbit includes OCM), and got the same jar as found in the apache repository. The jar doesn't contain necessary dependencies and is useless to me.

回答1:

Use the Maven Shade plugin

...but be careful of the gotchas (similar to the one described further down my answer), which has got a workaround explained here.

Also, be ultra careful with the Shade plugin config. I accidentally used double <configuration> tags once, and the transformers didn't apply at all, and the plugin also took the liberty of not warning me.

Don't use the Maven Assembly plugin

assembly:single will unpack your dependency JARs as-is, and this could not be what you want. E.g. stuff like META-INF/spring.schemas will be overridden with the last Spring dependency JAR that's evaluated, and as such your XSDs won't be found (apart from those in the last JAR, of course). Which is why systems like Alfresco made their AMP plugin which bundles dependencies inside lib/ inside the AMP you're building. The latter raises dependency management issues, though.



回答2:

You may have some luck with the appassembler plugin. Failing that, take a look at the assembly plugin. That's more flexible, but lower level. If you're using the assembly plugin, you may find the chapter on it in maven: the definitive guide to be useful.



回答3:

As Dominic said, using the assembly plugin will do the trick. You would usually configure it inside your own project's POM to gather and package all required dependencies:

  ...
  <plugin>
    <artifactId>maven-assembly-plugin</artifactId>
    <configuration>
      <descriptorRefs>
        <descriptorRef>jar-with-dependencies</descriptorRef>
      </descriptorRefs>
    </configuration>
  </plugin>
  ...

jar-with-dependencies is predefined by the assembly plugin and will include all dependencies in the final package (see the documentation here).

If you don't want to use Maven for your own project, you will need to modify the libraries' POMs and repackage them yourself (download the sources, add the above snippet to pom.xml and run mvn package). Beware of duplicate or incompatible transitive dependencies if you use multiple libraries. exclusions might help in that case (see documentation here).



回答4:

As a couple of the posters said, the assembly plugin is a good way of creating a complete jar file, with all project dependencies. However, you don't actually have to modify the pom.xml file. Simply run:

mvn assembly:single -DdescriptorId=jar-with-dependencies

... in order to create a jar file. If you want to do anything more advanced, you should probably modify pom.xml, and create a custom assembly descriptor.



回答5:

Change the pom.xml file and use the <Embed-Dependency> directive. A similar example can be found here so you can adapt it to your scenario.

<Embed-Dependency>*;scope=!test;inline=true</Embed-Dependency>

I think this should do the trick.


Here is the example at the above URL that seems to give timeout.

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>br.gov.lexml</groupId>
    <artifactId>toolkit</artifactId>
    <packaging>bundle</packaging>
    <version>3.0</version>
    <parent>
        <artifactId>lexml</artifactId>
        <groupId>br.gov.lexml</groupId>
        <version>1.0</version>
    </parent>
    <build>
        <finalName>Lexml_Toolkit-2.0</finalName>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.5</source>
                    <target>1.5</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.felix</groupId>
                <artifactId>maven-bundle-plugin</artifactId>
                <extensions>true</extensions>
                <configuration>
                    <instructions>
                        <!--_include>src/toolkit/resources/META-INF/MANIFEST.MF</_include-->

                        <Export-Package>*;-split-package:=merge-last</Export-Package>
                        <Bundle-Activator>br.gov.lexml.borda.Toolkit</Bundle-Activator>
                        <Bundle-Name>Toolkit</Bundle-Name>
                        <Private-Package />
                        <Embed-Dependency>*;scope=!test;inline=true</Embed-Dependency>
                        <Bundle-ClassPath>.,{maven-dependencies}</Bundle-ClassPath>
                    </instructions>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <dependencies>
        <dependency>
            <groupId>org.apache.xmlbeans</groupId>
            <artifactId>xmlbeans</artifactId>
            <version>2.4.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.xmlbeans</groupId>
            <artifactId>xmlbeans-xmlpublic</artifactId>
            <version>2.4.0</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.15</version>
        </dependency>
        <dependency>
            <groupId>br.gov.lexmlbeans</groupId>
            <artifactId>lexmlbeans</artifactId>
            <version>3.0</version>
        </dependency>
    </dependencies>
</project>


回答6:

I believe the Maven Shade Plugin will satisfy your needs. I use it when I am building command line interface tools to create an Uber JAR including my classes and along with the classes from all my dependencies.

Its very easy to use and I think this example is self-explanatory.