使用 proguard 混淆后,使用 google guice 注入不再起作用
Posted
技术标签:
【中文标题】使用 proguard 混淆后,使用 google guice 注入不再起作用【英文标题】:Injection with google guice does not work anymore after obfuscation with proguard 【发布时间】:2011-01-23 02:51:37 【问题描述】:有没有人尝试过将 google guice 的使用与混淆(特别是 proguard)结合起来? 我的代码的混淆版本不适用于 google guice,因为 guice 抱怨缺少类型参数。这个信息似乎被 proguard 所做的转换步骤删除了,即使相关类被排除在混淆之外。
堆栈跟踪如下所示:
com.google.inject.CreationException: Guice creation errors:
1) Cannot inject a Provider that has no type parameter
while locating com.google.inject.Provider
for parameter 0 at de.repower.lvs.client.admin.user.administration.AdminUserCommonPanel.setPasswordPanelProvider(SourceFile:499)
at de.repower.lvs.client.admin.user.administration.AdminUserCommonPanel.setPasswordPanelProvider(SourceFile:499)
while locating de.repower.lvs.client.admin.user.administration.AdminUserCommonPanel
for parameter 0 at de.repower.lvs.client.admin.user.administration.b.k.setParentPanel(SourceFile:65)
at de.repower.lvs.client.admin.user.administration.b.k.setParentPanel(SourceFile:65)
at de.repower.lvs.client.admin.user.administration.o.a(SourceFile:38)
2) Cannot inject a Provider that has no type parameter
while locating com.google.inject.Provider
for parameter 0 at de.repower.lvs.client.admin.user.administration.AdminUserCommonPanel.setWindTurbineAccessGroupProvider(SourceFile:509)
at de.repower.lvs.client.admin.user.administration.AdminUserCommonPanel.setWindTurbineAccessGroupProvider(SourceFile:509)
while locating de.repower.lvs.client.admin.user.administration.AdminUserCommonPanel
for parameter 0 at de.repower.lvs.client.admin.user.administration.b.k.setParentPanel(SourceFile:65)
at de.repower.lvs.client.admin.user.administration.b.k.setParentPanel(SourceFile:65)
at de.repower.lvs.client.admin.user.administration.o.a(SourceFile:38)
2 errors
at com.google.inject.internal.Errors.throwCreationExceptionIfErrorsExist(Errors.java:354)
at com.google.inject.InjectorBuilder.initializeStatically(InjectorBuilder.java:152)
at com.google.inject.InjectorBuilder.build(InjectorBuilder.java:105)
at com.google.inject.Guice.createInjector(Guice.java:92)
at com.google.inject.Guice.createInjector(Guice.java:69)
at com.google.inject.Guice.createInjector(Guice.java:59)
我尝试创建一个似乎重现问题的小示例(不使用 guice):
package de.repower.common;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
class SomeClass<S>
public class ParameterizedTypeTest
public void someMethod(SomeClass<Integer> param)
System.out.println("value: " + param);
System.setProperty("my.dummmy.property", "hallo");
private static void checkParameterizedMethod(ParameterizedTypeTest testObject)
System.out.println("checking parameterized method ...");
Method[] methods = testObject.getClass().getMethods();
for (Method method : methods)
if (method.getName().equals("someMethod"))
System.out.println("Found method " + method.getName());
Type[] types = method.getGenericParameterTypes();
Type parameterType = types[0];
if (parameterType instanceof ParameterizedType)
Type parameterizedType = ((ParameterizedType) parameterType).getActualTypeArguments()[0];
System.out.println("Parameter: " + parameterizedType);
System.out.println("Class: " + ((Class) parameterizedType).getName());
else
System.out.println("Failed: type ist not instance of ParameterizedType");
public static void main(String[] args)
System.out.println("Starting ...");
try
ParameterizedTypeTest someInstance = new ParameterizedTypeTest();
checkParameterizedMethod(someInstance);
catch (SecurityException e)
e.printStackTrace();
如果你在 unsbfuscated 的情况下运行此代码,输出如下所示:
Starting ...
checking parameterized method ...
Found method someMethod
Parameter: class java.lang.Integer
Class: java.lang.Integer
但运行使用 proguard 混淆的版本:
Starting ...
checking parameterized method ...
Found method someMethod
Failed: type ist not instance of ParameterizedType
这些是我用来混淆的选项:
-injars classes_eclipse\methodTest.jar
-outjars classes_eclipse\methodTestObfuscated.jar
-libraryjars 'C:\Program Files\Java\jre6\lib\rt.jar'
-dontskipnonpubliclibraryclasses
-dontskipnonpubliclibraryclassmembers
-dontshrink
-printusage classes_eclipse\shrink.txt
-dontoptimize
-dontpreverify
-verbose
-keep class **.ParameterizedTypeTest.class
<fields>;
<methods>;
-keep class **
<fields>;
<methods>;
# Keep - Applications. Keep all application classes, along with their 'main'
# methods.
-keepclasseswithmembers public class *
public static void main(java.lang.String[]);
# Also keep - Enumerations. Keep the special static methods that are required in
# enumeration classes.
-keepclassmembers enum *
public static **[] values();
public static ** valueOf(java.lang.String);
# Also keep - Database drivers. Keep all implementations of java.sql.Driver.
-keep class * extends java.sql.Driver
# Also keep - Swing UI L&F. Keep all extensions of javax.swing.plaf.ComponentUI,
# along with the special 'createUI' method.
-keep class * extends javax.swing.plaf.ComponentUI
public static javax.swing.plaf.ComponentUI createUI(javax.swing.JComponent);
# Keep names - Native method names. Keep all native class/method names.
-keepclasseswithmembers,allowshrinking class *
native <methods>;
# Keep names - _class method names. Keep all .class method names. This may be
# useful for libraries that will be obfuscated again with different obfuscators.
-keepclassmembers,allowshrinking class *
java.lang.Class class$(java.lang.String);
java.lang.Class class$(java.lang.String,boolean);
有没有人知道如何解决这个问题(除了将相关文件放入单独的 jar 并且不混淆它的明显解决方法)?
最好的问候, 斯蒂芬
【问题讨论】:
【参考方案1】:在使用 proguard 很长一段时间后,我决定如何解决有关反射的问题(Guice 只是它的一个用例)。
只要没有以字符串形式输入类或方法名,反射就可以与 Proguard 一起使用。
也就是说这段代码是有效的,经过ProGuard混淆后可以工作
Class someClass = Class.forName(SomeClass.class.getName());
虽然这段代码不起作用
Class someClass = Class.forName("SomeClass");
此外,Proguard 将收缩未调用的方法和构造函数。因此,@987654321@
方法将不起作用。不幸的是,通常的 Guice 绑定使用这种方法。
这对 Guice 代码有一些影响。
您的所有注入都必须使用 @Provides 注释方法生成,因为 ProGuard 会收缩类,因为它们的构造函数没有被显式调用。 Proguard 不得压缩模块类的代码。 ProGuard 不得缩小注释,无论它们在哪里,无论它们在哪里(可以配置,但我不记得在哪里)。【讨论】:
哇,这是一个可怕的消息,你必须使用 @Provides 方法来阻止 ProGuard 剥离依赖项。这需要一个工具来提供帮助。我认为在 proguard 配置中粘贴一堆 -keep 行也可以(几乎同样令人讨厌)? 可能是因为添加 -keep 注释会降低重构效率,因为我们必须确保在输出 jar 中类名有效。在我看来,在纯 Java 级别上运行会使我们的代码更符合重构要求(如果存在这种情况)【参考方案2】:在 JDK 5.0 及更高版本中编译时,需要“Signature”属性才能访问泛型类型。
使用 -keepattributes 签名修复 ParameterizedType 错误
【讨论】:
它没有解决上面示例中的问题。我仍然需要尝试解决我原来使用 google guice 的问题。 这为我在 android 上使用 no-aop 构建修复了它,我遇到了与 OP 相同的问题。我在 proguard 配置中也有以下内容。 -keepclassmembers 类 * @com.google.inject.Inject使用当前版本的 Proguard (4.7),我可以通过添加以下内容来使其正常工作:-
-keepattributes *Annotation*,Signature
-keep class com.google.inject.Binder
-keep public class com.google.inject.Inject
# keeps all fields and Constructors with @Inject
-keepclassmembers,allowobfuscation class *
@com.google.inject.Inject <fields>;
@com.google.inject.Inject <init>(...);
除了显式保留由 Guice 创建的任何类,例如
-keep class com.example.Service
【讨论】:
此修改不需要显式添加您的@Inject 类(如其他帖子中所述容易出错):-keepclasseswithmembers,allowoptimization,allowobfuscation class * @com.google.inject.Inject <methods>; -keepclassmembers,allowobfuscation,allowoptimization class * @com.google.inject.Provides <methods>;
【参考方案4】:
以下代码对我有用,遇到了同样的问题。
-keepattributes Signature
是解决方法。
-optimizationpasses 5
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-dontpreverify
#-dontobfuscate
-repackageclasses ''
-keepattributes *Annotation*
-keepattributes Signature
-verbose
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.app.backup.BackupAgentHelper
-keep public class * extends android.preference.Preference
-keep public class com.android.vending.licensing.ILicensingService
-keepclasseswithmembernames class *
native <methods>;
-keepclasseswithmembernames class *
public <init>(android.content.Context, android.util.AttributeSet);
-keepclasseswithmembernames class *
public <init>(android.content.Context, android.util.AttributeSet, int);
-keepclassmembers enum *
public static **[] values();
public static ** valueOf(java.lang.String);
-keepattributes Signature
-keep class * implements android.os.Parcelable
public static final android.os.Parcelable$Creator *;
-keep class com.google.inject.Binder
-keepclassmembers class *
@com.google.inject.Inject <init>(...);
-keep public class * extends android.view.View
public <init>(android.content.Context);
public <init>(android.content.Context, android.util.AttributeSet);
public <init>(android.content.Context, android.util.AttributeSet, int);
public void set*(...);
# I didn't need this one, maybe you need it.
#-keep public class roboguice.**
-keepclassmembers class **.R$*
public static <fields>;
【讨论】:
以上是关于使用 proguard 混淆后,使用 google guice 注入不再起作用的主要内容,如果未能解决你的问题,请参考以下文章
ProGuard 混淆、java、Google Gson 和泛型集合——如何留住成员?
通过将 proguard 规则映射文件上传到 google play 控制台去混淆生产 Android 错误
使用 ProGuard 混淆 clojure uberjars