Java中如何动态改变对象行为,动态代理没用

How to change object behavior dynamically in Java, dynamic proxy is useless

这里是解释问题的代码片段:

public class Demo {
    // I want to enhance this method.
    public String a() {
        return "A";
    }

    // this method is not enhanced but the internal a() invocation should be
    public String b() {
        return "-> " + a();
    }
}

我想增强方法a(),当调用方法b()时,应该执行增强的a()。我想 CGLIB 代理不需要任何努力。

另外,这个增强动作应该是对象级的,而不是class级的。

public static void verify() {
    assert Objects.equals("-> A", new Demo().b());
    assert !Objects.equals("-> A", enhance(new Demo()).b());
}

public static Demo enhance(Demo demo) {
    throw new UnsupportedOperationException("I don't know how to do");
}

有没有办法解决这个问题?

顺便说一句:抱歉我的英语不好,但我认为我的意图描述得很清楚。

补充:

其实我的问题是:

将记录异常堆栈跟踪,但其中一些使用 new XXException("sensitive") 在其 detailMessage 字段中包含敏感数据,或者一些 IOException 将打印详细的绝对路径.

printStackTrace()会调用toString()方法,toString()方法会调用getMessage()方法。 (可以看JDK源码)

我想为 wrap/enhance 和 Exception 提供实用方法,让 printStackTrace() 不打印敏感数据,而是打印堆栈跟踪以查找错误。 然后这些Exception对象就可以安全的发送到log4j了。

我不明白子类化有什么问题。

class EnhancedDemo extends Demo{
    @Override
    public String a(){
        return "Enhanced A";
    }
}

您仍然可以使用它来代替普通的演示:

public static void main(String... args) throws Exception {
    Demo myDemo = new EnhancedDemo();
    System.out.println(myDemo.b());
    //output: -> Enhanced A
}

这还不够吗?您不能在您认为需要此代码的地方重构代码以允许使用 dependency injection 吗?

正如您提到的 CGLIB 代理(和 java 动态代理)都有同样的问题。当从代理方法调用另一个方法时(在您的情况下,当 b() 调用 a() 时),将使用引用 'self',代理永远不会出现。

作为解决方法,您可以考虑将代理对象本身传递给 class,如 SO Answer 中所示。该线程会问一个与您类似的问题,其中 'enhancement' 是 Spring 的事务处理代理。 之后,您可以在任何地方使用对 self 的引用,而不是直接调用。

你也可以考虑,重构你的代码,这样就不会出现这种情况了。但是,这可能会或可能不会,具体取决于您的用例。

至于后一个问题 enhancement action should be object-level rather than class-level java 动态代理在对象级别上工作。因此,如果您创建 class 的新对象并且不代理它,它将保持 'enchancement-free'.

您似乎没有正确处理问题。 问题似乎 "how do I change junk dynamically" 不如 "how do I correctly design my code to allow for dynamic change of a method's implementation" 严重。答案是:为方法一个功能实现一个策略模式。

这是一个例子:

public interface MethodAStrategy
{
    String methodAImplementation();
}

public class SimpleAStrategy
implements MethodAStrategy
{
    public String methodAImplementation()
    {
        return "a";
    }
}

public class EnhancedAStrategy
implements MethodAStrategy
{
    public String methodAImplementation()
    {
        return "enhanced a";
    }
}

public class EnhancedDemo
extends Demo
{
    private MethodAStrategy strategyForMethodA;
    private MethodAStrategy strategyForMethodB;

    public EnhancedDemo()
    {
        strategyForMethodA = new SimpleAStrategy();
        strategyForMethodB = new EnhancedAStrategy()
    }

    public String a()
    {
        return strategyForMethodA.methodAImplementation();
    }

    public String b()
    {
        return "->" + strategyForMethodB.methodAImplementation();
    }

    // as desired, add methods to change the strategies.
}

由于您的真正问题是阻止某些数据被记录到文件中,最简单的方法是在记录点拦截它。扩展 Log4J 的 FileAppender 并编辑那里的绝对路径应该很容易。然后,您只需要确保使用自定义 appender 而不是常规 appender,但这比您想象的要容易。