Как создать новый экземпляр из имени класса в gwt - PullRequest
7 голосов
/ 14 июня 2010

У меня есть класс int со следующим именем com.test.TestClass

В какой-то момент в моем коде я должен получить экземпляр этого класса, имея только строку имени класса. Я пытался использовать GWT.create() Но это работает только в режиме разработки. Может ли кто-нибудь сказать мне, как получить экземпляр в gwt из имени класса.

Ответы [ 3 ]

13 голосов
/ 14 июня 2010

Поскольку отражение на стороне клиента невозможно, единственное решение, имитирующее отражение, заключается в использовании отложенного связывания.

Используйте отложенную привязку, чтобы обнаружить все классы, для которых вы хотите создать экземпляр с именем класса во время компиляции. Вы можете использовать интерфейс маркера на всех таких классах, чтобы помочь TypeOracle идентифицировать их. Вы динамически генерируете фабричный класс, который принимает простое имя класса и возвращает вновь созданный объект этого класса. Подход очень прост, и вы найдете хорошее объяснение отложенного связывания в учебниках Google для загрузки.

Edit: - Некоторый скелетный код, чтобы вы начали. (Урезанная версия моего производственного кода, проверьте на ошибки компилятора в сгенерированном файле! И отладьте поток)

First> Добавьте следующую ошибку в ваш * .gwt.xml, чтобы компилятор вызывал наш com.package.ReflectionGenerator, который сгенерирует простой фабричный класс для имитации отражения на стороне клиента. .

  <generate-with class="com.package.ReflectionGenerator">
      <when-type-assignable class="com.package.client.Reflection" />
  </generate-with>

Далее> Определить интерфейс для нашего фабричного класса

public interface Reflection {
    public <T, V extends T> T instantiate( Class<V> clazz );
}

Последнее> Реализация ReflectionGenerator

import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;

import com.google.gwt.core.ext.BadPropertyValueException;
import com.google.gwt.core.ext.Generator;
import com.google.gwt.core.ext.GeneratorContext;
import com.google.gwt.core.ext.PropertyOracle;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
import com.google.gwt.user.rebind.SourceWriter;

public class ReflectionGenerator extends Generator
{    
    @Override
    public String generate( TreeLogger logger, GeneratorContext context, String typeName ) throws UnableToCompleteException
    {
        TypeOracle oracle = context.getTypeOracle( );

        JClassType instantiableType = oracle.findType( MarkerInterface.class.getName( ) );

        List<JClassType> clazzes = new ArrayList<JClassType>( );

        PropertyOracle propertyOracle = context.getPropertyOracle( );

        for ( JClassType classType : oracle.getTypes( ) )
        {
            if ( !classType.equals( instantiableType ) && classType.isAssignableTo( instantiableType ) )
                clazzes.add( classType );
        }

        final String genPackageName = "com.package.client";
        final String genClassName = "ReflectionImpl";

        ClassSourceFileComposerFactory composer = new ClassSourceFileComposerFactory( genPackageName, genClassName );
        composer.addImplementedInterface( Reflection.class.getCanonicalName( ) );

        composer.addImport( "com.package.client.*" );

        PrintWriter printWriter = context.tryCreate( logger, genPackageName, genClassName );

        if ( printWriter != null )
        {
            SourceWriter sourceWriter = composer.createSourceWriter( context, printWriter );
            sourceWriter.println( "ReflectionImpl( ) {" );
            sourceWriter.println( "}" );

            printFactoryMethod( clazzes, sourceWriter );

            sourceWriter.commit( logger );
        }
        return composer.getCreatedClassName( );
    }

    private void printFactoryMethod( List<JClassType> clazzes, SourceWriter sourceWriter )
    {
        sourceWriter.println( );

        sourceWriter.println( "public <T, V extends T> T instantiate( Class<V> clazz ) {" );

        for ( JClassType classType : clazzes )
        {
            if ( classType.isAbstract( ) )
                continue;

            sourceWriter.println( );
            sourceWriter.indent( );
            sourceWriter.println( "if (clazz.getName().endsWith(\"." + classType.getName( ) + "\")) {" );
            sourceWriter.indent( );
            sourceWriter.println( "return (T) new " + classType.getQualifiedSourceName( ) + "( );" );
            sourceWriter.outdent( );
            sourceWriter.println( "}" );
            sourceWriter.outdent( );
            sourceWriter.println( );
        }
        sourceWriter.indent();
        sourceWriter.println("return (T) null;");
        sourceWriter.outdent();
        sourceWriter.println();
        sourceWriter.println("}");
        sourceWriter.outdent( );
        sourceWriter.println( );
    }
}

Это должно сгенерировать фабричный класс ReflectionGenerator в вашей рабочей области, проверить сгенерированный файл и настроить исходный код средства записи, чтобы сгенерировать нужный код.

Использование GWT.create( Reflection.class ).instantiate( YourClass.class );

Я использовал маркерный интерфейс 'MarkerInterface' в генераторе, чтобы ограничить число классов, поддерживаемых фабрикой, поэтому в результате все участвующие классы должны реализовать 'MarkerInterface'

6 голосов
/ 17 июня 2013

Вот хорошо протестированная, прокомментированная и слегка переработанная версия кода Эшвина Прабху:

https://bitbucket.org/espinosa/z025-gwt-maven-alternative-setup/src/d35a3fb7e627b5598fb763f480e3f76932cf4232/src/main/java/my/code/z025/util/ClassFromStringFactoryGenerator.java?at=master

Пример использования:

String targetEntryPointClass = "my.code.client.Sample3";
ClassFromStringFactory classFromStringFactory = GWT.create(ClassFromStringFactory.class);
Object targetEntryPointInstance = classFromStringFactory.instantiate(targetEntryPointClass);
if (targetEntryPointInstance == null) {
      // throw some exception
}
if (targetEntryPointInstance instanceof EntryPoint) {
      ((EntryPoint) targetEntryPointInstance).onModuleLoad();
} else {
      // throw some exception
}

См. Полный исходный код: https://bitbucket.org/espinosa/z025-gwt-maven-alternative-setup/src/d35a3fb7e627b5598fb763f480e3f76932cf4232/src/main/java/my/code/z025/client/Dispatcher.java?at=master

В моем проекте я использую собственную EntryPoint GWT в качестве интерфейса маркера. Это позволяет мне запускать произвольную EntryPoint только через URL: http://localhost:8080/my.code.client.Sample3; Диспетчер EntryPoint создает my.code.client.Sample3 через мой ClassFromStringFactory. В дескрипторе модуля GWT и отложенной привязке настроена только точка входа диспетчера, все остальное является динамическим.

Для любопытства, вот что генерирует GWT (сервер кода в DevMode или компилятор для производственного режима), содержимое моего ClassFromStringFactoryImpl:

package my.code.client.reflection;

public class ClassFromStringFactoryImpl implements ClassFromStringFactory {
  public ClassFromStringFactoryImpl( ) {}

  public Object instantiate(String className) {
    if (className == null) {
      return null
    }
    else if (className.equals("my.code.client.Sample1")) {
      return new my.code.client.Sample1( );
    }
    else if (className.equals("my.code.client.Sample2")) {
      return new my.code.client.Sample2( );
    }
    ..and so on, 3 same lines per every supported type
    return null;
  }
}

Во временном файле, например: C:\Users\espinosa\AppData\Local\Temp\my.code.client.reflection.ClassFromStringFactoryImpl4245548251877324156.java. Примечание: этот файл генерируется только в случае сбоя, а не при успешной компиляции

Как видите, это не настоящий самоанализ. Отложенное связывание не делает никакой особой магии. Подобный код Java может быть сгенерирован шаблоном Velocity как часть сборки Maven или специальных инструментов, таких как XText, APT-Jelly. Использование GWT Generator - это просто удобство.

Важно ограничить количество «поддерживаемых» классов, иначе сгенерированный ClassFromStringFactoryImpl будет слишком большим, нереально огромным или даже превысит ограничения для класса Java. Необходима какая-то фильтрация, маркировка интерфейса - это всего лишь одна опция, другие маркируют аннотацию (см. JClassType # getAnnotation (Class)) GWT или только выбранные пакеты. В любом случае, убедитесь, что число поддерживаемых классов этим «отражением» не превышает величины сотен.

Большое спасибо Ашвину Прабху за то, что он указал мне правильное направление.

2 голосов
/ 09 июня 2012

GWT.create (Reflection.class) .instantiate (YourClass.class);

Почему бы вам тогда не использовать только GWT.create( YourClass.class );?

Может бытьВы можете GWT.create( Reflection.class ).instantiate( "YourClass" );

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...