alibaba / alibaba/one-java-agent
匹配 interface的机制会提前加载类,导致 ClassFileTransformer#transform 丢失部分类
- 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