alibaba / alibaba/one-java-agent

匹配 interface的机制会提前加载类,导致 ClassFileTransformer#transform 丢失部分类

Offen
#37 0 Kommentare 0 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen
bug
Vorherrschende Sprache
Java
Sterne
579
Forks
128
PR-Merge-Kennzahlen
Keine gemergten PRs in 30 T.

Beschreibung

## 重现方式

写一个匹配interface的`@Instrument`,比如

```java
@Instrument(Interface = "org.apache.dubbo.rpc.Invoker")
public abstract class Invoker {

/**
* invoke.
*
* @param invocation
* @return result
* @throws RpcException
*/
public Result invoke(Invocation invocation) throws RpcException {
DubboUtils.test(invocation);
System.err.println("invoker class: " + this.getClass().getName());
Result result = InstrumentApi.invokeOrigin();
System.err.println("result:" + result + ", invoker class: " + this.getClass().getName());
return result;
}

}
```

再写一个匹配abstract类的匹配,比如:

```java
@Instrument(Class = "org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory")
public class AbstractAutowireCapableBeanFactory {

protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
return InstrumentApi.invokeOrigin();
}
```

那么在加载类 org.springframework.beans.factory.support.DefaultListableBeanFactory 时,会经过`Invoker`的判断

```java
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
```

对 `@Instrument(Interface = "org.apache.dubbo.rpc.Invoker")` 的匹配判断比较复杂,因为 interface 可能有多重继承

* **目前的实现比较简单,直接调用loader.loadClass来加载 interface了。**
* 导致结果是在 ClassFileTransformer#transform 处理 DefaultListableBeanFactory 时,又加载了 AbstractAutowireCapableBeanFactory ,所以后面 AbstractAutowireCapableBeanFactory 不会被 ClassFileTransformer#transform 处理了!
* 这里 jdk处理的逻辑不符合用户的想像

```java
public class SimpleInterfaceMatcher implements ClassMatcher {

@Override
public boolean match(ClassLoader loader, String className, Class classBeingRedefined,
ProtectionDomain protectionDomain, byte[] classfileBuffer) {
loader = ClassLoaderUtils.wrap(loader);

if (classBeingRedefined != null) { // 在retransform 时,可以直接判断
return match(classBeingRedefined);
} else {
// 读取出具体的 类名,还有父类名, 如果有匹配,则返回 true,没有,就返回 false
ClassReader reader = new ClassReader(classfileBuffer);
String clazzName = reader.getClassName();
String superName = reader.getSuperName();
String[] interfacesArray = reader.getInterfaces();

// 如果是接口,则没有需要处理的地方
if ((reader.getAccess() & Opcodes.ACC_INTERFACE) != 0) {
return false;
}

if (interfaces != null && interfaces.contains(clazzName.replace('/', '.'))) {
return true;
}

for (String i : interfacesArray) {
try {
Class interfaceClass = loader.loadClass(i.replace('/', '.'));
if (matchInterface(interfaceClass)) {
return true;
}
} catch (ClassNotFoundException e) {
// ignore
}
}
```

## 后续

* 需要重新实现 interface匹配的实现,避免加载类

Beitragsleitfaden

Für dieses Repository ist kein Beitragsleitfaden indexiert

Rechercherichtung

Beginne in SimpleInterfaceMatcher.match und verfolge den Pfad zur Interface-Zuordnung, der während ClassFileTransformer#transform verwendet wird. Stelle das Szenario mit DefaultListableBeanFactory und Invoker nach und überprüfe anschließend, dass die Zuordnung keine Interface- oder Superklassen lädt, während geerbte Interfaces weiterhin erkannt werden. Erledigt ist die Aufgabe, wenn AbstractAutowireCapableBeanFactory für die Verarbeitung durch den Transformer verfügbar bleibt.

Vom Indexierungsmodell aus dem Issue-Text verfasst.

Bewertung

Tech-Stack
java
Bereich
devtools
Issue-Typ
Bug
Schwierigkeit
4/5
Geschätzter Aufwand
3-5 Tage
Aktivitätsstatus
Veraltet
Klarheit
Größtenteils klar
Anfängerfreundlichkeit
35/100

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.