📄 bindutil.java
字号:
/* * Copyright 2007 Tim Peierls * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package org.directwebremoting.guice;import com.google.inject.Binder;import com.google.inject.Inject;import com.google.inject.Injector;import com.google.inject.Key;import com.google.inject.Provider;import java.lang.reflect.Constructor;import java.lang.reflect.Method;import java.lang.reflect.InvocationTargetException;import static java.lang.reflect.Modifier.isStatic;/** * Binding utilities for creating ad hoc providers. These come in two flavors, * constructor and factory method. In both cases, you can specify additional * methods to inject after creation by chaining calls to * {@link BindingProvider#injecting injecting(methodName, paramKeys)}. * Each method has two variants, one with a {@code Binder} parameter and one * without. The former reports construction errors via the binder (so more than * one error can be reported), the latter throws a runtime exception immediately, * preventing further error reporting. * * <p> Some examples: * <pre> * bind(SomeInterface.class) * .toProvider( * .fromConstructor(LegacyImpl.class, * Key.get(String.class, MyAnnotation.class)) * .injecting("configure", Key.get(Configuration.class))); * </pre> * This binds {@code SomeInterface} to a provider that uses the * {@code LegacyImpl} constructor with a single String parameter. * That parameter is injected as if it had been marked with {@code @MyAnnotation}. * It also calls the {@code LegacyImpl.configure} method with an injected * instance of {@code Configuration}. * <pre> * bind(SomeInterface.class) * .toProvider( * .fromFactoryMethod(SomeInterface.class, * LegacyFactory.class, "newSomeInterface", * Key.get(String.class, MyAnnotation.class))); * </pre> * This binds {@code SomeInterface} to a provider that calls a factory method * {@code newSomeInterface} of {@code LegacyFactory} with a single string parameter, * which is injected as if it were marked with {@code @MyAnnotation}. If the * method is not static, an instance of {@code LegacyFactory} is created by injection * and used to call the method. * @author Tim Peierls [tim at peierls dot net] */public class BindUtil { /** * For fluent-style decoration with one or more method bindings when * using {@link #fromConstructor}. */ public interface BindingProvider<T> extends Provider<T> { /** * Adds injection of a method defined by the given name and parameter * types (specified as Guice keys) to this provider. */ BindingProvider<T> injecting(String methodName, Key... paramKeys); } /** * Creates a chainable provider that constructs an instance of the * given type given a list of constructor parameter types, specified * as Guice keys. Construction errors are thrown immediately. */ public static <T> BindingProvider<T> fromConstructor(Class<T> type, final Key... keys) { return new ConstructorBindingProvider<T>(null, type, keys); } /** * Creates a chainable provider that constructs an instance of the * given type given a list of constructor parameter types, specified * as Guice keys. Construction errors are passed to {@code binder} * to be thrown at the end of all binding. */ public static <T> BindingProvider<T> fromConstructor(Binder binder, Class<T> type, final Key... keys) { return new ConstructorBindingProvider<T>(binder, type, keys); } /** * Creates a chainable provider that constructs an instance of {@code providedType} * using a factory method defined by {@code factoryType}, {@code methodName}, * and a list of method parameter types specified as Guice keys. If the * method is not static an instance of the factory type is created and injected. * Construction errors are thrown immediately. */ public static <T> BindingProvider<T> fromFactoryMethod( Class<T> providedType, Class<?> factoryType, String methodName, final Key... keys) { return new FactoryMethodBindingProvider<T>( null, providedType, Key.get(factoryType), methodName, keys); } /** * Creates a chainable provider that constructs an instance of {@code providedType} * using a factory method defined by {@code factoryType}, {@code methodName}, * and a list of method parameter types specified as Guice keys. If the * method is not static an instance of the factory type is created and injected. * Construction errors are passed to {@code binder} to be thrown at the end * of all binding. */ public static <T> BindingProvider<T> fromFactoryMethod( Binder binder, Class<T> providedType, Class<?> factoryType, String methodName, final Key... keys) { return new FactoryMethodBindingProvider<T>( binder, providedType, Key.get(factoryType), methodName, keys); } /** * Creates a chainable provider that constructs an instance of {@code providedType} by * calling method {@code methodName} of the type in {@code factoryKey} with * method parameter types specified as Guice keys. If the method is not static * an instance is created and injected using the factory key. * Construction errors are thrown immediately. */ public static <T> BindingProvider<T> fromFactoryMethod( Class<T> providedType, Key<?> factoryKey, String methodName, final Key... keys) { return new FactoryMethodBindingProvider<T>( null, providedType, factoryKey, methodName, keys); } /** * Creates a chainable provider that constructs an instance of {@code providedType} by * calling method {@code methodName} of the type in {@code factoryKey} with * method parameter types specified as Guice keys. If the method is not static * an instance is created and injected using the factory key. * Construction errors are passed to {@code binder} to be thrown at the end * of all binding. */ public static <T> BindingProvider<T> fromFactoryMethod( Binder binder, Class<T> providedType, Key<?> factoryKey, String methodName, final Key... keys) { return new FactoryMethodBindingProvider<T>( binder, providedType, factoryKey, methodName, keys); } // // Implementation classes // private static abstract class AbstractBindingProvider<T> implements BindingProvider<T> { protected AbstractBindingProvider(Binder binder, Class<T> type, Key... keys) { this.binder = binder; this.type = type; this.keys = keys; } public final T get() { return get(theInjector); } protected abstract T get(Injector injector); public final BindingProvider<T> injecting(String methodName, Key... paramKeys) { return new MethodBindingProvider(this, type, methodName, paramKeys); } protected final Class[] getTypes() { Class[] types = new Class[keys.length]; int i = 0; for (Key key : keys) { @SuppressWarnings("unchecked") Class type = (Class) key.getTypeLiteral().getType(); types[i++] = type; } return types; } protected final Object[] getValues(Injector injector) { Object[] values = new Object[keys.length]; int i = 0; for (Key key : keys) { Object param = injector.getInstance(key); values[i++] = param; } return values; } protected final Binder binder; protected final Class<T> type; protected final Key[] keys; /** * Effectively immutable: Injected at end of bind-time, * read-only thereafter, and there is (or should be) a * happens-before edge between bind-time and subsequent reads. */ @Inject private Injector theInjector; } private static class ConstructorBindingProvider<T> extends AbstractBindingProvider<T> { ConstructorBindingProvider(Binder binder, Class<T> type, Key... keys) { super(binder, type, keys); Constructor<T> constructor = null; try { constructor = type.getConstructor(getTypes()); } catch (NoSuchMethodException e) { if (binder == null) { throw new IllegalArgumentException("no such constructor", e); } else { binder.addError(e); } } this.constructor = constructor; } public T get(Injector injector) { try { return constructor.newInstance(getValues(injector)); } catch (InstantiationException e) { throw new IllegalStateException(e); } catch (IllegalAccessException e) { throw new IllegalStateException(e); } catch (InvocationTargetException e) { throw new IllegalStateException(e); } } private final Constructor<T> constructor; } private static class MethodBindingProvider<T> extends AbstractBindingProvider<T> { MethodBindingProvider(AbstractBindingProvider<T> prev, Class<T> type, String methodName, Key... keys) { super(prev.binder, type, keys); Method method = null; try { method = type.getMethod(methodName, getTypes()); } catch (NoSuchMethodException e) { if (binder == null) { throw new IllegalArgumentException("no such method", e); } else { binder.addError(e); } } this.prev = prev; this.method = method; } public T get(Injector injector) { T target = prev.get(injector); try { method.invoke(target, getValues(injector)); } catch (IllegalAccessException e) { throw new IllegalStateException(e); } catch (InvocationTargetException e) { throw new IllegalStateException(e); } return target; } private final AbstractBindingProvider<T> prev; private final Method method; } private static class FactoryMethodBindingProvider<T> extends AbstractBindingProvider<T> { FactoryMethodBindingProvider(Binder binder, Class<T> providedType, Key<?> factoryKey, String methodName, Key... keys) { super(binder, providedType, keys); Method method = null; boolean isStaticFactory = false; try { @SuppressWarnings("unchecked") Class factoryType = (Class) factoryKey.getTypeLiteral().getType(); method = factoryType.getMethod(methodName, getTypes()); method.getReturnType().asSubclass(providedType); } catch (NoSuchMethodException e) { if (binder == null) { throw new IllegalArgumentException("no such method", e); } else { binder.addError(e); } } catch (ClassCastException e) { if (binder == null) { throw new IllegalArgumentException("bad return type", e); } else { binder.addError(e); } } this.method = method; this.factoryKey = factoryKey; } public T get(Injector injector) { try { Object target = null; if (!isStatic(method.getModifiers())) { target = injector.getInstance(factoryKey); } @SuppressWarnings("unchecked") T result = (T) method.invoke(target, getValues(injector)); return result; } catch (IllegalAccessException e) { throw new IllegalStateException(e); } catch (InvocationTargetException e) { throw new IllegalStateException(e); } } private final Method method; private final Key<?> factoryKey; }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -