在任何其他方法被调用时调用一个方法。

14

是否有一种方法可以创建一种“超级方法”,使得每次调用方法时都会调用它,即使是未定义的方法也是如此?类似于这样:

public void onStart() {
    System.out.println("Start");
}

public void onEnd() {
    System.out.println("End");
}

public SuperMethod superMethod() {
    System.out.println("Super");
}

// "Start"
// "Super"
onStart();

// "End"
// "Super"
onEnd();

// "Super"
onRun();

编辑-具体内容:我有一个库,它会经常更新并每次都重新混淆。为了使我的工作流程更容易,我正在让我的程序自动更新库(必须这样做才能实现我想要的功能,我不会过于详细地解释为什么,但是我的程序将与未来的更新兼容),并且我已经在库中下载了混淆映射。我想创建一个名为Library的代理,然后当我调用Library.getInstance()时,它会获取getInstance()的混淆映射,并调用当前时刻映射为getInstance()abz的库方法getInstance()


1
你为什么想要这样做? - user1983983
1
请注意,在Java中使用“super”这个词会让人感到困惑,因为它已经有了其他的含义。 - keyser
9
你需要熟悉“Aspects”。 - Konstantin Yovkov
1
什么是 Proxy - tom
我已经添加了关于我需要这个的具体信息 @user1983983 - Jordan Doyle
显示剩余3条评论
5个回答

9

当然可以做到这一点,但需要使用AspectJ而不是标准的Java。

这里有一个简单的例子:

使用后置通知的切面:

package net.fsa.aspectj.test;


public aspect SuperMethdAspect {

    pointcut afterPointCut() : execution(public * com.my.pack.age.MyClass.*(..));

    after() : afterPointCut() {
        System.out.println("Super");
    }
}

您的目标类
package com.my.pack.age;

public class MyClass {

    public void onStart() {
        System.out.println("Start");
    }

    public void onEnd() {
        System.out.println("End");
    }
}

最后是一些测试应用程序。
package net.fsa.aspectj.test;

import com.my.pack.age.MyClass;

public class MyApp {

    public static void main(String... args) {
        MyClass myClass = new MyClass();
        myClass.onStart();
        myClass.onEnd();
    }
}

输出

Start
Super
End
Super

谢谢。有没有办法使 com.my.pack.age.MyClass 动态化?该库的映射一直在变化。 - Jordan Doyle
@JordanDoyle pointcut表达式允许进行多种组合。如果您不知道要拦截的包/类集,则可能需要一个以上的切面,例如每个库一个切面,并将实际功能放置在其他类中。如果要针对某个包中的所有类进行操作,则可以使用com.my.pack.age.*.* - A4L
@JordanDoyle 如果您不能编译目标类和切面,那么您绝对需要考虑加载时织入 - A4L

9

以下是使用Java代理类 Proxy 实现的纯Java代码:

import java.lang.reflect.*;
import java.util.*;

public class Demo
{
    public static void main(String[] args)
    {
        Map<String, String> map = new HashMap<String, String>();
        map.put("onStart", "abc");
        map.put("onEnd", "def");
        Library library = new LibraryProxy(map, new LibraryImpl()).proxy();
        library.onStart();
        library.onEnd();
        library.onRun();
    }
}

interface Library
{
    void onStart();
    void onEnd();
    void onRun();
}

class LibraryImpl
{
    public void abc() { System.out.println("Start"); }
    public void def() { System.out.println("End"); }
}

class LibraryProxy implements InvocationHandler
{
    Map<String, String> map;
    Object impl;

    public LibraryProxy(Map<String, String> map, Object impl)
    {
        this.map = map;
        this.impl = impl;
    }

    public Library proxy()
    {
        return (Library) Proxy.newProxyInstance(Library.class.getClassLoader(),
            new Class[] { Library.class }, this);
    }

    @Override
    public Object invoke(Object proxy, Method m, Object[] args) throws Throwable
    {
        Object res = null;
        String name = map.get(m.getName());
        if (name == null) {
            System.out.println("[" + m.getName() + " is not defined]");
        } else {
            m = impl.getClass().getMethod(name, m.getParameterTypes());
            res = m.invoke(impl, args);
        }
        System.out.println("super duper");
        return res;
    }
}

输出:

Start
super duper
End
super duper
[onRun is not defined]
super duper

那绝对是完美的,正是我所需要的。谢谢 =D - Jordan Doyle
有没有办法代理字段 @tom?谢谢。 - Jordan Doyle
1
@JordanDoyle并没有直接的方法,但是你可以在接口中添加getter/setter方法,并让代理将它们翻译为字段访问。 - tom
如果Library是一个带有参数化构造函数的类,会怎样呢? - Venkatesh
@Venkatesh:构造函数没有名称,因此它们不会被混淆。您可以正常创建混淆类的实例,然后将其传递给代理。使用我的示例,如果LibraryImpl有一个构造函数,例如LibraryImpl(String s, int i),则应使用new LibraryProxy(map, new LibraryImpl("x", 1)).proxy() - tom
@Venkatesh:这篇帖子已经超过三年了。如果您需要更多帮助,请提出一个单独的问题。 - tom

4

Java并不能像魔法一样实现这个功能。为了进行调用,必须在代码中明确添加。因此,答案是否定的,除非你显式地添加相关方法的调用。然而,你可以通过使用预处理器或运行时代码生成来隐藏它。

我认为AspectJ可能是你想要的。


0

我猜这不完全是你想要的,但你可以将所有代码包装在方法中,使用 try {}finally {supermethod ()}

这样可以确保 supermethod 被调用。


0
这个概念背后的术语被称为拦截器。你需要一个容器来实现,比如应用服务器、CDI容器、Spring或AOP。它的工作原理是这样的。
1. call your Method
2. pause your Method
3. call intercepter
4. do interceptor stuff
5. resume your Method inside the interceptor

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接