/** * Returns a proxy instance for the specified interfaces * that dispatches method invocations to the specified invocation * handler. * <p> * <a id="restrictions">{@code IllegalArgumentException} will be thrown * if any of the following restrictions is violated:</a> * <ul> * <li>All of {@code Class} objects in the given {@code interfaces} array * must represent {@linkplain Class#isHidden() non-hidden} and * {@linkplain Class#isSealed() non-sealed} interfaces, * not classes or primitive types. * * <li>No two elements in the {@code interfaces} array may * refer to identical {@code Class} objects. * * <li>All of the interface types must be visible by name through the * specified class loader. In other words, for class loader * {@code cl} and every interface {@code i}, the following * expression must be true:<p> * {@code Class.forName(i.getName(), false, cl) == i} * * <li>All of the types referenced by all * public method signatures of the specified interfaces * and those inherited by their superinterfaces * must be visible by name through the specified class loader. * * <li>All non-public interfaces must be in the same package * and module, defined by the specified class loader and * the module of the non-public interfaces can access all of * the interface types; otherwise, it would not be possible for * the proxy class to implement all of the interfaces, * regardless of what package it is defined in. * * <li>For any set of member methods of the specified interfaces * that have the same signature: * <ul> * <li>If the return type of any of the methods is a primitive * type or void, then all of the methods must have that same * return type. * <li>Otherwise, one of the methods must have a return type that * is assignable to all of the return types of the rest of the * methods. * </ul> * * <li>The resulting proxy class must not exceed any limits imposed * on classes by the virtual machine. For example, the VM may limit * the number of interfaces that a class may implement to 65535; in * that case, the size of the {@code interfaces} array must not * exceed 65535. * </ul> * * <p>Note that the order of the specified proxy interfaces is * significant: two requests for a proxy class with the same combination * of interfaces but in a different order will result in two distinct * proxy classes. * * @param loader the class loader to define the proxy class * @param interfaces the list of interfaces for the proxy class * to implement * @param h the invocation handler to dispatch method invocations to * @return a proxy instance with the specified invocation handler of a * proxy class that is defined by the specified class loader * and that implements the specified interfaces * @throws IllegalArgumentException if any of the <a href="#restrictions"> * restrictions</a> on the parameters are violated * @throws SecurityException if a security manager, <em>s</em>, is present * and any of the following conditions is met: * <ul> * <li> the given {@code loader} is {@code null} and * the caller's class loader is not {@code null} and the * invocation of {@link SecurityManager#checkPermission * s.checkPermission} with * {@code RuntimePermission("getClassLoader")} permission * denies access;</li> * <li> for each proxy interface, {@code intf}, * the caller's class loader is not the same as or an * ancestor of the class loader for {@code intf} and * invocation of {@link SecurityManager#checkPackageAccess * s.checkPackageAccess()} denies access to {@code intf};</li> * <li> any of the given proxy interfaces is non-public and the * caller class is not in the same {@linkplain Package runtime package} * as the non-public interface and the invocation of * {@link SecurityManager#checkPermission s.checkPermission} with * {@code ReflectPermission("newProxyInPackage.{package name}")} * permission denies access.</li> * </ul> * @throws NullPointerException if the {@code interfaces} array * argument or any of its elements are {@code null}, or * if the invocation handler, {@code h}, is * {@code null} * * @see <a href="#membership">Package and Module Membership of Proxy Class</a> * @revised 9 */ @CallerSensitive publicstatic Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) { Objects.requireNonNull(h);
/** * {@code InvocationHandler} is the interface implemented by * the <i>invocation handler</i> of a proxy instance. * * <p>Each proxy instance has an associated invocation handler. * When a method is invoked on a proxy instance, the method * invocation is encoded and dispatched to the {@code invoke} * method of its invocation handler. * * @author Peter Jones * @see Proxy * @since 1.3 */ publicinterfaceInvocationHandler {
/** * Processes a method invocation on a proxy instance and returns * the result. This method will be invoked on an invocation handler * when a method is invoked on a proxy instance that it is * associated with. * * @param proxy the proxy instance that the method was invoked on * * @param method the {@code Method} instance corresponding to * the interface method invoked on the proxy instance. The declaring * class of the {@code Method} object will be the interface that * the method was declared in, which may be a superinterface of the * proxy interface that the proxy class inherits the method through. * * @param args an array of objects containing the values of the * arguments passed in the method invocation on the proxy instance, * or {@code null} if interface method takes no arguments. * Arguments of primitive types are wrapped in instances of the * appropriate primitive wrapper class, such as * {@code java.lang.Integer} or {@code java.lang.Boolean}. * * @return the value to return from the method invocation on the * proxy instance. If the declared return type of the interface * method is a primitive type, then the value returned by * this method must be an instance of the corresponding primitive * wrapper class; otherwise, it must be a type assignable to the * declared return type. If the value returned by this method is * {@code null} and the interface method's return type is * primitive, then a {@code NullPointerException} will be * thrown by the method invocation on the proxy instance. If the * value returned by this method is otherwise not compatible with * the interface method's declared return type as described above, * a {@code ClassCastException} will be thrown by the method * invocation on the proxy instance. * * @throws Throwable the exception to throw from the method * invocation on the proxy instance. The exception's type must be * assignable either to any of the exception types declared in the * {@code throws} clause of the interface method or to the * unchecked exception types {@code java.lang.RuntimeException} * or {@code java.lang.Error}. If a checked exception is * thrown by this method that is not assignable to any of the * exception types declared in the {@code throws} clause of * the interface method, then an * {@link UndeclaredThrowableException} containing the * exception that was thrown by this method will be thrown by the * method invocation on the proxy instance. * * @see UndeclaredThrowableException */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
/** * Invokes the specified default method on the given {@code proxy} instance with * the given parameters. The given {@code method} must be a default method * declared in a proxy interface of the {@code proxy}'s class or inherited * from its superinterface directly or indirectly. * <p> * Invoking this method behaves as if {@code invokespecial} instruction executed * from the proxy class, targeting the default method in a proxy interface. * This is equivalent to the invocation: * {@code X.super.m(A* a)} where {@code X} is a proxy interface and the call to * {@code X.super::m(A*)} is resolved to the given {@code method}. * <p> * Examples: interface {@code A} and {@code B} both declare a default * implementation of method {@code m}. Interface {@code C} extends {@code A} * and inherits the default method {@code m} from its superinterface {@code A}. * * <blockquote><pre>{@code * interface A { * default T m(A a) { return t1; } * } * interface B { * default T m(A a) { return t2; } * } * interface C extends A {} * }</pre></blockquote> * * The following creates a proxy instance that implements {@code A} * and invokes the default method {@code A::m}. * * <blockquote><pre>{@code * Object proxy = Proxy.newProxyInstance(loader, new Class<?>[] { A.class }, * (o, m, params) -> { * if (m.isDefault()) { * // if it's a default method, invoke it * return InvocationHandler.invokeDefault(o, m, params); * } * }); * }</pre></blockquote> * * If a proxy instance implements both {@code A} and {@code B}, both * of which provides the default implementation of method {@code m}, * the invocation handler can dispatch the method invocation to * {@code A::m} or {@code B::m} via the {@code invokeDefault} method. * For example, the following code delegates the method invocation * to {@code B::m}. * * <blockquote><pre>{@code * Object proxy = Proxy.newProxyInstance(loader, new Class<?>[] { A.class, B.class }, * (o, m, params) -> { * if (m.getName().equals("m")) { * // invoke B::m instead of A::m * Method bMethod = B.class.getMethod(m.getName(), m.getParameterTypes()); * return InvocationHandler.invokeDefault(o, bMethod, params); * } * }); * }</pre></blockquote> * * If a proxy instance implements {@code C} that inherits the default * method {@code m} from its superinterface {@code A}, then * the interface method invocation on {@code "m"} is dispatched to * the invocation handler's {@link #invoke(Object, Method, Object[]) invoke} * method with the {@code Method} object argument representing the * default method {@code A::m}. * * <blockquote><pre>{@code * Object proxy = Proxy.newProxyInstance(loader, new Class<?>[] { C.class }, * (o, m, params) -> { * if (m.isDefault()) { * // behaves as if calling C.super.m(params) * return InvocationHandler.invokeDefault(o, m, params); * } * }); * }</pre></blockquote> * * The invocation of method {@code "m"} on this {@code proxy} will behave * as if {@code C.super::m} is called and that is resolved to invoking * {@code A::m}. * <p> * Adding a default method, or changing a method from abstract to default * may cause an exception if an existing code attempts to call {@code invokeDefault} * to invoke a default method. * * For example, if {@code C} is modified to implement a default method * {@code m}: * * <blockquote><pre>{@code * interface C extends A { * default T m(A a) { return t3; } * } * }</pre></blockquote> * * The code above that creates proxy instance {@code proxy} with * the modified {@code C} will run with no exception and it will result in * calling {@code C::m} instead of {@code A::m}. * <p> * The following is another example that creates a proxy instance of {@code C} * and the invocation handler calls the {@code invokeDefault} method * to invoke {@code A::m}: * * <blockquote><pre>{@code * C c = (C) Proxy.newProxyInstance(loader, new Class<?>[] { C.class }, * (o, m, params) -> { * if (m.getName().equals("m")) { * // IllegalArgumentException thrown as {@code A::m} is not a method * // inherited from its proxy interface C * Method aMethod = A.class.getMethod(m.getName(), m.getParameterTypes()); * return InvocationHandler.invokeDefault(o, aMethod params); * } * }); * c.m(...); * }</pre></blockquote> * * The above code runs successfully with the old version of {@code C} and * {@code A::m} is invoked. When running with the new version of {@code C}, * the above code will fail with {@code IllegalArgumentException} because * {@code C} overrides the implementation of the same method and * {@code A::m} is not accessible by a proxy instance. * * @apiNote * The {@code proxy} parameter is of type {@code Object} rather than {@code Proxy} * to make it easy for {@link InvocationHandler#invoke(Object, Method, Object[]) * InvocationHandler::invoke} implementation to call directly without the need * of casting. * * @param proxy the {@code Proxy} instance on which the default method to be invoked * @param method the {@code Method} instance corresponding to a default method * declared in a proxy interface of the proxy class or inherited * from its superinterface directly or indirectly * @param args the parameters used for the method invocation; can be {@code null} * if the number of formal parameters required by the method is zero. * @return the value returned from the method invocation * * @throws IllegalArgumentException if any of the following conditions is {@code true}: * <ul> * <li>{@code proxy} is not {@linkplain Proxy#isProxyClass(Class) * a proxy instance}; or</li> * <li>the given {@code method} is not a default method declared * in a proxy interface of the proxy class and not inherited from * any of its superinterfaces; or</li> * <li>the given {@code method} is overridden directly or indirectly by * the proxy interfaces and the method reference to the named * method never resolves to the given {@code method}; or</li> * <li>the length of the given {@code args} array does not match the * number of parameters of the method to be invoked; or</li> * <li>any of the {@code args} elements fails the unboxing * conversion if the corresponding method parameter type is * a primitive type; or if, after possible unboxing, any of the * {@code args} elements cannot be assigned to the corresponding * method parameter type.</li> * </ul> * @throws IllegalAccessException if the declaring class of the specified * default method is inaccessible to the caller class * @throws NullPointerException if {@code proxy} or {@code method} is {@code null} * @throws Throwable anything thrown by the default method * @since 16 * @jvms 5.4.3. Method Resolution */ @CallerSensitive publicstatic Object invokeDefault(Object proxy, Method method, Object... args) throws Throwable { Objects.requireNonNull(proxy); Objects.requireNonNull(method);
// verify that the object is actually a proxy instance if (!Proxy.isProxyClass(proxy.getClass())) { thrownewIllegalArgumentException("'proxy' is not a proxy instance"); } if (!method.isDefault()) { thrownewIllegalArgumentException("\"" + method + "\" is not a default method"); } @SuppressWarnings("unchecked") Class<? extendsProxy> proxyClass = (Class<? extendsProxy>)proxy.getClass();
Class<?> intf = method.getDeclaringClass(); // access check on the default method method.checkAccess(Reflection.getCallerClass(), intf, proxyClass, method.getModifiers());
MethodHandlemh= Proxy.defaultMethodHandle(proxyClass, method); // invoke the super method try { // the args array can be null if the number of formal parameters required by // the method is zero (consistent with Method::invoke) Object[] params = args != null ? args : Proxy.EMPTY_ARGS; return mh.invokeExact(proxy, params); } catch (ClassCastException | NullPointerException e) { thrownewIllegalArgumentException(e.getMessage(), e); } catch (Proxy.InvocationException e) { // unwrap and throw the exception thrown by the default method throw e.getCause(); } } }