用ASM直接读取字节码来加载Class的Annotation

转自http://blog.chinaunix.net/uid-254237-id-2458630.html

分类: Java

Asm是很好的ByteCode generator 和 ByteCode reader。 Asm提供了ClassVisitor来访问Class中的每个元素。当用ClassReader来读取Class的字节码时,每read一个元素,ASM会调用指定的ClassVisitor来访问这个元素。这就是访问者模式。利用这个特点,当ClassVisitor访问Class的Annotation元素时,我们会把annotation的信息记录下来。这样就可以在将来使用这个Annotation。
举一个例子吧,我们用AnnotationFaker来标注一个Class,也就是Annotation的target是Type级别。当我们发现某个class是用AnnotationFaker标注的,我们就load这个class到jvm中,并初始化,否则免谈。

1.AnnotationFaker: annnotation用来标注需要初始化的class
 1 package com.oocl.isdc.sha.frm.test.config;
 2
 3 import java.lang.annotation.ElementType;
 4 import java.lang.annotation.Retention;
 5 import java.lang.annotation.RetentionPolicy;
 6 import java.lang.annotation.Target;
 7
 8 @Retention(RetentionPolicy.RUNTIME)
 9 @Target( {ElementType.TYPE})
10 public @ interface AnnotationFaker  {
11}

12

2.ClassFaker: 被AnnotationFaker标注的class

1 package com.oocl.isdc.sha.frm.test.config;
2
3 @AnnotationFaker
4 public  class ClassFaker  {
5    public void hello() {
6        System.out.println("hello world, load me success!");
7    }

8}

9


3. ClassVisitorFaker:ClassVisitor的一个实现,用来得到class上的Annotation

 1 package com.oocl.isdc.sha.frm.test.config;
 2
 3 import java.util.ArrayList;
 4 import java.util.List;
 5
 6 import org.objectweb.asm.AnnotationVisitor;
 7 import org.objectweb.asm.Attribute;
 8 import org.objectweb.asm.ClassVisitor;
 9 import org.objectweb.asm.FieldVisitor;
10 import org.objectweb.asm.MethodVisitor;
11 import org.objectweb.asm.tree.AnnotationNode;
12
13 public  class ClassVisitorFaker  implements ClassVisitor {
14
15    public List<AnnotationNode> visibleAnnotations;
16
17    public List<AnnotationNode> invisibleAnnotations;
18    
19    public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
20      
21    }

22
23    public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
24        AnnotationNode an = new AnnotationNode(desc);
25        if (visible) {
26            if (visibleAnnotations == null{
27                visibleAnnotations = new ArrayList<AnnotationNode> (1);
28            }

29            visibleAnnotations.add(an);
30        }
 else {
31            if (invisibleAnnotations == null{
32                invisibleAnnotations = new ArrayList<AnnotationNode> (1);
33            }

34            invisibleAnnotations.add(an);
35        }

36        return an;
37    }

38
39    public void visitAttribute(Attribute attr) {
40    }

41
42    public void visitEnd() {
43    }

44
45    public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
46        return null;
47    }

48
49    public void visitInnerClass(String name, String outerName, String innerName, int access) {
50    }

51
52    public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
53        return null;
54    }

55
56    public void visitOuterClass(String owner, String name, String desc) {
57    }

58
59    public void visitSource(String source, String debug) {
60    }

61
62    public List<AnnotationNode> getVisibleAnnotations() {
63        return visibleAnnotations;
64    }

65
66    public List<AnnotationNode> getInvisibleAnnotations() {
67        return invisibleAnnotations;
68    }

69}

70


4. ClassParser: main class, 分析classpath上的class, 如果是用AnnotationFaker标注的class,我们就初始化它。

 1 package com.oocl.isdc.sha.frm.test.config;
 2
 3 import java.io.File;
 4 import java.io.IOException;
 5 import java.net.URISyntaxException;
 6 import java.net.URL;
 7 import java.util.Enumeration;
 8 import java.util.HashSet;
 9 import java.util.Iterator;
10 import java.util.List;
11 import java.util.Set;
12 import java.util.zip.ZipEntry;
13 import java.util.zip.ZipException;
14 import java.util.zip.ZipFile;
15
16 import org.objectweb.asm.ClassReader;
17 import org.objectweb.asm.Type;
18 import org.objectweb.asm.tree.AnnotationNode;
19
20 public  class ClassParser  {
21    @SuppressWarnings("unchecked")
22    public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, URISyntaxException {
23        Enumeration<URL> urls = getClassLoader().getResources(".");
24        Set<String> result = new HashSet<String>();
25        while(urls.hasMoreElements()) {
26            URL url = urls.nextElement();
27            File file = new File(url.toURI());
28            if (file.isDirectory()) {
29                handleDirectory(result, file, null);
30            }
 else {
31                handleArchive(result, file);
32            }

33        }

34        
35        for(String clsRsName : result) {
36            ClassVisitorFaker cv = new ClassVisitorFaker();
37            ClassReader cr = new ClassReader(getClassLoader().getResourceAsStream(clsRsName));
38            cr.accept(cv, 0);
39            List<AnnotationNode> annotationsList = cv.getVisibleAnnotations();
40            if(null != annotationsList) {
41                for(Iterator<AnnotationNode> it = annotationsList.iterator(); it.hasNext();) {
42                    AnnotationNode annotation = it.next();
43                    Type t = Type.getType(annotation.desc);
44                    if(AnnotationFaker.class.getName().equals(t.getClassName())) {
45                        Class clazz = Class.forName(filenameToClassname(clsRsName));
46                        ClassFaker faker = (ClassFaker) clazz.newInstance();
47                        faker.hello();
48                    }

49                }

50            }

51          
52        }

53     
54    }

55    
56    private static void handleDirectory(final Set<String> result, final File file, final String path) {
57        for (final File child : file.listFiles()) {
58            final String newPath = path == null ? child.getName() : path + '/' + child.getName();
59            if (child.isDirectory()) {
60                handleDirectory(result, child, newPath);
61            }
 else {
62                handleItem(result, newPath);
63            }

64        }

65    }

66    
67    private static void handleItem(final Set<String> result, final String name) {
68        if (name.endsWith(".class")) {
69            result.add(name);
70        }

71    }

72    
73    private static void handleArchive(final Set<String> result, final File file) throws ZipException, IOException {
74        final ZipFile zip = new ZipFile(file);
75        final Enumeration<? extends ZipEntry> entries = zip.entries();
76        while (entries.hasMoreElements()) {
77            final ZipEntry entry = entries.nextElement();
78            final String name = entry.getName();
79            handleItem(result, name);
80        }

81    }

82    
83    private static ClassLoader getClassLoader() {
84        ClassLoader clsLoader = Thread.currentThread().getContextClassLoader();
85        if(clsLoader == null{
86            clsLoader = ClassLoader.getSystemClassLoader();
87        }

88        return clsLoader;
89    }

90    
91    
92    public static String filenameToClassname(final String filename) {
93        return filename.substring(0, filename.lastIndexOf(".class")).replace('/', '.').replace('\\', '.');
94    }

95    
96
97}


代码中,需要引入org.objectweb.asm-3.3.1.v201105211655.jar包。因为博客不能穿附件,会上传到资源处。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
通过 ASM 直接读取字节码文件获取普通注释内容需要使用 ASMClassReader 类。ClassReader 类可以读取一个字节码文件,并将其转换成一个类的内部表示,包括类名、访问标志、字段、方法等信息。我们可以使用 ClassReader 类获取每个方法的字节码,并解析其中的普通注释内容。 下面是一个示例代码,可以通过 ASM 直接读取字节码文件获取普通注释内容: ```java import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.List; public class CommentReader extends ClassVisitor { private List<String> comments = new ArrayList<>(); public CommentReader() { super(Opcodes.ASM9); } public void readComments(InputStream inputStream) throws IOException { ClassReader reader = new ClassReader(inputStream); reader.accept(this, 0); } public void visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); mv.visitCode(); for (int i = 0; i < mv.getInstructions().size(); i++) { Object insn = mv.getInstructions().get(i); if (insn instanceof org.objectweb.asm.tree.LineNumberNode) { org.objectweb.asm.tree.LineNumberNode line = (org.objectweb.asm.tree.LineNumberNode) insn; String text = mv.visitLineNumber(line.line, line.start).getText(); if (text != null && text.startsWith("//")) { comments.add(text); } break; } } mv.visitEnd(); } public List<String> getComments() { return comments; } } ``` 在上述示例代码中,我们创建了一个 CommentReader 类来读取字节码文件,并解析其中的普通注释内容。在 CommentReader 类中,我们重写了 visitMethod() 方法,该方法会在读取到每个方法时被调用。在 visitMethod() 方法中,我们使用 MethodVisitor 类的 visitCode() 方法获取方法的字节码,然后遍历字节码中的每行代码,如果该行代码是注释行,则将其保存到 comments 列表中。 最后,我们可以通过调用 CommentReader 类的 readComments() 方法来读取字节码文件,并通过 getComments() 方法获取解析出来的注释内容。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值