Skip to content

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

@hengyunabc

Description

@hengyunabc

重现方式

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

@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类的匹配,比如:

@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的判断

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory

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

  • 目前的实现比较简单,直接调用loader.loadClass来加载 interface了。
  • 导致结果是在 ClassFileTransformer#transform 处理 DefaultListableBeanFactory 时,又加载了 AbstractAutowireCapableBeanFactory ,所以后面 AbstractAutowireCapableBeanFactory 不会被 ClassFileTransformer#transform 处理了!
  • 这里 jdk处理的逻辑不符合用户的想像
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匹配的实现,避免加载类

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions