2

Google Guice でターゲットのアノテーション値を解釈するプロバイダーとバインドする方法はありますか?

例:

bind(Resource.class)
    .annotatedWith(MyAnnotation.class)
    .toProvider(new MyProvider<MyAnnotation, Resource>{
        public Resource get(MyAnnotation anno){
            return resolveResourceByAnnoValue(anno.value());
        }
    });

アノテーション付きバインディングで Android Activity クラスのフィールドを初期化したい。一意の ID によって複数のリソースを取得する必要があります。

元の方法:

public class TestActivity extends Activity{
    private TextView textView;
    private Button testButton;

    public void onAfterCreate(...){
        // set UI declaration resource.
        setContentView(R.layout.activity_test);

        // initialize fields, it must be done after setting ui definition.
        textView = (TextView) findViewById(R.id.textView);

        .... initialize other fields, hook them...

    ...
}

上記のように実用的にではなく、宣言的な方法で UI とそのフィールドをバインドしたい:

@ResourceID(R.layout.activity_test)
public class TestActivity extends InjectiveActivity{
    @ResourceID(R.id.textView) // Auto generated static resource id constant
    private TextView textView;

    @ResourceID(R.id.testButton)
    private Button testButton;

    ...
}
4

2 に答える 2

3

これ自体は不可能です。

がバインディングアノテーションの場合、そのメソッド@MyAnnotationを使用して比較されます。にバインドされ、。と比較してまったく一致しません。詳細については、このSOの回答を確認してください。その答えのように、必要に応じて、可能な注釈値をループして、それぞれを個別にバインドすることができます。equals@MyAnnotation(5) Resource@MyAnnotation(5) Resource@MyAnnotation(6) Resource

がバインディングアノテーションでない場合@MyAnnotation、プロバイダーからはまったくアクセスできません。このSO回答で述べたように、インジェクションサイト情報をプロバイダーまたは依存関係自体に追加することは拒否された機能です。

最善の策は、パラメータを受け入れるための@Assistedインジェクション(または手動ファクトリ)を作成することです。

class MyConsumer {
  final Resource resource;
  @Inject MyConsumer(Resource.Factory resourceFactory) {
    int previouslyAnnotatedValue = 5;
    this.resource = resourceFactory.createWithValue(previouslyAnnotatedValue);
  }
}

カスタムインジェクションの使用を検討することもできます。これにより、以外@Injectの任意のアノテーションを使用できるようになります。これにより、ランタイムアノテーション値を任意に使用できます。

于 2013-02-05T04:17:54.147 に答える
0

これはScalaの例です(私はプロトタイピングにScalaを使用するのが好きです。結局のところ、別のドレスのJavaです) 、アノテーションの値に応じて動的Googleジュースインジェクションで自分で疑問に思った後に思いつきました

import java.lang.reflect.{Constructor, Parameter}
import java.util.concurrent.atomic.AtomicReference
import javax.inject.{Inject, Named, Provider}

import com.google.inject.matcher.Matchers
import com.google.inject.spi.ProvisionListener.ProvisionInvocation
import com.google.inject.{AbstractModule, Binder, Guice}
import com.google.inject.spi.{DependencyAndSource, ProviderInstanceBinding, ProvisionListener}
import com.typesafe.config.ConfigFactory
import net.codingwell.scalaguice.InjectorExtensions._
import net.codingwell.scalaguice.ScalaModule

import scala.collection.JavaConverters._

object GuiceExperiments extends App {

  val injector = Guice.createInjector(new MyModule())

  val some = injector.instance[Some]

  println(some)

  some.go()
}

trait Some {
  def go(): Unit
}

class Impl @Inject()(
                    @Named("a.a.a") hello: String,
                    @Named("a.a.b") bello: String,
                    @Named("a.b.a") kello: String

                    ) extends Some {
  override def go() = {
    println(hello)
    println(bello)
    println(kello)
  }
}

abstract class DynamicProvider[T >: Null](binder: Binder) extends Provider[T] {

  private[this] val nextValue = new AtomicReference[T]

  binder.bindListener(Matchers.any(), new ProvisionListener {

    private[this] def tryProvide(target: DependencyAndSource): Unit = {
      val dependency = target.getDependency
      val injectionPoint = dependency.getInjectionPoint
      val parameterIndex = dependency.getParameterIndex

      injectionPoint.getMember match {
        case constructor: Constructor[_] =>
          val parameter = constructor.getParameters()(parameterIndex)
          nextValue.set(getFor(parameter))
      }
    }

    override def onProvision[V](provision: ProvisionInvocation[V]): Unit = {

      provision.getBinding match {
        case binding: ProviderInstanceBinding[_] if binding.getUserSuppliedProvider eq DynamicProvider.this =>
          provision.getDependencyChain.asScala.lastOption.foreach(tryProvide)
        case _ => ()
      }
    }
  })

  final override def get(): T = nextValue.getAndSet(null)

  def getFor(parameter: Parameter): T
}

class MyModule extends AbstractModule with ScalaModule {

  override def configure(): Unit = {

    bind[Some].to[Impl]

    bind[String].annotatedWith[Named].toProvider(new DynamicProvider[String](binder) {
      override def getFor(parameter: Parameter): String = {
        if (parameter.isAnnotationPresent(classOf[Named])) {
          parameter.getAnnotation(classOf[Named]).value()
        } else {
          null
        }
      }
    })

  }

}

これは の値を挿入するだけですが、@Namedかなり機能しているように見えます。ありえないほど。

于 2016-04-16T23:03:22.893 に答える