java 自动委托一个java类的所有方法
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/30344715/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Automatically delegating all methods of a java class
提问by walkeros
Say I have a class with many of public methods:
假设我有一个包含许多公共方法的类:
public class MyClass {
public void method1() {}
public void method2() {}
(...)
public void methodN() {}
}
Now I would like to create a wrapperclass which would delegate all the methods to wrapped instance (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(); }
}
Now if MyClass has a lot of methods I would need to override each of them which is more or less the same code which just "delegates". I was wondering if it is possible to do some magic to automatically call a method in Java (so the Wrapper class would need to say "Hey if you call a method on me just go to delegateobject and call this method on it).
现在,如果 MyClass 有很多方法,我需要覆盖它们中的每一个,这些方法或多或少都是“委托”的相同代码。我想知道是否有可能做一些魔术来自动调用 Java 中的方法(所以 Wrapper 类需要说“嘿,如果你在我身上调用一个方法,只需去委托对象并在它上面调用这个方法)。
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).
顺便说一句:我不能使用继承,因为委托不在我的控制之下。我只是从其他地方获取它的实例(另一种情况是如果 MyClass 是最终的)。
NOTE: I do not want IDE generation. I know I can do it with help of IntelliJ/Eclipse, but I'm curious if this can be done in code.
注意:我不想要 IDE 生成。我知道我可以在 IntelliJ/Eclipse 的帮助下做到这一点,但我很好奇这是否可以在代码中完成。
Any suggestions how to achieve something like this? (NOTE: I would probably be able to do it in some scripting languages like php where I could use php magic functions to intercept the call).
任何建议如何实现这样的目标?(注意:我可能可以用一些脚本语言来完成,比如 php,我可以使用 php 魔术函数来拦截调用)。
回答by CoronA
Perhaps the dynamic Proxy
of java can help you. It only works if you consequently use interfaces. In this case, I will call the interface MyInterface
and set up a default implementation:
也许Proxy
java的动态可以帮助你。它仅在您因此使用接口时才有效。在这种情况下,我将调用该接口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();
}
}
The wrapper class implementation would look like:
包装类实现看起来像:
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;
}
}
}
Note that this class:
注意这个类:
- extends
MyClass
, to inherit a default implementation (any other would do) - implements
Invocationhandler
, to allow the proxy to do reflection - optionally implement
MyInterface
(to satisfy the decorator pattern)
- extends
MyClass
,继承默认实现(任何其他都可以) - 实现
Invocationhandler
,允许代理进行反射 - 可选地实现
MyInterface
(以满足装饰器模式)
This solution allows you to override special methods, but to delegate all others. This will even work with sub classes of Wrapper class.
此解决方案允许您覆盖特殊方法,但委托所有其他方法。这甚至适用于 Wrapper 类的子类。
Note that the method findMethod
does not yet capture the special cases.
请注意,该方法findMethod
尚未捕获特殊情况。
回答by Mark Cramer
This question is 6 months old already and @CoronA's wonderful answer has satisfied and been accepted by @walkeros, but I thought I would add something here as I think this can be pushed an extra step.
这个问题已经有 6 个月大了,@CoronA 的精彩回答已经得到满意并被 @walkeros 接受,但我想我会在这里添加一些东西,因为我认为这可以再推进一步。
As discussed with @CoronA in the comments to his answer, instead of having to create and maintain a long list of MyClass
methods in WrapperClass
(i.e. public void methodN() { delegate.methodN(); }
), the dynamic proxy solution moves this to the interface. The issue is that you still have to create and maintain a long list of signatures for the MyClass
methods in the interface, which is perhaps a bit simpler but doesn't completely solve the problem. This is especially the case if you don't have access to MyClass
in order to know all the methods.
正如@CoronA 在对他的回答的评论中所讨论的那样,动态代理解决方案不必MyClass
在WrapperClass
(ie public void methodN() { delegate.methodN(); }
) 中创建和维护一长串方法,而是将其移至接口。问题是您仍然必须为MyClass
接口中的方法创建和维护一长串签名,这可能更简单一些,但并不能完全解决问题。如果您无法访问MyClass
以了解所有方法,则尤其如此。
According to 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.
对于较长的类,程序员必须选择两害相权取其轻:实现许多包装方法并保留装饰对象的类型,或者维护简单的装饰器实现并牺牲保留装饰对象类型。
So perhaps this is an expected limitation of the Decorator Pattern.
所以也许这是装饰者模式的预期限制。
@Mark-Bramnik, however, gives an fascinating solution using CGLIBat Interposing on Java Class Methods (without interfaces). I was able to combine this with @CoronaA's solution in order to create a wrapper that can override individual methods but then pass everything else to the wrapped object withoutrequiring an interface.
但是,@Mark-Bramnik在Interposing on Java Class Methods (without interfaces) 中使用 CGLIB给出了一个引人入胜的解决方案。我能够将它与@CoronaA 的解决方案结合起来,以创建一个包装器,该包装器可以覆盖单个方法,然后将其他所有内容传递给被包装的对象,而无需接口。
Here is MyClass
.
这是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); }
}
Here is WrapperClass
which only overrides method2()
. As you'll see below, the non-overridden methods are, in fact, not passed to the delegate, which can be a problem.
这是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);
}
}
Here is MyInterceptor
which extends MyClass
. It employs the proxy solution using CGLIB as described by @Mark-Bramnik. It also employs @CononA's method of determining whether or not to send the method to the wrapper (if it is overridden) or the wrapped object (if it is not).
这是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;
}
}
}
Here is Main
and the results you get if you run it.
这是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
As you can see, when you run the methods on wrapped
you get the wrapper for the methods that are not overridden (i.e. method1()
and method3()
). When you run the methods on proxified
, however, all of the methods are run on the wrapped object without the pain of having to delegate them all in WrapperClass
or put all of the method signatures in an interface. Thanks to @CoronA and @Mark-Bramnik for what seems like a pretty cool solution to this problem.
如您所见,当您运行这些方法时,wrapped
您将获得未被覆盖的方法(即method1()
和method3()
)的包装器。proxified
但是,当您在 上运行方法时,所有方法都在包装的对象上运行,而无需将它们全部委托WrapperClass
或将所有方法签名放入接口中。感谢@CoronA 和@Mark-Bramnik 为这个问题提供了一个看起来很酷的解决方案。
回答by Snekse
Switch to Groovy :-)
切换到 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
http://mrhaki.blogspot.com/2009/08/groovy-goodness-delegate-to-simplify.html
回答by Vanessa Schissato
Check the @Delegate annotation from Lombok framework: https://projectlombok.org/features/Delegate.html
检查来自 Lombok 框架的 @Delegate 注释:https: //projectlombok.org/features/Delegate.html
回答by Marcus Müller
You don't have to do this -- your Wrapper class is a subclass of the original class, so it inherits allof its publicly accessible methods -- and if you don't implement them, the original method will be called.
您不必这样做——您的 Wrapper 类是原始类的子类,因此它继承了所有可公开访问的方法——如果您不实现它们,则将调用原始方法。
You shouldn't have extends Myclass
together with a private MyClass
object -- that's really really redundant, and I can't think of a design pattern where doing that is right. Your WrapperClass
isa MyClass
, and hence you can just use its own fields and methods instead of calling delegate
.
你不应该extends Myclass
和一个私有MyClass
对象在一起——那真的是多余的,我想不出一个设计模式在哪里这样做是正确的。你WrapperClass
是一个MyClass
,因此你可以只使用它自己的字段和方法而不是调用delegate
.
EDIT: In the case of MyClass
being final
, you'd be circumventing the willfull declaration to not allow subclassing by "faking" inheritance; I can't think of anyone willing to do that other than you, who is in control of WrapperClass
; but, since you're in control of WrapperClass
, not wrapping everything you don't need is really more than an option -- it's the right thing to do, because your object is nota MyClass
, and should only behave like one in the cases you mentally considered.
编辑:在的情况下MyClass
是final
,你会绕过willfull声明不允许通过“伪造”遗产继承; 除了你,我想不出还有谁愿意这样做,谁控制着WrapperClass
;但是,因为你的掌握之中WrapperClass
,未包装的一切你不需要真的是多一个选择-这是应该做的事情,因为你的目标是不是一个MyClass
问题,仅表现得像一个的情况下,你心理上考虑。
EDITyou've just changed your question to mean something completely different by removing the MyClass
superclass to your WrapperClass
; that's a bit bad, because it invalidates all answers given so far. You should have opened another question.
编辑你刚刚改变了你的问题,通过将超MyClass
类删除到你的WrapperClass
; 这有点糟糕,因为它使迄今为止给出的所有答案无效。你应该打开另一个问题。
回答by Nitin Dandriyal
Define a method in WrapperClass
i.e. delegate()
that returns the instance of MyClass
在WrapperClass
iedelegate()
中定义一个方法,该方法返回的实例MyClass
OR
或者
You can use reflection to do that but the caller has to pass the method name as an argument to an exposed method. And there will be complications regarding the method arguments/overloaded methods etc.
您可以使用反射来做到这一点,但调用者必须将方法名称作为参数传递给公开的方法。并且会出现有关方法参数/重载方法等的并发症。
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)
顺便说一句:我不能使用继承,因为委托不在我的控制之下。我只是从其他地方获取它的实例(另一种情况是如果 MyClass 是最终的)
The code that you have posted has public class WrapperClass extends MyClass
您发布的代码有 public class WrapperClass extends MyClass
Actually your current implementation of WrapperClass
is actually a decorator on top of MyClass
实际上,您当前的实现WrapperClass
实际上是 MyClass 之上的装饰器
回答by erde
Let me redefine the problem for a specific case. I want to override the close method of ResultSet interface in jdbc. My aim is to close the preparedstatement in close method of result set. I could not access to the Class (DelegatingResultSet) that implements in ResultSet interface. There are a lot of methods in ResultSet interface and overriding them one by one and calling the corresponding method from the ResultSet object is one solution. For a dynamic solution I used Dynamic ProxyClasses (https://docs.oracle.com/javase/1.5.0/docs/guide/reflection/proxy.html).
让我重新定义一个特定案例的问题。我想覆盖jdbc中ResultSet接口的close方法。我的目标是在结果集的关闭方法中关闭准备好的语句。我无法访问在 ResultSet 接口中实现的类 (DelegatingResultSet)。ResultSet 接口中有很多方法,将它们一一覆盖并从 ResultSet 对象调用相应的方法是一种解决方案。对于动态解决方案,我使用了动态代理类(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;
}
}
// How to call it:
// 如何调用它:
ResultSet prs = (ResultSet) MyResultSet.newInstance(rs,ps);
回答by Timmos
Credits go to CoronA for Pointing out the Proxy and InvocationHandler classes. I worked out a more reusable utility class based on his solution, using generics:
感谢 CoronA 指出 Proxy 和 InvocationHandler 类。我使用泛型根据他的解决方案制定了一个更可重用的实用程序类:
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;
}
}
}
Test it:
测试一下:
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();
}
}
I wanted to create an automatic delegation class that executes the delegatee's methods on the EDT. With this class, you just create a new utility method that will use an EDTDecorator, in which the implementation will wrap m.invoke
in a SwingUtilities.invokeLater
.
我想创建一个自动委托类,在 EDT 上执行被委托人的方法。使用此类,您只需创建一个新的实用程序方法,该方法将使用 EDTDecorator,其中实现将包装m.invoke
在SwingUtilities.invokeLater
.
However, if I reflect on this, I may want to reconsider making a non-Reflection based proxy per interface that I have - it might be cleaner and faster, and more understandable. But, it's possible.
但是,如果我对此进行反思,我可能想重新考虑为我拥有的每个接口制作一个非基于反射的代理——它可能更干净、更快,也更容易理解。但是,这是可能的。