对于注解失效的思考

最近在工作中遇到了@Transactional和@Async注解不生效
发现原因是this方法调用导致AOP失效
于是在想问一个问题,我们能不能够像idea搜索方法引用那样直接找出来,这些存在的问题呢?

首先,我们一开始需要扫描类中的被Transactional或者Async注解的类方法,于是我们需要扫描我们项目底下的编译目录,遍历每个方法是否已经注解了上面这些方法。

Class<?> clazz = Class.forName(className);
try {
    for (Method method : clazz.getDeclaredMethods()) {
        spanAnnotation.forEach(annotation->{
            if (method.isAnnotationPresent(annotation)) {
                methods.add(clazz.getName()+"#" + method.getName());
            }
        });
    }
} catch (Throwable e) {
    e.printStackTrace();
}

随后如何在项目中寻找方法的引用呢?
ASM
ASM可以在方法体中找到对应的方法引用

   class MethodReferenceClassVisitor extends ClassVisitor {
            private final String methodName;
            private final String className;
            private final HashSet<String> referencedBy = new HashSet<>();

            public MethodReferenceClassVisitor(String methodName, String className) {
                super(Opcodes.ASM6);
                this.methodName = methodName;
                this.className = className;
            }

            public HashSet<String> getReferencedBy() {
                return referencedBy;
            }

            @Override
            public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
                return new MethodReferenceMethodVisitor(className + "#" + name, methodName, referencedBy);
            }
        }

        class MethodReferenceMethodVisitor extends MethodVisitor {
            private final String selfMethod;
            private final String methodName;
            private final HashSet<String> referencedBy;
            public String localVariables=null;
            public int Line;
            public MethodReferenceMethodVisitor(String name, String methodName, HashSet<String> referencedBy) {
                super(Opcodes.ASM6);
                this.selfMethod = name;
                this.methodName = methodName;
                this.referencedBy = referencedBy;
            }
            @Override
            public void visitVarInsn(int opcode, int var) {
                if (opcode == Opcodes.ALOAD && var == 0) {
                    localVariables= Opcodes.ALOAD+"-"+var;
                }
                super.visitVarInsn(opcode, var);
            }

            @Override
            public void visitTypeInsn(int opcode, String type) {
                super.visitTypeInsn(opcode, type);
            }

            @Override
            public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) {
                owner = owner.replaceAll("/", ".");
                if ((owner +"#"+ name).equals(methodName)) {
                    if((Opcodes.ALOAD +"-0").equals(localVariables)){
                        referencedBy.add(selfMethod+":"+Line);
                    }
                }
            }
            @Override
            public void visitLineNumber(int line, Label start) {
                super.visitLineNumber(line, start);
                Line=line;
            }
        }

简单来说就是构建一个ClassVisitor重载对应方法,在读取Class的时候ClassReader会调用ClassVisitor的visitMethod方法,随后调用MethodVisitor加载方法体中的内容
visitMethodInsn可以读取到方法名参数返回
visitLineNumber可以读取到引用的行数
visitVarInsn可以读取到方法当前是否使用对象

不过基于以上这些还是无法判断出对象引用还是this方法引用

下面附上代码
项目为jdk11内置asm,如果jdk不对,需要自己引入asm包

 <dependencies>
        <dependency>
            <groupId>org.apache.maven</groupId>
            <artifactId>maven-model</artifactId>
            <version>3.8.1</version>
        </dependency>
    </dependencies>
import jdk.internal.org.objectweb.asm.*;
import org.apache.maven.model.Model;
import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
 import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.stream.Collectors;

public class SpanAnno {
    static List<String> classes = new LinkedList<>();
    static HashMap<String, ClassReader> classReaders = new HashMap<>();
    static List<String> methods = new LinkedList<>();
    static List<String> errors = new LinkedList<>();
     static String[] spanStr = new String[]{
            "org.springframework.scheduling.annotation.Async",
            "org.springframework.transaction.annotation.Transactional",
    };
    static List<Class<? extends Annotation>> spanAnnotation = new ArrayList<>(spanStr.length);
     static {
        for (String s : spanStr) {
            try {
                spanAnnotation.add((Class<? extends Annotation>) Class.forName(s));
            } catch (ClassNotFoundException e) {
             }
        }
    }
     public static void main(String[] args) throws Exception {
        URL resource = ClassLoader.getSystemResource("");
        File pomFile = new File(resource.getPath().replaceAll("/target", "").replaceAll("/classes", "") + "pom.xml");
        MavenXpp3Reader reader = new MavenXpp3Reader();
        Model model = reader.read(new FileInputStream(pomFile));
        List<File> Dirs = new ArrayList<>();
         model.getDependencies().forEach(f -> {
            File file1 = new File(f.getArtifactId() + "/target/classes");
            if (!file1.exists()) {
                return;
            }
            Dirs.add(file1);
        });
        Dirs.add(new File(resource.getPath()));
        for (File dir : Dirs) {
            try {
                String basePath = dir.getAbsolutePath();
                List<File> files = Files.walk(Paths.get(basePath))
                        .filter(Files::isRegularFile)
                        .filter(file -> file.toString().endsWith(".class"))
                        .map(Path::toFile)
                        .collect(Collectors.toList());
                for (File file : files) {
                    String className = file.getAbsolutePath().replace(basePath, "")
                            .replaceAll("^[/\\\\]+|[/\\\\]+$", "")
                            .replaceAll("[/\\\\]", ".");
                    className = className.substring(0, className.lastIndexOf("."));
                    classes.add(className);
                    try {
                        Class<?> clazz = Class.forName(className);
                        try {
                            for (Method method : clazz.getDeclaredMethods()) {
                                spanAnnotation.forEach(annotation -> {
                                    if (method.isAnnotationPresent(annotation)) {
                                        methods.add(clazz.getName() + "#" + method.getName());
                                    }
                                });
                            }
                        } catch (Throwable e) {
                            e.printStackTrace();
                        }
                    } catch (Throwable e) {
                        e.printStackTrace();
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        MethodReferenceFinder finder = new MethodReferenceFinder();
        System.out.println(new Date());
        methods.forEach(m -> {
            classes.forEach(c -> {
                try {
                    finder.findReferences(c, m);
                    HashSet<String> references = finder.getReferences(c);
                    for (String reference : references) {
                        if (reference.split("#")[0].equals(m.split("#")[0])) {
                            errors.add(m + " -> " + reference);
                        }
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            });
        });
        errors.forEach(System.err::println);
    }
     static class MethodReferenceFinder {
        private final Map<String, HashSet<String>> references = new HashMap<>();
         public void findReferences(String className, String methodName) throws IOException {
            ClassReader classReader;
            if (classReaders.containsKey(className)) {
                classReader = classReaders.get(className);
            } else {
                classReader = new ClassReader(className);
                classReaders.put(className, classReader);
            }
            MethodReferenceClassVisitor classVisitor = new MethodReferenceClassVisitor(methodName, className);
            classReader.accept(classVisitor, Opcodes.ASM6);
            references.put(className, classVisitor.getReferencedBy());
        }
         public HashSet<String> getReferences(String className) {
            return references.get(className);
        }
         class MethodReferenceClassVisitor extends ClassVisitor {
            private final String methodName;
            private final String className;
            private final HashSet<String> referencedBy = new HashSet<>();
             public MethodReferenceClassVisitor(String methodName, String className) {
                super(Opcodes.ASM6);
                this.methodName = methodName;
                this.className = className;
            }
             public HashSet<String> getReferencedBy() {
                return referencedBy;
            }
             @Override
            public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
                return new MethodReferenceMethodVisitor(className + "#" + name, methodName, referencedBy);
            }
        }
         class MethodReferenceMethodVisitor extends MethodVisitor {
            private final String selfMethod;
            private final String methodName;
            private final HashSet<String> referencedBy;
            public String localVariables = null;
            public int Line;
             public MethodReferenceMethodVisitor(String name, String methodName, HashSet<String> referencedBy) {
                super(Opcodes.ASM6);
                this.selfMethod = name;
                this.methodName = methodName;
                this.referencedBy = referencedBy;
            }
             @Override
            public void visitVarInsn(int opcode, int var) {
                if (opcode == Opcodes.ALOAD && var == 0) {
                    localVariables = Opcodes.ALOAD + "-" + var;
                }
                super.visitVarInsn(opcode, var);
            }
             @Override
            public void visitTypeInsn(int opcode, String type) {
                super.visitTypeInsn(opcode, type);
            }
             @Override
            public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) {
                owner = owner.replaceAll("/", ".");
                if ((owner + "#" + name).equals(methodName)) {
                    if ((Opcodes.ALOAD + "-0").equals(localVariables)) {
                        referencedBy.add(selfMethod + ":" + Line);
                    }
                }
            }
             @Override
            public void visitLineNumber(int line, Label start) {
                super.visitLineNumber(line, start);
                Line = line;
            }
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值