2016-10-18 4 views
3

Я пытаюсь изучить (на уровне байт-кода, ASM) классы, реализующие некоторые конкретные интерфейсы (в данном случае java.sql.Connection), и обнаружил, что в некоторых случаях библиотека имеет другой интерфейс, расширяющий что-то из моего набора интерфейсов ... и затем их классы реализуют интерфейс THAT. (В этом случае новый расширенный интерфейс com.mysql.jdbc.Connection расширяет java.sql.Connection, а затем их реализации, например, ConnectionImpl реализует com.mysql.jdbc.Connection.) Поэтому я пропускаю идентификацию ConnectionImpl в качестве целевого класса.Есть ли способ узнать, является ли класс интерфейсом?

Итак, результатом является то, что при загрузке класса мне нужно идентифицировать com.mysql.jdbc.Connection как «интересный» интерфейс. Но я не вижу, как определить класс AS как интерфейс, а не только обычный класс. Есть ли что-то в ClassReader, чем может дать мне такую ​​информацию?

+0

В конце концов, я думаю, что я был занят. Даже если бы я мог это сделать, нет ничего, чтобы убедиться, что интерфейс действительно загружен перед классом. Например, из того, что я вижу, ConnectionImpl загружается перед mysql.jdbc.Connection, и поэтому мне не известно, что ConnectionImpl действительно реализует java.sql.Connection. – ticktock

ответ

2

В соответствии с названием, если вы хотите, чтобы проверить, является ли класс интерфейс:

ClassNode node = // However you wish to load the class 
if ((node.access & Opcodes.ACC_INTERFACE) != 0){ 
    // is interface 
} else { 
    // is not an interface 
} 

В вашем посте вы утверждаете, вы хотите, чтобы найти детей/реализаций java.sql.Connection.

Проблема у Вас есть это:
java.sql.Connection -> com.mysql.jdbc.Connection -> ConnectionImpl

ConnectionImpl не напрямую реализовать java.sql.Connection поэтому он не был обнаружен, как ребенок/реализации. Для такой проблемы вам нужно будет путешествовать по иерархии классов. Если бы я был в вашей ситуации, я бы загрузил карту ClassNodes <String, ClassNode>, где строка является именем ClassNode. Затем я бы использовал рекурсивный метод для проверки того, является ли данный класс предназначенным типом. Поэтому, если вы отправляете ClassNode, он будет вызывать себя с родительским узлом, а затем с интерфейсами. В конце концов, если исходный данный узел является правильным типом, в конце будет пройден и найден интерфейс java.sql.Connection. Кроме того, если вы хотите пропустить резервные копии, вы можете сохранить результаты каждого ClassNode на карте, чтобы вам не приходилось проверять всю иерархию снова и снова.

Edit: Сорта как этот

public static boolean isMatch(ClassNode cn, Map<String,ClassNode> nodes, String target){ 
    if (cn.name.equals(target)) return true; 
    else{ 
     if (nodes.containsKey(cn.superName) && isMatch(nodes.get(cn.superName),nodes,target)) return true; 
     for (String interf : cn.interfaces){ 
      if (nodes.containsKey(interf) && isMatch(nodes.get(interf),nodes,target)) return true; 
     } 
    } 
    return false; 
} 
-1

Метод isInterface()java.lang.Class для проверки того, является ли класс интерфейсом или нет.

+0

Добро пожаловать в переполнение стека! Хотя это может быть ценным советом для решения проблемы, ответ действительно должен продемонстрировать решение. Пожалуйста, [править], чтобы предоставить пример кода, чтобы показать, что вы имеете в виду. В качестве альтернативы подумайте о том, чтобы вместо этого писать это как комментарий. –

0

Существует метод, который проверяет его для вас Class#isInterface()

if (yourClass.isInterface()) { 
    //do something 
} 

http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/Class.html#isInterface%28%29

+1

Предполагается, что класс загружен. Извините, это вопрос ASM, и я смотрю на байтовый код. Мне нужно уточнить этот вопрос. – ticktock

+0

Возможно, вы могли бы реализовать поток для загрузки mysql.jdbc.Connection и обеспечить его завершение, прежде чем двигаться вперед –

1

Как уже упоминалось, вы должны проверить каждый тип интерфейса для его интерфейсов с целью определения такого подтипа отношения , Это, однако, не сложно сделать, если у вас есть доступ ко всем ресурсам.

Когда вы используете ASM, вам просто нужно взять имена интерфейса вашего исходного класса и найти файл класса для каждого такого интерфейса. Затем вы можете разобрать каждый файл класса для своих интерфейсов и так далее. Таким образом, вы можете определить весь график и определить отношение подтипа.

Если вы не хотите делать это вручную, вы можете использовать Byte Buddy, который предлагает Вам методы, подобные отражения API для ненагруженных типов:

ClassFileLocator cfl = ... // can be file system, class loader, etc. 
TypePool.Default.of(cfl).describe("your.initial.type") 
         .resolve() 
         .isAssignableTo(someInterface); 

Вы также можете использовать TypePool читать целевой интерфейс, если он недоступен.

0

Использование org.objectweb.asm.ClassReader, мы можем определить, загруженный класс действительно класс или интерфейс. Пример фрагмента кода ниже

public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, 
     ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException{ 

     ClassReader classReader = null; 
     try{ 
      classReader=new ClassReader(classfileBuffer); 
      if(isInterface(classReader)){ 
       return classfileBuffer; 
      } 
     } 
     catch(Throwable exp){ 
      return classfileBuffer; 
     } 
     // Remaining logic here 

    } 

    public boolean isInterface(ClassReader cr) { 
     return ((cr.getAccess() & 0x200) != 0); 
    }