ProGuard(http://proguard.sourceforge.net/)对jar文件可以进行缩减,优化和混淆等操作。缩减jar文件将多个输入jar文件(injar参数)合成一个输出jar文件(outjar参数)。优化执行与编译器优化类似的操作。基本的混淆操作将变量名、方法名等替换为较短的别名。 这里利用ProGuard的第一个功能(缩减)将多个jar文件合成一个jar文件,并剔除这些文件中用不到的类。
注意:在缩减和混淆操作中,为保证结果的正确,ProGuard能够自动处理Class.forName("MyClass") 和 MyClass.class这样的结构,因而不会将类MyClass剔除或混淆; 在某些情况下,如果ProGuard不能作出决定会警告用户。
例如有一个应用程序myapp.jar, 该应用程序用到Apache commons-lang3-3.0库,但是只用到个别的类,为了避免在发布时将Apache commons-lang3-3.0库和myapp.jar一起发布,可以通过ProGuard的缩减功能将这两个jar文件合并成一个jar:myapp-dist.jar(注:不清楚这样做是否违反了Apache的license约定)。
执行build.xml的run target之前的目录结构:
./src/org/test/proguard/Main.java
./commons-lang3-3.0/commons-lang3-3.0.jar
./lib/proguard.jar
./build.xml
./manifest.mf
执行完build.xml的run任务之后的目录结构:
./src/org/test/proguard/Main.java
./commons-lang3-3.0/commons-lang3-3.0.jar
./lib/proguard.jar
./build/org/test/proguard/Main.class
./out/myapp.jar
./lib/myapp-dist.jar
./build.xml
./manifest.mf
其中./build/org/test/proguard/Main.class是compile target生成的;./out/myapp.jar是jar target生成的;./lib/myapp-dist.jar是shrink target生成的,这个文件就是合并之后的jar文件。
文件列表:
myapp.jar/Main.java:
package org.test.proguard;
import org.apache.commons.lang3.StringUtils;
public class Main {
public static void main(String[] args) {
String text = "ThisIsAVeryLongLongText";
String shorttext = StringUtils.abbreviate(text, 15);
System.out.println(text);
System.out.println(shorttext);
}
}
build.xml: (注: 没有使用ProGuard的优化和混淆功能:optimize="off" obfuscate ="off")
<?xml version="1.0" encoding="UTF-8"?>
<project name="proguard" default="run" basedir="./">
<property name="package.dir" value="org/test/proguard"/>
<property name="javahome" value="/usr/local/jdk1.5.0_16" />
<property name="jdk.lib.dir" value="${javahome}/lib" />
<property name="jre.lib.dir" value="${javahome}/jre/lib" />
<property name="proguard.lib" value="./lib" />
<property name="commons.lang.lib" value="./commons-lang3-3.0/" />
<property name="src.dir" value="./src" />
<property name="build.dir" value="./build" />
<property name="out.dir" value="./out" />
<property name="dist.dir" value="./lib" />
<property name="manifest.file" value="manifest.mf"/>
<property name="outjar.file" value="myapp.jar"/>
<property name="distjar.file" value="${dist.dir}/myapp-dist.jar"/>
<path id="lib.path">
<fileset dir="${jdk.lib.dir}">
<include name="**/*.jar" />
</fileset>
<fileset dir="${jre.lib.dir}">
<include name="**/*.jar" />
</fileset>
<fileset dir="${proguard.lib}">
<include name="**/*.jar" />
</fileset>
<fileset dir="${commons.lang.lib}">
<include name="**/*.jar" />
</fileset>
</path>
<target name="clean">
<tstamp/>
<delete includeEmptyDirs="true" quiet="yes">
<fileset dir="${build.dir}" />
<fileset dir="${out.dir}" />
</delete>
</target>
<target name="compile" depends="clean">
<mkdir dir="${build.dir}" />
<javac srcdir="${src.dir}/${package.dir}" destdir="${build.dir}" debug="yes">
<classpath refid="lib.path" />
</javac>
</target>
<target name="jar" depends="compile">
<mkdir dir="${out.dir}" />
<jar jarfile="${out.dir}/${outjar.file}" manifest="${manifest.file}">
<fileset dir="${build.dir}">
<include name="**/*"/>
</fileset>
</jar>
</target>
<target name="shrink" depends="jar">
<taskdef resource="proguard/ant/task.properties" classpathref="lib.path"/>
<proguard optimize="off" obfuscate ="off" printseeds="on"
printmapping="out.map"
renamesourcefileattribute="SourceFile">
<!-- Specify the input jars, output jars, and library jars. -->
<injar file="${out.dir}/${outjar.file}" />
<injar file="${commons.lang.lib}/commons-lang3-3.0.jar" />
<outjar file="${distjar.file}" />
<libraryjar file="${java.home}/lib/rt.jar" />
<!-- libraryjar file="junit.jar" / -->
<!-- libraryjar file="servlet.jar" / -->
<!-- libraryjar file="jai_core.jar" / -->
<!-- ... / -->
<!-- Preserve line numbers in the obfuscated stack traces. -->
<keepattribute name="LineNumberTable" />
<keepattribute name="SourceFile" />
<!-- Preserve all annotations. -->
<keepattribute name="*Annotation*" />
<!-- Preserve all public applications. -->
<keepclasseswithmembers access="public">
<method access ="public static"
type ="void"
name ="main"
parameters="java.lang.String[]" />
</keepclasseswithmembers>
<!-- Preserve all native method names and the names of their classes. -->
<keepclasseswithmembernames>
<method access="native" />
</keepclasseswithmembernames>
<!-- Preserve the methods that are required in all enumeration classes. -->
<keepclassmembers extends="java.lang.Enum">
<method access="public static"
type="**[]"
name="values"
parameters="" />
<method access="public static"
type="**"
name="valueOf"
parameters="java.lang.String" />
</keepclassmembers>
<!-- Explicitly preserve all serialization members. The Serializable
interface is only a marker interface, so it wouldn't save them.
You can comment this out if your library doesn't use serialization.
If your code contains serializable classes that have to be backward
compatible, please refer to the manual. -->
<keepclassmembers implements="java.io.Serializable">
<field access ="static final"
type ="long"
name ="serialVersionUID" />
<field access ="static final"
type ="java.io.ObjectStreamField[]"
name ="serialPersistentFields" />
<method access ="private"
type ="void"
name ="writeObject"
parameters="java.io.ObjectOutputStream" />
<method access ="private"
type ="void"
name ="readObject"
parameters="java.io.ObjectInputStream" />
<method type ="java.lang.Object"
name ="writeReplace"
parameters="" />
<method type ="java.lang.Object"
name ="readResolve"
parameters="" />
</keepclassmembers>
<!-- Your application may contain more items that need to be preserved;
typically classes that are dynamically created using Class.forName -->
</proguard>
</target>
<target name="run" depends="shrink">
<!--run: java -jar lib/app-dist.jar -->
<java jar="${distjar.file}" fork="yes">
</java>
</target>
</project>