alibaba / alibaba/one-java-agent
匹配 interface的机制会提前加载类,导致 ClassFileTransformer#transform 丢失部分类
- Lingua principale
- Java
- Stelle
- 579
- Fork
- 128
- Metriche di merge delle PR
- Nessuna PR unita negli ultimi 30g
Descrizione
## 重现方式
写一个匹配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匹配的实现,避免加载类
Guida per i contributori
Nessuna guida per i contributori indicizzata per questo repository
Direzione di ricerca
Inizia da SimpleInterfaceMatcher.match e segui il percorso di corrispondenza delle interfacce utilizzato durante ClassFileTransformer#transform. Riproduci lo scenario con DefaultListableBeanFactory e Invoker, quindi verifica che la corrispondenza non carichi le classi delle interfacce o delle superclassi, continuando però a riconoscere le interfacce ereditate. Il lavoro è completato quando AbstractAutowireCapableBeanFactory rimane disponibile per l’elaborazione da parte del transformer.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Valutazione
- Stack tecnologico
- java
- Ambito
- devtools
- Tipo di issue
- Bug
- Difficoltà
- 4/5
- Tempo stimato
- 3-5 giorni
- Stato di attività
- Ferma
- Chiarezza
- Abbastanza chiara
- Idoneità per principianti
- 35/100