2017-12-06 10 views
1

プロバイダに何かが注入されているクラスを取得する方法はありますか?これはロギング用です - 私のSqlDatabaseが何かをするとき、それが使用されているクラスの名前でログに表示されます。私が考えることができる最高のスタックトレースを取得し、それを使用してどこを見つけるために後方に行くが、私は本当にむしろ噴射時間にそれを行うだろう。Guiceプロバイダでバインディングターゲットを取得するにはどうすればよいですか?

質問をする別の方法は次のとおりです。注入されたクラスのインスタンスを作成するために、注入サイト(@Inject注釈が見つかった正確なクラス)を見つける必要があります。

+1

は、Java 9をターゲットにしている場合は、新しいスタックを使用することができますウォーキングAPI:https://www.javaworld.com/article/3188289/core-java/java-9s-other-new-enhancements-part-5-stack-walking-apihtml –

+0

@TomG - より多くのguice特有の方法を見つけましたが、そのAPIは本当にクールです。それらを追加するのに十分な時間がかかりました:-)。 –

答えて

0

だから私は...事を思い付いた...動作するように思われる:

import com.google.inject.Binder; 
import com.google.inject.Binding; 
import com.google.inject.Key; 
import com.google.inject.matcher.AbstractMatcher; 
import com.google.inject.matcher.Matcher; 
import com.google.inject.spi.DependencyAndSource; 
import com.google.inject.spi.InjectionPoint; 
import com.google.inject.spi.ProvisionListener; 
import java.util.List; 
import java.util.function.Function; 
import javax.inject.Provider; 

/** 
* This is a dirty, dirty hack to allow some types to know what they're being injected into. There are other 
* (possibly cleaner) ways to do this, but this has the nice advantage of the injected class not needing 
* to know anything about this. 
*/ 
public class ContextAwareInjection 
{ 
    public static <T> void bindContextAwareProvider(Binder binder, Class<T> type, Function<InjectionPoint, T> provider) 
    { 
     bindContextAwareProvider(binder, Key.get(type), provider); 
    } 

    public static <T> void bindContextAwareProvider(Binder binder, Key<T> key, Function<InjectionPoint, T> provider) 
    { 
     Matcher<Binding<?>> matcher = new AbstractMatcher<Binding<?>>() { 
      @Override public boolean matches(Binding<?> binding) { return binding.getKey().equals(key); } }; 
     ContextAwareImpl<T> impl = new ContextAwareImpl<>(provider, key); 
     binder.bind(key).toProvider(impl); 
     binder.bindListener(matcher, impl); 
    } 

    private static class ContextAwareImpl<T> implements ProvisionListener, Provider<T> 
    { 
     private final Key<T> key; 
     private final Function<InjectionPoint, T> provider; 
     private final ThreadLocal<T> current = new ThreadLocal<>(); 

     private ContextAwareImpl(Function<InjectionPoint, T> provider, Key<T> key) 
     { 
      this.provider = provider; 
      this.key = key; 
     } 

     @Override 
     public <T2> void onProvision(ProvisionInvocation<T2> pi) 
     { 
      if(!pi.getBinding().getKey().equals(key)) 
       throw new RuntimeException("Unexpected key -- got " + pi.getBinding().getKey() + ", expected " + key); 
      try 
      { 
       List<DependencyAndSource> chain = pi.getDependencyChain(); 
       if(chain.isEmpty()) 
        throw new RuntimeException("This should never be empty"); 
       DependencyAndSource das = chain.get(chain.size() - 1); 
       InjectionPoint ip = das == null || das.getDependency() == null ? null : das.getDependency().getInjectionPoint(); 
       T value = provider.apply(ip); 
       if(value == null) 
        throw new RuntimeException("Context aware providers should never return null"); 
       current.set(value); 
       pi.provision(); 
      } 
      finally 
      { 
       current.remove(); 
      } 
     } 

     @Override 
     public T get() 
     { 
      T value = current.get(); 
      if(value == null) 
       throw new RuntimeException("There is no current value -- this should never happen"); 
      return value; 
     } 
    } 
} 

これは、いくつかのわずかな実行時のパフォーマンスに影響があるかもしれませんが、テストのために、それは正常に動作します。さらに重要なことは、には変更がないことがすべてターゲットクラスのであることです。あなたはいつものように@Injectを使い続けることができます。私は2つの選択肢を考えることができ

0

:使用Guice AOP

  • あなたのバインディングを宣言するときに、あなたが実際のメソッドの実行に
  • を記録することができます

    1. 使用@Provides方法はここで両方のアプローチのおもちゃの例です:

      package stackoverflowscrapbook; 
      
      import java.util.logging.Logger; 
      
      import org.aopalliance.intercept.MethodInterceptor; 
      import org.aopalliance.intercept.MethodInvocation; 
      import org.junit.Test; 
      
      import com.google.inject.AbstractModule; 
      import com.google.inject.Guice; 
      import com.google.inject.Injector; 
      import com.google.inject.Provides; 
      import com.google.inject.matcher.Matchers; 
      
      public class TestLogInjectedClassName { 
      
          interface SqlDatabase { 
           void doSomething(); 
          } 
      
          static class SqlDatabaseImpl implements SqlDatabase { 
      
           public void doSomething() { 
            System.out.println("foo"); 
           } 
      
          } 
      
          static class AlternativeImpl implements SqlDatabase { 
      
           public void doSomething() { 
            System.out.println("bar"); 
           } 
      
          } 
      
          class Module extends AbstractModule { 
      
           @Provides 
           SqlDatabase database(SqlDatabaseImpl sqlDatabase) { 
            Logger.getLogger(TestLogInjectedClassName.Module.class.getName()) 
              .info("Providing " + sqlDatabase.getClass()); 
            return sqlDatabase; 
           } 
      
           @Override 
           protected void configure() { 
            //TODO: other bindings 
           } 
          } 
      
          class AlternativeModule extends AbstractModule { 
      
           @Provides 
           SqlDatabase database(AlternativeImpl sqlDatabase) { 
            Logger.getLogger(TestLogInjectedClassName.Module.class.getName()) 
              .info("Providing " + sqlDatabase.getClass()); 
            return sqlDatabase; 
           } 
      
           @Override 
           protected void configure() { 
            //TODO: other bindings 
           } 
          } 
      
          /** 
          * Just log 
          */ 
          class LoggingInterceptor implements MethodInterceptor { 
      
           public Object invoke(MethodInvocation methodInvocation) throws Throwable { 
            Logger.getLogger(getClass().getName()).info(methodInvocation.getStaticPart().toString()); 
            return methodInvocation.proceed(); 
           } 
      
          } 
      
          /** 
          * Binds an interceptor on any method of any class implementing SqlDatabase 
          */ 
          class AopModule extends AbstractModule { 
      
           @Override 
           protected void configure() { 
            bindInterceptor(Matchers.subclassesOf(SqlDatabase.class), Matchers.any(), new LoggingInterceptor()); 
      
           } 
          } 
      
          @Test 
          public void test() { 
           Injector injector = Guice.createInjector(new Module()); 
           injector.getInstance(SqlDatabase.class); 
      
           Injector anotheInjector = Guice.createInjector(new AlternativeModule()); 
           anotheInjector.getInstance(SqlDatabase.class); 
      
          } 
      
          @Test 
          public void testAop() { 
           Injector aopInjector = Guice.createInjector(new AopModule(), new Module()); 
           aopInjector.getInstance(SqlDatabase.class).doSomething(); 
      
           Injector alterntiveAopInjector = Guice.createInjector(new AopModule(), new AlternativeModule()); 
           alterntiveAopInjector.getInstance(SqlDatabase.class).doSomething(); 
          } 
      
      } 
      

      私はここにtestAop()を実行すると、私はHERESに私のコンソール

      Dec 10, 2017 9:12:15 AM stackoverflowscrapbook.TestLogInjectedClassName$Module database 
      INFO: Providing class stackoverflowscrapbook.TestLogInjectedClassName$SqlDatabaseImpl 
      Dec 10, 2017 9:12:15 AM stackoverflowscrapbook.TestLogInjectedClassName$AlternativeModule database 
      INFO: Providing class stackoverflowscrapbook.TestLogInjectedClassName$AlternativeImpl 
      

      test()を実行すると、私のコンソールです。この場合、メソッドの両方のログインを記録することに注意してください。 @Providesメソッドのログを削除して、実行のみを記録することもできます。またGuiceがインスタンスSqlDatabaseを作成し、その実装のメソッドがでなく、 finalである場合にのみ可能です。 (行がプレビューにみじん切りされていたように私は以下のログコードをフォーマットしていない)

      2017年12月10日午前9時14分01秒AM stackoverflowscrapbook.TestLogInjectedClassName $モジュールデータベース INFO:クラスstackoverflowscrapbook.TestLogInjectedClassName $ SqlDatabaseImplを提供$$公共のボイドstackoverflowscrapbook.TestLogInjectedClassName $ SqlDatabaseImpl.doSomething() FOO 2017年12月10日9時14分01秒AMのstackoverflowscrapbook:EnhancerByGuice $$は9時14分01秒AM stackoverflowscrapbook.TestLogInjectedClassName $ LoggingInterceptorが INFOを呼び出す2017年12月10日 をd7a0782d。 TestLogInjectedClassName $ AlternativeModuleデータベース INFO:クラスの提供stackoverflowscrapbook.TestLogInjectedClassName $ AlternativeImpl $$ EnhancerByGuice $$ 3ef 8fbf1 2017年12月10日午前9時14分01秒AM stackoverflowscrapbook.TestLogInjectedClassName $ LoggingInterceptor呼び出す INFOます。public void stackoverflowscrapbook.TestLogInjectedClassName $ AlternativeImpl.doSomething() バー

    +0

    ありがとう!残念ながら、これらのメソッドのどちらも、注入が完了する前にコンテキストにアクセスする方法や、注入される場所によってオブジェクトを異なる方法で変更/初期化する方法はありません。 –

    関連する問題