如何获取调用静态方法的类?

huangapple go评论81阅读模式
英文:

How to get class which invokes static method?

问题

以下是您的代码的翻译部分:

这是我的代码:

class A {
    
    public static void doIt() {
        //Class currentClass = new Object() { }.getClass().getEnclosingClass();
        //Class currentClass = MethodHandles.lookup().lookupClass();
        String currentClass = Thread.currentThread().getStackTrace()[1].getClassName();
        System.out.println("调用类:" + currentClass);
    }
}

class B extends A { }

public class NewMain {

    public static void main(String[] args) {
        A.doIt();
        B.doIt();
    }
}

如您所见,doIt 方法可以由 AB 类调用。在 doIt 方法中,我想知道是哪个类用于调用该方法(A 还是 B)。这是可能的吗?我尝试过的三种解决方案都没有起作用 - 它总是显示 A 类。

英文:

This is my code:

class A {
    
    public static void doIt() {
        //Class currentClass = new Object() { }.getClass().getEnclosingClass();
        //Class currentClass = MethodHandles.lookup().lookupClass();
        String currentClass = Thread.currentThread().getStackTrace()[1].getClassName();
        System.out.println("CALLING CLASS:" + currentClass);
    }
}

class B extends A { }

public class NewMain {

    public static void main(String[] args) {
        A.doIt();
        B.doIt();
    }
}

As you see doIt method can be called by A and B classes. In doIt I want to know what class was used to call method (A or B). Is it possible? Three solutions I tried didn't work - it always says A class.

答案1

得分: 2

起初我认为这是不可能的因为 Java 编译器可以找出将调用哪个方法并生成相同的指令

事实证明它实际上记录了调用类的方式

所以现在的问题是

* 我们如何获得调用方法的位置
* 我们如何使用这些信息来获取方法被调用的方式

第一个问题很容易解决我们使用 StackWalker它可以给我们提供[字节码索引](https://download.java.net/java/GA/jdk14/docs/api/java.base/java/lang/StackWalker.StackFrame.html#getByteCodeIndex%28%29)。

现在我们只需要解析类查看该字节码索引处的指令然后找出如何调用此方法

我使用了 ASM 来实现但在这里可能不是最合适的工具

```java
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.lang.StackWalker.StackFrame;
import java.util.Set;

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;

import static org.objectweb.asm.Opcodes.*;

class CallingClassVisitor extends ClassVisitor {
    
    private final StackFrame where;
    String ownerClass = null;

    public CallingClassVisitor(StackFrame where) {
        // 我们需要一个 ClassWriter 作为支持,以便解析 Label。
        super(ASM8, new ClassWriter(0));
        this.where = where;
    }
    
    @Override
    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature,
            String[] exceptions) {
        MethodVisitor parent = super.visitMethod(access, name, descriptor, signature, exceptions);
        if (name.equals(where.getMethodName()) && descriptor.equals(where.getDescriptor())) {
            return new CallingMethodVisitor(where, parent);
        } else {
            return parent;
        }
    }
    
    class CallingMethodVisitor extends MethodVisitor {

        private final StackFrame where;
        public CallingMethodVisitor(StackFrame where, MethodVisitor parent) {
            super(ASM8, parent);
            this.where = where;
        }
        
        @Override
        public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) {
            Label lbl = new Label();
            visitLabel(lbl);
            if (lbl.getOffset() == where.getByteCodeIndex()) {
                ownerClass = owner; 
            }
            super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
        }
    }
    
    public String getOwnerClass() {
        return ownerClass;
    }
}



class A {

    static final StackWalker SW = StackWalker.getInstance(Set.of(StackWalker.Option.RETAIN_CLASS_REFERENCE));
    
    public static void doIt() {
        StackFrame sf = SW.walk(s -> s.skip(1).findFirst()).orElseThrow();
        InputStream source = sf.getDeclaringClass().getClassLoader()
                .getResourceAsStream(sf.getClassName().replace('.', '/') + ".class");
        try {
            CallingClassVisitor ccv = new CallingClassVisitor(sf);
            new ClassReader(source).accept(ccv, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
            String how = ccv.getOwnerClass();
            System.out.println(how);
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }
}

class B extends A { }

public class NewMain {

    public static void main(String[] args) {
        A.doIt();
        B.doIt();
    }
}

最终,我不确定您的要求是否值得这样的努力。


<details>
<summary>英文:</summary>
At first, I thought this is impossible as the java compiler can figure out which method will be called and emit the same instruction.
Turns out that it actually records the way the class is called.
So, the question now becomes:
* How do we get the place where the method is called?
* How do we use this information to get the way the method is called?
The first one is easy: We use a StackWalker, which can give us the [bytecode index](https://download.java.net/java/GA/jdk14/docs/api/java.base/java/lang/StackWalker.StackFrame.html#getByteCodeIndex%28%29).
Now we only need to parse the class, look at the instruction at that bytecode index, and figure out how this method was called.
I used ASM for that, but it might be the wrong tool here.
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.lang.StackWalker.StackFrame;
import java.util.Set;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import static org.objectweb.asm.Opcodes.*;
class CallingClassVisitor extends ClassVisitor {
private final StackFrame where;
String ownerClass = null;
public CallingClassVisitor(StackFrame where) {
// We need a backing ClassWriter, so the Label is resolved.
super(ASM8, new ClassWriter(0));
this.where = where;
}
@Override
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature,
String[] exceptions) {
MethodVisitor parent = super.visitMethod(access, name, descriptor, signature, exceptions);
if (name.equals(where.getMethodName()) &amp;&amp; descriptor.equals(where.getDescriptor())) {
return new CallingMethodVisitor(where, parent);
} else {
return parent;
}
}
class CallingMethodVisitor extends MethodVisitor {
private final StackFrame where;
public CallingMethodVisitor(StackFrame where, MethodVisitor parent) {
super(ASM8, parent);
this.where = where;
}
@Override
public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) {
Label lbl = new Label();
visitLabel(lbl);
if (lbl.getOffset() == where.getByteCodeIndex()) {
ownerClass = owner; 
}
super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
}
}
public String getOwnerClass() {
return ownerClass;
}
}
class A {
static final StackWalker SW = StackWalker.getInstance(Set.of(StackWalker.Option.RETAIN_CLASS_REFERENCE));
public static void doIt() {
StackFrame sf = SW.walk(s -&gt; s.skip(1).findFirst()).orElseThrow();
InputStream source = sf.getDeclaringClass().getClassLoader()
.getResourceAsStream(sf.getClassName().replace(&#39;.&#39;, &#39;/&#39;) + &quot;.class&quot;);
try {
CallingClassVisitor ccv = new CallingClassVisitor(sf);
new ClassReader(source).accept(ccv, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
String how = ccv.getOwnerClass();
System.out.println(how);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
}
class B extends A { }
public class NewMain {
public static void main(String[] args) {
A.doIt();
B.doIt();
}
}
In the end, I&#39;m not sure if your requirement is worth that effort.
</details>
# 答案2
**得分**: 0
以下是翻译好的部分:
```java
如何看待这个?我知道它并不完全符合您的要求,但正如其他人已经指出的那样...... 您可能需要重新考虑设计方法。
class A {
protected static Class childClass; // 或者选择 private
// protected static Class getChildClass { return childClass; }
public static void setChildClass(Class classReference) {
childClass = classReference;
}
public static void doIt() {
System.out.println("CLASS:" + childClass.getSimpleName());
}
}
class B extends A { 
// doIt() 将被继承
// 带参数的方法重载
public static void doIt(String message) {
System.out.println("CLASS:" + B.class.getSimpleName() + ", message: " + message);
}
}
public class NewMain {
public static void main(String[] args) {
A.setChildClass(B.class);
A.doIt(); // CLASS:B
B.doIt(); // CLASS:B
B.doIt("Hello") // CLASS:B, message: Hello
}
}
**编辑**
您可以拥有一个简单的控制器和一些委托来实现类似的功能。虽然不全是静态的,但您对各个部分有更多的控制。请注意,这个示例故意非常基本。
您还可以拥有类 A 和类 B,其中 B 将 A 作为依赖,并且您只需重写 A(委托)应有的不同操作。在这里,也不是静态的,但我认为这并不是您的主要问题。毕竟问题更多地与设计有关。
import java.util.*;
public class MyClass {
public static void main(String... args) {
final DoItController doItController = DoItController.Singleton.getInstance();
doItController.doIt(DoerDelegateA.class);
doItController.doIt(DoerDelegateB.class);
doItController.doIt(DoerDelegateComplex.class);
}
}
interface Doer {
void doIt();
}
class DoItController {
final Map<Class<? extends DoerDelegate>, DoerDelegate> doerDelegateMap;
private DoItController() {
doerDelegateMap = new LinkedHashMap();
doerDelegateMap.put(DoerDelegateA.class, new DoerDelegateA());
doerDelegateMap.put(DoerDelegateB.class, new DoerDelegateB());
doerDelegateMap.put(DoerDelegateComplex.class, new DoerDelegateComplex());
}
public void doIt(Class<? extends DoerDelegate> delegateReference) {
final DoerDelegate delegate = doerDelegateMap.getOrDefault(delegateReference, null);
if (delegate != null) {
delegate.doIt();
}
}
public static class Singleton {
private static DoItController instance;
public static DoItController getInstance() {
if (instance == null) {
instance = new DoItController();
}
return instance;
}
}
}
class DoerDelegate implements Doer {
@Override
public void doIt() {
System.out.println("使用:" + getClass().getSimpleName());
}
}
class DoerDelegateA extends DoerDelegate {}
class DoerDelegateB extends DoerDelegate {}
class DoerDelegateComplex extends DoerDelegateA {
@Override
public void doIt() {
System.out.println("使用重写的标准方法:" + getClass().getSimpleName());
}
}
或者...
public class MyClass {
public static void main(String... args) {
final B b = new B();
b.doIt();
}
}
interface Doer {
void doIt();
}
class A implements Doer {
@Override
public void doIt() { System.out.println("执行它"); }
}
class B implements Doer {
final A aDelegate;
public B() {
aDelegate = new A();
}
@Override
public void doIt() {
// 标准操作
aDelegate.doIt();
// 您特定的 B 操作
//...
}
}

如果还有其他需要翻译的部分,请告诉我。

英文:

How about this one here? I know its not exactly what you want but as others already stated ... you might want to rethink your design approach.

class A {
protected static Class childClass; // alt. private
// protected static Class getChildClass { return childClass; }
public static void setChildClass(Class classReference) {
childClass = classReference;
}
public static void doIt() {
System.out.println(&quot;CLASS:&quot; + childClass.getSimpleName());
}
}
class B extends A { 
// doIt() will be inherited
// overloading method with a param
public static void doIt(String message) {
System.out.println(&quot;CLASS:&quot; + B.class.getSimpleName() + &quot;, message: &quot; + message);
}
}
public class NewMain {
public static void main(String[] args) {
A.setChildClass(B.class);
A.doIt(); // CLASS:B
B.doIt(); // CLASS:B
B.doIt(&quot;Hello&quot;) // CLASS:B, message: Hello
}
}

EDIT

You could have a simple controller and a few delegates to do something similar. Although its not all just static, you have alot more control over the parts. Please note that the example is very basic on purpose.

You could also have a class A and a class B and B has A as a dependency and then you just override what A (the delegate) should do differently. Also here ... its not static, but I do not think that this is your main problem. Its more the design after all.

import java.util.*;
public class MyClass {
public static void main(String... args) {
final DoItController doItController = DoItController.Singleton.getInstance();
doItController.doIt(DoerDelegateA.class);
doItController.doIt(DoerDelegateB.class);
doItController.doIt(DoerDelegateComplex.class);
}
}
interface Doer {
void doIt();
}
class DoItController {
final Map&lt;Class&lt;? extends DoerDelegate&gt;, DoerDelegate&gt; doerDelegateMap;
private DoItController() {
doerDelegateMap = new LinkedHashMap();
doerDelegateMap.put(DoerDelegateA.class, new DoerDelegateA());
doerDelegateMap.put(DoerDelegateB.class, new DoerDelegateB());
doerDelegateMap.put(DoerDelegateComplex.class, new DoerDelegateComplex());
}
public void doIt(Class&lt;? extends DoerDelegate&gt; delegateReference) {
final DoerDelegate delegate = doerDelegateMap.getOrDefault(delegateReference, null);
if (delegate != null) {
delegate.doIt();
}
}
public static class Singleton {
private static DoItController instance;
public static DoItController getInstance() {
if (instance == null) {
instance = new DoItController();
}
return instance;
}
}
}
class DoerDelegate implements Doer {
@Override
public void doIt() {
System.out.println(&quot;Do something with: &quot; + getClass().getSimpleName());
}
}
class DoerDelegateA extends DoerDelegate {}
class DoerDelegateB extends DoerDelegate {}
class DoerDelegateComplex extends DoerDelegateA {
@Override
public void doIt() {
System.out.println(&quot;Override standard method with: &quot; + getClass().getSimpleName());
}
}

or ...

public class MyClass {
public static void main(String... args) {
final B b = new B();
b.doIt();
}
}
interface Doer {
void doIt();
}
class A implements Doer {
@Override
public void doIt() { System.out.println(&quot;DO IT&quot;); }
}
class B implements Doer {
final A aDelegate;
public B() {
aDelegate = new A();
}
@Override
public void doIt() {
// standard
aDelegate.doIt();
// your specific B stuff
//...
}
}

huangapple
  • 本文由 发表于 2020年4月3日 19:52:05
  • 转载请务必保留本文链接:https://go.coder-hub.com/61011291.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定