自动委托 java class 的所有方法
Automatically delegating all methods of a java class
假设我有一个 class 有许多 public 方法:
public class MyClass {
public void method1() {}
public void method2() {}
(...)
public void methodN() {}
}
现在我想创建一个 wrapper class 它将所有方法委托给包装实例 (delegate) :
public class WrapperClass extends MyClass {
private final MyClass delegate;
public WrapperClass(MyClass delegate) {
this.delagate = delegate;
}
public void method1() { delegate.method1(); }
public void method2() { delegate.method2(); }
(...)
public void methodN() { delegate.methodN(); }
}
现在,如果 MyClass 有很多方法,我将需要重写它们中的每一个,它们或多或少与 "delegates" 相同的代码。我想知道是否可以做一些魔术来自动调用 Java 中的方法(因此包装器 class 需要说“嘿,如果你对我调用方法,请转到 委托对象并在其上调用此方法)。
顺便说一句:我不能使用继承,因为委托不在我的 control.I 之下,只是从其他地方获取它的实例(另一种情况是如果 MyClass 是最终的)。
注意:我不想IDE生成。我知道我可以在 IntelliJ/Eclipse 的帮助下做到这一点,但我很好奇这是否可以用代码完成。
有什么建议可以实现这样的目标吗? (注意:我可能可以用一些脚本语言来做到这一点,比如 php,在那里我可以使用 php 魔术函数来拦截调用)。
您不必这样做 -- 您的 Wrapper class 是原始 class 的子class,因此它继承了 all 的公开访问方法 -- 如果您不实现它们,将调用原始方法。
您不应该将 extends Myclass
与私有 MyClass
对象一起使用 - 这确实是多余的,而且我想不出这样做是正确的设计模式。你的 WrapperClass
是 一个 MyClass
,因此你可以只使用它自己的字段和方法而不是调用 delegate
.
编辑:在 MyClass
为 final
的情况下,您将绕过故意声明不允许通过 "faking" 继承进行子class;除了你,我想不出还有谁愿意这样做,你控制着 WrapperClass
;但是,由于您可以控制 WrapperClass
,所以不包装您不需要的所有东西实际上不仅仅是一种选择——这是正确的做法,因为您的对象 不是 a MyClass
,并且只应在您考虑过的情况下表现得像一个。
EDIT 您刚刚通过将 MyClass
superclass 删除到您的 WrapperClass
将您的问题更改为完全不同的意思;这有点糟糕,因为它使到目前为止给出的所有答案都无效。你应该打开另一个问题。
在 WrapperClass
中定义一个方法,即 delegate()
returns MyClass
的实例
或
您可以使用反射来做到这一点,但调用者必须将方法名称作为参数传递给公开的方法。并且关于方法 arguments/overloaded 方法等会有并发症
BTW: I can not use inheritance because the delegate is not under my control.I just get its instance from elsewhere (another case would be if MyClass was final)
您发布的代码有 public class WrapperClass extends MyClass
实际上,您当前的 WrapperClass
实现实际上是 MyClass
之上的装饰器
也许 java 的动态 Proxy
可以帮到你。它仅在您因此使用接口时才有效。在这种情况下,我将调用接口 MyInterface
并设置默认实现:
public class MyClass implements MyInterface {
@Override
public void method1() {
System.out.println("foo1");
}
@Override
public void method2() {
System.out.println("foo2");
}
@Override
public void methodN() {
System.out.println("fooN");
}
public static void main(String[] args) {
MyClass wrapped = new MyClass();
wrapped.method1();
wrapped.method2();
MyInterface wrapper = WrapperClass.wrap(wrapped);
wrapper.method1();
wrapper.method2();
}
}
包装器 class 实现如下所示:
public class WrapperClass extends MyClass implements MyInterface, InvocationHandler {
private final MyClass delegate;
public WrapperClass(MyClass delegate) {
this.delegate = delegate;
}
public static MyInterface wrap(MyClass wrapped) {
return (MyInterface) Proxy.newProxyInstance(MyClass.class.getClassLoader(), new Class[] { MyInterface.class }, new WrapperClass(wrapped));
}
//you may skip this definition, it is only for demonstration
public void method1() {
System.out.println("bar");
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Method m = findMethod(this.getClass(), method);
if (m != null) {
return m.invoke(this, args);
}
m = findMethod(delegate.getClass(), method);
if (m != null) {
return m.invoke(delegate, args);
}
return null;
}
private Method findMethod(Class<?> clazz, Method method) throws Throwable {
try {
return clazz.getDeclaredMethod(method.getName(), method.getParameterTypes());
} catch (NoSuchMethodException e) {
return null;
}
}
}
注意这个 class:
- 扩展
MyClass
,以继承默认实现(任何其他都可以)
- 实施
Invocationhandler
,以允许代理进行反射
- 可选择实现
MyInterface
(以满足装饰器模式)
此解决方案允许您覆盖特殊方法,但委托所有其他方法。这甚至可以与 Wrapper class.
的子 classes 一起使用
请注意,方法 findMethod
尚未捕获特殊情况。
这个问题已经有 6 个月了,@CoronA 的精彩回答已经满足并被@walkeros 接受,但我想我会在这里添加一些东西,因为我认为这可以进一步推进。
正如@CoronA 在对他的回答的评论中讨论的那样,不必在 WrapperClass
(即 public void methodN() { delegate.methodN(); }
)中创建和维护一长串 MyClass
方法,动态代理解决方案将其移至界面。问题是你仍然需要为接口中的 MyClass
方法创建和维护一长串签名,这可能更简单一些但并不能完全解决问题。如果您无法访问 MyClass
以了解所有方法,情况尤其如此。
根据Three approaches for decorating your code,
For longer classes, a programmer must choose the lesser of two evils:
implement many wrapper methods and keep the type of decorated object
or maintain a simple decorator implementation and sacrifice retaining
the decorated object type.
所以这也许是装饰器模式的预期限制。
然而,@Mark-Bramnik 给出了 fascinating solution using CGLIB at Interposing on Java Class Methods (without interfaces)。我能够将它与@CoronaA 的解决方案结合起来,以创建一个包装器,该包装器可以覆盖各个方法,然后将其他所有内容传递给包装对象 而无需 需要接口。
这里是MyClass
.
public class MyClass {
public void method1() { System.out.println("This is method 1 - " + this); }
public void method2() { System.out.println("This is method 2 - " + this); }
public void method3() { System.out.println("This is method 3 - " + this); }
public void methodN() { System.out.println("This is method N - " + this); }
}
这里是 WrapperClass
,它只覆盖 method2()
。正如您将在下面看到的,非覆盖方法实际上并没有传递给委托,这可能是个问题。
public class WrapperClass extends MyClass {
private MyClass delagate;
public WrapperClass(MyClass delegate) { this.delagate = delegate; }
@Override
public void method2() {
System.out.println("This is overridden method 2 - " + delagate);
}
}
这里是 MyInterceptor
,它扩展了 MyClass
。如@Mark-Bramnik 所述,它使用使用 CGLIB 的代理解决方案。它还使用@CononA 的方法来确定是将方法发送到包装器(如果它被覆盖)还是被包装的对象(如果它没有被覆盖)。
import java.lang.reflect.Method;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class MyInterceptor extends MyClass implements MethodInterceptor {
private Object realObj;
public MyInterceptor(Object obj) { this.realObj = obj; }
@Override
public void method2() {
System.out.println("This is overridden method 2 - " + realObj);
}
@Override
public Object intercept(Object arg0, Method method, Object[] objects,
MethodProxy methodProxy) throws Throwable {
Method m = findMethod(this.getClass(), method);
if (m != null) { return m.invoke(this, objects); }
Object res = method.invoke(realObj, objects);
return res;
}
private Method findMethod(Class<?> clazz, Method method) throws Throwable {
try {
return clazz.getDeclaredMethod(method.getName(), method.getParameterTypes());
} catch (NoSuchMethodException e) {
return null;
}
}
}
这是 Main
和 运行 得到的结果。
import net.sf.cglib.proxy.Enhancer;
public class Main {
private static MyClass unwrapped;
private static WrapperClass wrapped;
private static MyClass proxified;
public static void main(String[] args) {
unwrapped = new MyClass();
System.out.println(">>> Methods from the unwrapped object:");
unwrapped.method1();
unwrapped.method2();
unwrapped.method3();
wrapped = new WrapperClass(unwrapped);
System.out.println(">>> Methods from the wrapped object:");
wrapped.method1();
wrapped.method2();
wrapped.method3();
proxified = createProxy(unwrapped);
System.out.println(">>> Methods from the proxy object:");
proxified.method1();
proxified.method2();
proxified.method3();
}
@SuppressWarnings("unchecked")
public static <T> T createProxy(T obj) {
Enhancer e = new Enhancer();
e.setSuperclass(obj.getClass());
e.setCallback(new MyInterceptor(obj));
T proxifiedObj = (T) e.create();
return proxifiedObj;
}
}
>>> Methods from the unwrapped object:
This is method 1 - MyClass@e26db62
This is method 2 - MyClass@e26db62
This is method 3 - MyClass@e26db62
>>> Methods from the wrapped object:
This is method 1 - WrapperClass@7b7035c6
This is overridden method 2 - MyClass@e26db62
This is method 3 - WrapperClass@7b7035c6
>>> Methods from the proxy object:
This is method 1 - MyClass@e26db62
This is overridden method 2 - MyClass@e26db62
This is method 3 - MyClass@e26db62
如您所见,当您 运行 wrapped
上的方法时,您将获得未被覆盖的方法的包装器(即 method1()
和 method3()
) .但是,当您 运行 proxified
上的方法时,所有方法都 运行 在包装对象上,而不必将它们全部委托给 WrapperClass
或将所有方法都放在接口中的方法签名。感谢@CoronA 和@Mark-Bramnik 为这个问题提供了一个看起来很酷的解决方案。
切换到 Groovy :-)
@CompileStatic
public class WrapperClass extends MyClass {
@Delegate private final MyClass delegate;
public WrapperClass(MyClass delegate) {
this.delagate = delegate;
}
//Done. That's it.
}
http://mrhaki.blogspot.com/2009/08/groovy-goodness-delegate-to-simplify.html
感谢 CoronA 指出代理和 InvocationHandler classes。我根据他的解决方案,使用泛型设计了一个更可重用的实用程序 class:
public class DelegationUtils {
public static <I> I wrap(Class<I> iface, I wrapped) {
return wrapInternally(iface, wrapped, new SimpleDecorator(wrapped));
}
private static <I> I wrapInternally (Class<I> iface, I wrapped, InvocationHandler handler) {
return (I) Proxy.newProxyInstance(wrapped.getClass().getClassLoader(), new Class[] { iface }, handler);
}
private static class SimpleDecorator<T> implements InvocationHandler {
private final T delegate;
private SimpleDecorator(T delegate) {
this.delegate = delegate;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Method m = findMethod(delegate.getClass(), method);
if (m == null) {
throw new NullPointerException("Found no method " + method + " in delegate: " + delegate);
}
return m.invoke(delegate, args);
}
}
private static Method findMethod(Class<?> clazz, Method method) throws Throwable {
try {
return clazz.getDeclaredMethod(method.getName(), method.getParameterTypes());
} catch (NoSuchMethodException e) {
return null;
}
}
}
测试一下:
public class Test {
public interface Test {
public void sayHello ();
}
public static class TestImpl implements Test {
@Override
public void sayHello() {
System.out.println("HELLO!");
}
}
public static void main(String[] args) {
Test proxy = DelegationUtils.wrap(Test.class, new TestImpl());
proxy.sayHello();
}
}
我想创建一个自动委托 class 来在 EDT 上执行委托人的方法。使用此 class,您只需创建一个将使用 EDTDecorator 的新实用程序方法,其中实现将 m.invoke
包装在 SwingUtilities.invokeLater
.
中
但是,如果我反思这一点,我可能想重新考虑为我拥有的每个接口制作一个基于非反射的代理——它可能更干净、更快,也更容易理解。但是,这是可能的。
检查来自 Lombok 框架的 @Delegate 注释:
https://projectlombok.org/features/Delegate.html
让我针对具体案例重新定义问题。
我想重写jdbc中ResultSet接口的close方法。我的目的是在结果集的close方法中关闭preparedstatement。我无法访问在 ResultSet 接口中实现的 Class (DelegatingResultSet)。 ResultSet 接口中有很多方法,一个一个地覆盖它们并从 ResultSet 对象中调用相应的方法是一种解决方案。对于动态解决方案,我使用了 Dynamic ProxyClasses (https://docs.oracle.com/javase/1.5.0/docs/guide/reflection/proxy.html).
// New ResultSet implementation
public class MyResultSet implements InvocationHandler {
ResultSet rs;
PreparedStatement ps;
private Method closeMethod;
public MyResultSet(ResultSet rs, PreparedStatement ps) {
super();
this.rs = rs;
this.ps = ps;
try {
closeMethod = ResultSet.class.getMethod("close",null);
} catch (NoSuchMethodException | SecurityException e) {
e.printStackTrace();
}
}
public void close() {
try {
rs.close();
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
public static Object newInstance(ResultSet rs, PreparedStatement ps) {
return java.lang.reflect.Proxy.newProxyInstance(rs.getClass().getClassLoader(), rs.getClass().getInterfaces(),
new MyResultSet(rs,ps));
}
public Object invoke(Object proxy, Method m, Object[] args)
throws Throwable {
Object result = null;
try {
Class declaringClass = m.getDeclaringClass();
if (m.getName().compareTo("close")==0) {
close();
} else {
result = m.invoke(rs, args);
}
} catch (InvocationTargetException e) {
throw e.getTargetException();
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
} finally {
}
return result;
}
}
// 调用方式:
ResultSet prs = (ResultSet) MyResultSet.newInstance(rs,ps);
我非常感谢@CoronA 的回答。我也查看了@Mark Cramer 的回答,但是,如果我没有遗漏任何东西,我认为总是至少有两个“代理”class 的实例在两个对象之间具有奇怪的关系。
这一点以及 cglib 现在已被弃用的事实促使我寻找基于 ByteBuddy 的新实现。
这是我想出的:
public class MyClass {
public String testMethod() {
return "11111";
}
public String testMethod2() {
return "aaaaa";
}
}
public class MyClassWithDelegate extends MyClass {
private static final Constructor<? extends MyClassWithDelegate> CONSTRUCTOR_WITH_DELEGATE;
static {
Constructor<? extends MyClassWithDelegate> temp = null;
try {
final var instrumentedMyClassWithDelegateType =
new ByteBuddy()
.subclass(MyClassWithDelegate.class)
.method(ElementMatchers.any())
.intercept(MethodDelegation.to(MethodInterceptor.class))
.make()
.load(MyClassWithDelegate.class.getClassLoader())
.getLoaded();
temp = instrumentedMyClassWithDelegateType.getConstructor(MyClass.class);
} catch (final Exception e) {
LOGGER.error("Cannot instrument class {}", MyClassWithDelegate.class, e);
}
CONSTRUCTOR_WITH_DELEGATE = temp;
}
public static MyClassWithDelegate getInstanceWithDelegate(final MyClass myClass) {
try {
return CONSTRUCTOR_WITH_DELEGATE.newInstance(myClass);
} catch (final Exception e) {
LOGGER.error("Cannot get instance of {}", MyClassWithDelegate.class, e);
throw new IllegalStateException();
}
}
private final boolean initialized;
private final MyClass delegate;
public MyClassWithDelegate(final MyClass delegate) {
super();
this.delegate = delegate;
this.initialized = true;
}
public String testMethod() {
return "22222";
}
public static class MethodInterceptor {
@RuntimeType
public static Object intercept(@This final MyClassWithDelegate self,
@Origin final Method method,
@AllArguments final Object[] args,
@SuperMethod final Method superMethod) throws Throwable {
if (!self.initialized || method.getDeclaringClass().equals(MyClassWithDelegate.class)) {
return superMethod.invoke(self, args);
} else {
return method.invoke(self.delegate, args);
}
}
}
}
initialized
字段用于防止 super
构造函数的方法调用在其赋值之前被重定向到委托(在这种情况下这不是问题,但我想创建通用解决方案)。
在 MyClassWithDelegate
的实例上调用的每个方法都将被重定向到委托,但在 MyClassWithDelegate
本身内部声明的方法除外。
在此示例中,在 MyClassWithDelegate
的实例上调用 testMethod()
将 return“22222”,而 testMethod2()
将 return“aaaaa”。
显然,只有在调用 getInstanceWithDelegate
工厂方法获得 MyClassWithDelegate
的每个实例时,委托才会真正起作用。
假设我有一个 class 有许多 public 方法:
public class MyClass {
public void method1() {}
public void method2() {}
(...)
public void methodN() {}
}
现在我想创建一个 wrapper class 它将所有方法委托给包装实例 (delegate) :
public class WrapperClass extends MyClass {
private final MyClass delegate;
public WrapperClass(MyClass delegate) {
this.delagate = delegate;
}
public void method1() { delegate.method1(); }
public void method2() { delegate.method2(); }
(...)
public void methodN() { delegate.methodN(); }
}
现在,如果 MyClass 有很多方法,我将需要重写它们中的每一个,它们或多或少与 "delegates" 相同的代码。我想知道是否可以做一些魔术来自动调用 Java 中的方法(因此包装器 class 需要说“嘿,如果你对我调用方法,请转到 委托对象并在其上调用此方法)。
顺便说一句:我不能使用继承,因为委托不在我的 control.I 之下,只是从其他地方获取它的实例(另一种情况是如果 MyClass 是最终的)。
注意:我不想IDE生成。我知道我可以在 IntelliJ/Eclipse 的帮助下做到这一点,但我很好奇这是否可以用代码完成。
有什么建议可以实现这样的目标吗? (注意:我可能可以用一些脚本语言来做到这一点,比如 php,在那里我可以使用 php 魔术函数来拦截调用)。
您不必这样做 -- 您的 Wrapper class 是原始 class 的子class,因此它继承了 all 的公开访问方法 -- 如果您不实现它们,将调用原始方法。
您不应该将 extends Myclass
与私有 MyClass
对象一起使用 - 这确实是多余的,而且我想不出这样做是正确的设计模式。你的 WrapperClass
是 一个 MyClass
,因此你可以只使用它自己的字段和方法而不是调用 delegate
.
编辑:在 MyClass
为 final
的情况下,您将绕过故意声明不允许通过 "faking" 继承进行子class;除了你,我想不出还有谁愿意这样做,你控制着 WrapperClass
;但是,由于您可以控制 WrapperClass
,所以不包装您不需要的所有东西实际上不仅仅是一种选择——这是正确的做法,因为您的对象 不是 a MyClass
,并且只应在您考虑过的情况下表现得像一个。
EDIT 您刚刚通过将 MyClass
superclass 删除到您的 WrapperClass
将您的问题更改为完全不同的意思;这有点糟糕,因为它使到目前为止给出的所有答案都无效。你应该打开另一个问题。
在 WrapperClass
中定义一个方法,即 delegate()
returns MyClass
或
您可以使用反射来做到这一点,但调用者必须将方法名称作为参数传递给公开的方法。并且关于方法 arguments/overloaded 方法等会有并发症
BTW: I can not use inheritance because the delegate is not under my control.I just get its instance from elsewhere (another case would be if MyClass was final)
您发布的代码有 public class WrapperClass extends MyClass
实际上,您当前的 WrapperClass
实现实际上是 MyClass
也许 java 的动态 Proxy
可以帮到你。它仅在您因此使用接口时才有效。在这种情况下,我将调用接口 MyInterface
并设置默认实现:
public class MyClass implements MyInterface {
@Override
public void method1() {
System.out.println("foo1");
}
@Override
public void method2() {
System.out.println("foo2");
}
@Override
public void methodN() {
System.out.println("fooN");
}
public static void main(String[] args) {
MyClass wrapped = new MyClass();
wrapped.method1();
wrapped.method2();
MyInterface wrapper = WrapperClass.wrap(wrapped);
wrapper.method1();
wrapper.method2();
}
}
包装器 class 实现如下所示:
public class WrapperClass extends MyClass implements MyInterface, InvocationHandler {
private final MyClass delegate;
public WrapperClass(MyClass delegate) {
this.delegate = delegate;
}
public static MyInterface wrap(MyClass wrapped) {
return (MyInterface) Proxy.newProxyInstance(MyClass.class.getClassLoader(), new Class[] { MyInterface.class }, new WrapperClass(wrapped));
}
//you may skip this definition, it is only for demonstration
public void method1() {
System.out.println("bar");
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Method m = findMethod(this.getClass(), method);
if (m != null) {
return m.invoke(this, args);
}
m = findMethod(delegate.getClass(), method);
if (m != null) {
return m.invoke(delegate, args);
}
return null;
}
private Method findMethod(Class<?> clazz, Method method) throws Throwable {
try {
return clazz.getDeclaredMethod(method.getName(), method.getParameterTypes());
} catch (NoSuchMethodException e) {
return null;
}
}
}
注意这个 class:
- 扩展
MyClass
,以继承默认实现(任何其他都可以) - 实施
Invocationhandler
,以允许代理进行反射 - 可选择实现
MyInterface
(以满足装饰器模式)
此解决方案允许您覆盖特殊方法,但委托所有其他方法。这甚至可以与 Wrapper class.
的子 classes 一起使用请注意,方法 findMethod
尚未捕获特殊情况。
这个问题已经有 6 个月了,@CoronA 的精彩回答已经满足并被@walkeros 接受,但我想我会在这里添加一些东西,因为我认为这可以进一步推进。
正如@CoronA 在对他的回答的评论中讨论的那样,不必在 WrapperClass
(即 public void methodN() { delegate.methodN(); }
)中创建和维护一长串 MyClass
方法,动态代理解决方案将其移至界面。问题是你仍然需要为接口中的 MyClass
方法创建和维护一长串签名,这可能更简单一些但并不能完全解决问题。如果您无法访问 MyClass
以了解所有方法,情况尤其如此。
根据Three approaches for decorating your code,
For longer classes, a programmer must choose the lesser of two evils: implement many wrapper methods and keep the type of decorated object or maintain a simple decorator implementation and sacrifice retaining the decorated object type.
所以这也许是装饰器模式的预期限制。
然而,@Mark-Bramnik 给出了 fascinating solution using CGLIB at Interposing on Java Class Methods (without interfaces)。我能够将它与@CoronaA 的解决方案结合起来,以创建一个包装器,该包装器可以覆盖各个方法,然后将其他所有内容传递给包装对象 而无需 需要接口。
这里是MyClass
.
public class MyClass {
public void method1() { System.out.println("This is method 1 - " + this); }
public void method2() { System.out.println("This is method 2 - " + this); }
public void method3() { System.out.println("This is method 3 - " + this); }
public void methodN() { System.out.println("This is method N - " + this); }
}
这里是 WrapperClass
,它只覆盖 method2()
。正如您将在下面看到的,非覆盖方法实际上并没有传递给委托,这可能是个问题。
public class WrapperClass extends MyClass {
private MyClass delagate;
public WrapperClass(MyClass delegate) { this.delagate = delegate; }
@Override
public void method2() {
System.out.println("This is overridden method 2 - " + delagate);
}
}
这里是 MyInterceptor
,它扩展了 MyClass
。如@Mark-Bramnik 所述,它使用使用 CGLIB 的代理解决方案。它还使用@CononA 的方法来确定是将方法发送到包装器(如果它被覆盖)还是被包装的对象(如果它没有被覆盖)。
import java.lang.reflect.Method;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class MyInterceptor extends MyClass implements MethodInterceptor {
private Object realObj;
public MyInterceptor(Object obj) { this.realObj = obj; }
@Override
public void method2() {
System.out.println("This is overridden method 2 - " + realObj);
}
@Override
public Object intercept(Object arg0, Method method, Object[] objects,
MethodProxy methodProxy) throws Throwable {
Method m = findMethod(this.getClass(), method);
if (m != null) { return m.invoke(this, objects); }
Object res = method.invoke(realObj, objects);
return res;
}
private Method findMethod(Class<?> clazz, Method method) throws Throwable {
try {
return clazz.getDeclaredMethod(method.getName(), method.getParameterTypes());
} catch (NoSuchMethodException e) {
return null;
}
}
}
这是 Main
和 运行 得到的结果。
import net.sf.cglib.proxy.Enhancer;
public class Main {
private static MyClass unwrapped;
private static WrapperClass wrapped;
private static MyClass proxified;
public static void main(String[] args) {
unwrapped = new MyClass();
System.out.println(">>> Methods from the unwrapped object:");
unwrapped.method1();
unwrapped.method2();
unwrapped.method3();
wrapped = new WrapperClass(unwrapped);
System.out.println(">>> Methods from the wrapped object:");
wrapped.method1();
wrapped.method2();
wrapped.method3();
proxified = createProxy(unwrapped);
System.out.println(">>> Methods from the proxy object:");
proxified.method1();
proxified.method2();
proxified.method3();
}
@SuppressWarnings("unchecked")
public static <T> T createProxy(T obj) {
Enhancer e = new Enhancer();
e.setSuperclass(obj.getClass());
e.setCallback(new MyInterceptor(obj));
T proxifiedObj = (T) e.create();
return proxifiedObj;
}
}
>>> Methods from the unwrapped object:
This is method 1 - MyClass@e26db62
This is method 2 - MyClass@e26db62
This is method 3 - MyClass@e26db62
>>> Methods from the wrapped object:
This is method 1 - WrapperClass@7b7035c6
This is overridden method 2 - MyClass@e26db62
This is method 3 - WrapperClass@7b7035c6
>>> Methods from the proxy object:
This is method 1 - MyClass@e26db62
This is overridden method 2 - MyClass@e26db62
This is method 3 - MyClass@e26db62
如您所见,当您 运行 wrapped
上的方法时,您将获得未被覆盖的方法的包装器(即 method1()
和 method3()
) .但是,当您 运行 proxified
上的方法时,所有方法都 运行 在包装对象上,而不必将它们全部委托给 WrapperClass
或将所有方法都放在接口中的方法签名。感谢@CoronA 和@Mark-Bramnik 为这个问题提供了一个看起来很酷的解决方案。
切换到 Groovy :-)
@CompileStatic
public class WrapperClass extends MyClass {
@Delegate private final MyClass delegate;
public WrapperClass(MyClass delegate) {
this.delagate = delegate;
}
//Done. That's it.
}
http://mrhaki.blogspot.com/2009/08/groovy-goodness-delegate-to-simplify.html
感谢 CoronA 指出代理和 InvocationHandler classes。我根据他的解决方案,使用泛型设计了一个更可重用的实用程序 class:
public class DelegationUtils {
public static <I> I wrap(Class<I> iface, I wrapped) {
return wrapInternally(iface, wrapped, new SimpleDecorator(wrapped));
}
private static <I> I wrapInternally (Class<I> iface, I wrapped, InvocationHandler handler) {
return (I) Proxy.newProxyInstance(wrapped.getClass().getClassLoader(), new Class[] { iface }, handler);
}
private static class SimpleDecorator<T> implements InvocationHandler {
private final T delegate;
private SimpleDecorator(T delegate) {
this.delegate = delegate;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Method m = findMethod(delegate.getClass(), method);
if (m == null) {
throw new NullPointerException("Found no method " + method + " in delegate: " + delegate);
}
return m.invoke(delegate, args);
}
}
private static Method findMethod(Class<?> clazz, Method method) throws Throwable {
try {
return clazz.getDeclaredMethod(method.getName(), method.getParameterTypes());
} catch (NoSuchMethodException e) {
return null;
}
}
}
测试一下:
public class Test {
public interface Test {
public void sayHello ();
}
public static class TestImpl implements Test {
@Override
public void sayHello() {
System.out.println("HELLO!");
}
}
public static void main(String[] args) {
Test proxy = DelegationUtils.wrap(Test.class, new TestImpl());
proxy.sayHello();
}
}
我想创建一个自动委托 class 来在 EDT 上执行委托人的方法。使用此 class,您只需创建一个将使用 EDTDecorator 的新实用程序方法,其中实现将 m.invoke
包装在 SwingUtilities.invokeLater
.
但是,如果我反思这一点,我可能想重新考虑为我拥有的每个接口制作一个基于非反射的代理——它可能更干净、更快,也更容易理解。但是,这是可能的。
检查来自 Lombok 框架的 @Delegate 注释: https://projectlombok.org/features/Delegate.html
让我针对具体案例重新定义问题。 我想重写jdbc中ResultSet接口的close方法。我的目的是在结果集的close方法中关闭preparedstatement。我无法访问在 ResultSet 接口中实现的 Class (DelegatingResultSet)。 ResultSet 接口中有很多方法,一个一个地覆盖它们并从 ResultSet 对象中调用相应的方法是一种解决方案。对于动态解决方案,我使用了 Dynamic ProxyClasses (https://docs.oracle.com/javase/1.5.0/docs/guide/reflection/proxy.html).
// New ResultSet implementation
public class MyResultSet implements InvocationHandler {
ResultSet rs;
PreparedStatement ps;
private Method closeMethod;
public MyResultSet(ResultSet rs, PreparedStatement ps) {
super();
this.rs = rs;
this.ps = ps;
try {
closeMethod = ResultSet.class.getMethod("close",null);
} catch (NoSuchMethodException | SecurityException e) {
e.printStackTrace();
}
}
public void close() {
try {
rs.close();
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
public static Object newInstance(ResultSet rs, PreparedStatement ps) {
return java.lang.reflect.Proxy.newProxyInstance(rs.getClass().getClassLoader(), rs.getClass().getInterfaces(),
new MyResultSet(rs,ps));
}
public Object invoke(Object proxy, Method m, Object[] args)
throws Throwable {
Object result = null;
try {
Class declaringClass = m.getDeclaringClass();
if (m.getName().compareTo("close")==0) {
close();
} else {
result = m.invoke(rs, args);
}
} catch (InvocationTargetException e) {
throw e.getTargetException();
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
} finally {
}
return result;
}
}
// 调用方式:
ResultSet prs = (ResultSet) MyResultSet.newInstance(rs,ps);
我非常感谢@CoronA 的回答。我也查看了@Mark Cramer 的回答,但是,如果我没有遗漏任何东西,我认为总是至少有两个“代理”class 的实例在两个对象之间具有奇怪的关系。
这一点以及 cglib 现在已被弃用的事实促使我寻找基于 ByteBuddy 的新实现。
这是我想出的:
public class MyClass {
public String testMethod() {
return "11111";
}
public String testMethod2() {
return "aaaaa";
}
}
public class MyClassWithDelegate extends MyClass {
private static final Constructor<? extends MyClassWithDelegate> CONSTRUCTOR_WITH_DELEGATE;
static {
Constructor<? extends MyClassWithDelegate> temp = null;
try {
final var instrumentedMyClassWithDelegateType =
new ByteBuddy()
.subclass(MyClassWithDelegate.class)
.method(ElementMatchers.any())
.intercept(MethodDelegation.to(MethodInterceptor.class))
.make()
.load(MyClassWithDelegate.class.getClassLoader())
.getLoaded();
temp = instrumentedMyClassWithDelegateType.getConstructor(MyClass.class);
} catch (final Exception e) {
LOGGER.error("Cannot instrument class {}", MyClassWithDelegate.class, e);
}
CONSTRUCTOR_WITH_DELEGATE = temp;
}
public static MyClassWithDelegate getInstanceWithDelegate(final MyClass myClass) {
try {
return CONSTRUCTOR_WITH_DELEGATE.newInstance(myClass);
} catch (final Exception e) {
LOGGER.error("Cannot get instance of {}", MyClassWithDelegate.class, e);
throw new IllegalStateException();
}
}
private final boolean initialized;
private final MyClass delegate;
public MyClassWithDelegate(final MyClass delegate) {
super();
this.delegate = delegate;
this.initialized = true;
}
public String testMethod() {
return "22222";
}
public static class MethodInterceptor {
@RuntimeType
public static Object intercept(@This final MyClassWithDelegate self,
@Origin final Method method,
@AllArguments final Object[] args,
@SuperMethod final Method superMethod) throws Throwable {
if (!self.initialized || method.getDeclaringClass().equals(MyClassWithDelegate.class)) {
return superMethod.invoke(self, args);
} else {
return method.invoke(self.delegate, args);
}
}
}
}
initialized
字段用于防止 super
构造函数的方法调用在其赋值之前被重定向到委托(在这种情况下这不是问题,但我想创建通用解决方案)。
在 MyClassWithDelegate
的实例上调用的每个方法都将被重定向到委托,但在 MyClassWithDelegate
本身内部声明的方法除外。
在此示例中,在 MyClassWithDelegate
的实例上调用 testMethod()
将 return“22222”,而 testMethod2()
将 return“aaaaa”。
显然,只有在调用 getInstanceWithDelegate
工厂方法获得 MyClassWithDelegate
的每个实例时,委托才会真正起作用。