Как получить класс, который вызывает метод stati c? - PullRequest
0 голосов
/ 03 апреля 2020

Это мой код:

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();
    }
}

Как видите, метод doIt может вызываться классами A и B. В doIt я хочу знать, какой класс использовался для вызова метода (A или B). Является ли это возможным? Три решения, которые я попробовал, не работали - это всегда говорит A класс.

Ответы [ 2 ]

2 голосов
/ 03 апреля 2020

Сначала я подумал, что это невозможно, поскольку компилятор java может выяснить, какой метод будет вызван, и выдать ту же инструкцию.

Оказывается, он фактически записывает способ вызова класса.

Итак, теперь возникает вопрос:

  • Как мы получаем место, где вызывается метод?
  • Как мы используем эту информацию, чтобы получить путь метод называется?

Первый прост: мы используем StackWalker, который может дать нам индекс байт-кода .

Теперь нам нужно только Проанализируйте класс, посмотрите инструкцию на этот индекс байт-кода и выясните, как вызывался этот метод.

Я использовал ASM для этого, но здесь это может быть не тот инструмент.

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()) && 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();
    }
}

В конце концов, я не уверен, стоит ли ваше требование этих усилий.

0 голосов
/ 03 апреля 2020

Как насчет этого здесь? Я знаю, что это не совсем то, что вы хотите, но, как уже говорили другие ... вы, возможно, захотите переосмыслить свой подход к проектированию.

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("CLASS:" + childClass.getSimpleName());
    }
}

class B extends A { 

    // doIt() will be inherited

    // overloading method with a param
    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
    }
}

РЕДАКТИРОВАТЬ

Вы могли бы иметь простой контроллер и несколько делегатов, чтобы сделать что-то подобное. Хотя это не все просто c, у вас гораздо больше контроля над деталями. Обратите внимание, что этот пример очень прост c специально.

Вы также можете иметь класс A и класс B, а B имеет зависимость A в качестве зависимости, а затем просто переопределить то, что A (делегат) должен делать сделать по-другому. Также здесь ... это не stati c, но я не думаю, что это ваша главная проблема. В конце концов, это больше дизайн.

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("Do something with: " + getClass().getSimpleName());
    }
}

class DoerDelegateA extends DoerDelegate {}
class DoerDelegateB extends DoerDelegate {}
class DoerDelegateComplex extends DoerDelegateA {
    @Override
    public void doIt() {
        System.out.println("Override standard method with: " + 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("DO IT"); }
}

class B implements Doer {
    final A aDelegate;
    public B() {
        aDelegate = new A();
    }

    @Override
    public void doIt() {
        // standard
        aDelegate.doIt();
        // your specific B stuff
        //...
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...