Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
hengyunabc opened this issue Nov 23, 2022 · 0 comments
Labels
bug Something isn't working
Milestone

Comments

@hengyunabc
Copy link
Collaborator

重现方式

写一个匹配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匹配的实现,避免加载类
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

1 participant