如何告诉 proguard 保留枚举常量和字段

Posted

技术标签:

【中文标题】如何告诉 proguard 保留枚举常量和字段【英文标题】:How to tell proguard to keep enum constants and fields 【发布时间】:2016-01-16 07:32:58 【问题描述】:

我尝试混淆我们使用 spring、jaxb 并严重依赖注释和反射的 Web 应用程序。 我应用了许多在互联网上找到的食谱来保留一些类、属性、注释和枚举。 但是用枚举还是有问题的。我已经能够保留枚举常量应用来自http://proguard.sourceforge.net/manual/examples.html#enumerations 的配置:

-keepclassmembers,allowoptimization enum * 
    public static **[] values();
    public static ** valueOf(java.lang.String);

乍一看,它看起来像是有效的解决方案并保留了常量,因此 (Class.getEnumConstants()) 返回正确的值列表。 但是当我尝试按名称检索字段时,我得到了 NoSuchFieldException。

问题来自jaxb reflection navigator,请看代码:

public Field[] getEnumConstants(Class clazz) 
    try 
        Object[] values = clazz.getEnumConstants();
        Field[] fields = new Field[values.length];
        for (int i = 0; i < values.length; i++) 
            fields[i] = clazz.getField(((Enum) values[i]).name());
        
        return fields;
     catch (NoSuchFieldException e) 
        // impossible
        throw new NoSuchFieldError(e.getMessage());
    

我完全落入“不可能”的分支。我认为查看调试会话屏幕截图会很容易理解(还列出了constants):

如果我尝试获取fields,它们会被混淆为a、b、c、d、e、f:

我的 proguard 配置现在看起来像(删除一些库列表并 kipp 关于 proguard 抱怨的特定类、字段和方法):

-injars  core-3.15.rc5.6.jar
-outjars core-3.15.rc5.6.proguard.jar

-libraryjars <java.home>/lib/rt.jar

-libraryjars ... # Other libs listed, strip out for shortness

-printmapping core-3.15.rc5.6.proguard.map

-keep public class ru.rlh.egais.portal.backend.controller.rest.**
-keep public class ru.rlh.egais.portal.backend.integration.soap.service.**

# http://midgetontoes.com/blog/2015/06/26/tips-for-using-proguard-with-spring-framework
-optimizations !class/marking/final

-adaptresourcefilecontents **.properties,META-INF/MANIFEST.MF,META-INF/spring.*,spring/*

-keepattributes Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,*Annotation*,EnclosingMethod
# Also tried:
# -keepattributes **

-allowaccessmodification
-dontshrink
-dontoptimize
-dontusemixedcaseclassnames
-keepdirectories
-keep @org.springframework.transaction.annotation.Transactional class *
-keep @org.springframework.stereotype.Service class *
-keep @org.springframework.stereotype.Repository class *
-keep @org.springframework.stereotype.Controller class *
-keep @org.springframework.stereotype.Component class *
-keep @org.springframework.beans.factory.annotation.Autowired class *
-keep @org.springframework.web.bind.annotation.ResponseBody class *
-keep @org.springframework.web.bind.annotation.RequestMapping class *
-keep @org.springframework.stereotype.Repository class *
-keep @javax.annotation.Resource class *
-keep @org.springframework.cache.annotation.EnableCaching class *
-keep @org.springframework.context.annotation.Configuration class *

-keepclassmembers class * 
    @org.springframework.beans.factory.annotation.* *;
    @org.springframework.beans.factory.annotation.Qualifier *;
    @org.springframework.beans.factory.annotation.Value *;
    @org.springframework.beans.factory.annotation.Required *;
    @org.springframework.context.annotation.Bean *;
    @javax.annotation.PostConstruct *;
    @javax.annotation.PreDestroy *;
    @org.aspectj.lang.annotation.AfterReturning *;
    @org.aspectj.lang.annotation.Pointcut *;
    @org.aspectj.lang.annotation.AfterThrowing *;
    @org.aspectj.lang.annotation.Around *;


-keepclassmembers enum * 
    public static **[] values();
    public static ** valueOf(java.lang.String);

那么,我的问题是如何完全防止公共枚举被混淆?在这两种情况下 - 使用它的常量 (class.getEnumConstants()) 和字段 (class.getFields())。

【问题讨论】:

【参考方案1】:

感谢http://sourceforge.net/p/proguard/discussion/182455/thread/1c28f199/我找到了我的问题的解决方案(必须添加<⁠fields>):

-keepclassmembers class * extends java.lang.Enum 
    <fields>;
    public static **[] values();
    public static ** valueOf(java.lang.String);

【讨论】:

对于遇到此问题并想要 ant 等效项的任何人:添加 到你的 proguard 任务。 我想你说的是maven任务配置?在最初的问题中没有关于 Maven 的内容。我使用gradle,但它也超出了范围。因此,答案中提供的所有配置都属于proguard.conf 语法。 Hubbitus 是的,但是我在尝试解决这个问题时发现这个答案是第三个链接,但使用的是 ant(不是 maven),而不是直接使用 proguard。两者之间的翻译不是很明显,至少对我来说不是,所以我添加了一条评论,以便在我的条件下找到这个的任何其他人也可以获得额外的信息。这绝不意味着您的答案不正确或不完整。如果这是你做出的推论,我很遗憾。 另见ProGuard docs - Processing enumeration classes。 - 对于 android 项目,可以通过在 build.gradle 中引用 proguard-android.txt 来加载这些设置。【参考方案2】:

这对我来说很好。

-keep public enum com.company.appname.**
    *;

其中**是包和子包。

【讨论】:

完美答案。

以上是关于如何告诉 proguard 保留枚举常量和字段的主要内容,如果未能解决你的问题,请参考以下文章

如何告诉 ProGuard 保留私有字段而不指定每个字段

Proguard 不会保留班级成员的枚举

如何告诉 ProGuard 保留用于 onClick 的函数?

如何使 ProGuard 保留整个(整个)类(带有字段和方法)而不使用“-keep”

如何防止proguard完全混淆方法

Proguard 保留公共类、字段和方法