Инъекция с Google Guice больше не работает после запутывания с Proguard - PullRequest
7 голосов
/ 03 марта 2010

Кто-нибудь когда-нибудь пытался совместить использование гугл гайса с обфускацией (в частности с 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)

Я попытался создать небольшой пример (без использования хитрости), который, кажется, воспроизводит проблему:

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();
        }

    }

}

Если вы запустите этот код без ошибок, вывод будет выглядеть следующим образом:

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);
}

Кто-нибудь имеет представление о том, как решить эту проблему (кроме очевидного обходного пути, чтобы поместить соответствующие файлы в отдельную банку и не запутывать ее)?

С уважением,
Stefan

Ответы [ 4 ]

8 голосов
/ 11 марта 2010

Воспользовавшись proguard в течение продолжительного времени, я решил решить вопросы, связанные с рефлексией (а Guice - лишь один из вариантов его использования).

Отражение может использоваться с Proguard до тех пор, пока НЕТ класса или имени метода не будут введены как строки.

То есть этот код действителен и будет работать после запутывания ProGuard

Class someClass = Class.forName(SomeClass.class.getName());

пока этот код не будет работать

Class someClass = Class.forName("SomeClass");

Кроме того, Proguard сократит невостребованные методы и конструктор. Как следствие, метод <a href="http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Class.html#newInstance()" rel="nofollow noreferrer">Class.newInstance</a> не будет работать. К сожалению, обычные привязки Guice работают с использованием этого метода.

Это имеет некоторые последствия для кода Guice.

  • Все ваши инъекции должны производиться с использованием аннотированных методов @Provides, поскольку ProGuard будет сокращать классы из-за того, что их конструкторы явно не вызываются.
  • Proguard не должен сокращать код классов ваших модулей.
  • ProGuard не должен сокращать аннотации, где бы они ни находились (это можно настроить, но я не могу вспомнить, где).
7 голосов
/ 12 марта 2010

Атрибут «Signature» необходим для доступа к универсальным типам при компиляции в JDK 5.0 и выше.

Используйте -keepattributes Signature для исправления ошибки с ParameterizedType

3 голосов
/ 25 апреля 2012

С текущей версией 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
0 голосов
/ 30 апреля 2011

Следующий код работает для меня, имея ту же проблему.

-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>;
}
...