だから私は...事を思い付いた...動作するように思われる:
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つの選択肢を考えることができ
は、Java 9をターゲットにしている場合は、新しいスタックを使用することができますウォーキングAPI:https://www.javaworld.com/article/3188289/core-java/java-9s-other-new-enhancements-part-5-stack-walking-apihtml –
@TomG - より多くのguice特有の方法を見つけましたが、そのAPIは本当にクールです。それらを追加するのに十分な時間がかかりました:-)。 –