如何生成构架图(结构图生成)
547
2022-05-30
前言
前几篇我们实现了配置的方式实现Bean的生成和注入,接下来我们将要通过注解的方式来实现这一过程。这是学习刘欣老师《从零开始造Spring》课程的学习笔记。在实现的过程中我们需要用到ASM的技术,具体可以参考ASM技术
实现思路
读取XML文件
对指定的package 进行扫描(scan) ,找到那些标记为@Component的类,创建BeanDefinition
2.1 把一个package下面的class变成resource
2.2 使用ASM读取Resource中的注解
2.3 创建BeanDefinition
通过BeanDefinition创建Bean实例,根据注解来注入。
具体实现
我们首先来了解下相关的注解,在此处,我们首先实现了两个最基本的注解@Component 注解和@Autowired 注解,注解的本质就是元数据。我们已@Autowired为例。
/** * * 注解本质上就是元数据, * @Target 标注注解的使用地方 * @Documented 说注解就是一个元素,可以被文档化 * Created by xiang.wei on 2018/7/14 * */ //构造器,字段,方法。 @Target({ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE}) //保留到运行时 @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Autowired { /** * Declares whether the annotated dependency is required. *
Defaults to {@code true}. */ boolean required() default true; }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
我们通过PackageResourceLoader类来将base-package 中配置的包下加载出对应的Resource
关键代码如下:
public Resource[] getResources(String basePackage) throws IOException { Assert.notNull(basePackage, "basePackage must not be null"); String location = ClassUtils.convertClassNameToResourcePath(basePackage); ClassLoader cl = getClassLoader(); URL url = cl.getResource(location); File rootDir = new File(url.getFile()); Set
1
2
3
4
5
6
7
8
9
10
11
12
13
14
调试如下:
实现两个Visitor,实现SimpleMetaDataReader 这两步参考ASM在Spring中的应用
实现Scanner(对ASM的抽象)
如类图所示,我们抽象了一个MetadataReader接口,该接口依赖于AnnotationVisitor抽象类和ClassVisitor抽象类,而AnnotationAttributesReadingVisitor类和ClassMetadataReadingVisitor类又是这两个抽象类的实现。由此我们就完全隐藏了AnnotationAttributesReadingVisitor类和ClassMetadataReadingVisitor类
测试代码
public class MetadataReaderTest { @Test public void testGetMetadata() throws IOException { ClassPathResource resource = new ClassPathResource("com/jay/spring/service/v4/PetStoreService.class"); MetadataReader reader = new SimpleMetadataReader(resource); AnnotationMetadata amd = reader.getAnnotationMetadata(); String annotation = Component.class.getName(); Assert.assertTrue(amd.hasAnnotation(annotation)); AnnotationAttributes attributes = amd.getAnnotationAttributes(annotation); Assert.assertEquals("petStoreService", attributes.get("value")); Assert.assertFalse(amd.isAbstract()); Assert.assertFalse(amd.isFinal()); Assert.assertEquals("com.jay.spring.service.v4.PetStoreService", amd.getClassName()); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
ClassMetadataReadingVisitor 类
/** * @param version * @param access * @param name * @param signature * @param supername * @param interfaces * * * 这个涉及到了Java的字节码, 在Java字节码中,常量池结束之后, * 有两个字节表示访问标识(access_flags), * 这个标识用于识别一些类或者接口层次的访问信息, * 例如这个Class是类或者接口,是否为public ,abstract ,final 等等 * 对类的处理,判断类的基本情况 * */ // @Override public void visit(int version, int access, String name, String signature, String supername, String[] interfaces) { this.className = ClassUtils.convertResourcePathToClassName(name); this.isInterface = ((access & Opcodes.ACC_INTERFACE) != 0); this.isAbstract = ((access & Opcodes.ACC_ABSTRACT) != 0); this.isFinal = ((access & Opcodes.ACC_FINAL) != 0); if (supername != null) { this.superClassName = ClassUtils.convertResourcePathToClassName(supername); } this.interfaces = new String[interfaces.length]; for (int i = 0; i < interfaces.length; i++) { this.interfaces[i] = ClassUtils.convertResourcePathToClassName(interfaces[i]); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
AnnotationMetadataReadingVisitor 类
public class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor implements AnnotationMetadata { private final Set
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
SimpleMetadataReader类
public SimpleMetadataReader(Resource resource) throws IOException { // 提高一点性能吧 InputStream inputStream = new BufferedInputStream(resource.getInputStream()); ClassReader classReader; try { classReader = new ClassReader(inputStream); } finally { inputStream.close(); } AnnotationMetadataReadingVisitor visitor = new AnnotationMetadataReadingVisitor(); classReader.accept(visitor, ClassReader.SKIP_DEBUG); this.annotationMetadata = visitor; this.classMetadata = visitor; this.resource = resource; }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
用ScannedGenericBeanDefinition 专门用来表达通过Scann扫描出的BeanDefiniton。
ClassPathBeanDefinitionScanner类
/** * 1. 给定一个package的名称列表,例如 org.litespring.service.v4,org.litespring.dao.v4" 2. 对指定的package 进行扫描(scan),找到那些标记为@Component 的类,创建ScannedGenericBeanDefinition,并且注册到BeanFactory中。 * Created by xiang.wei on 2018/7/15 * * @author xiang.wei */ public class ClassPathBeanDefinitionScanner { private final BeanDefinitionRegistry registry; private PackageResourceLoader resourceLoader = new PackageResourceLoader(); private final Log logger = LogFactory.getLog(getClass()); private BeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator(); public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry) { this.registry = registry; } /** * * @param packagesToScan 传入的base-package 字符串 * @return */ public Set
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
ScannedGenericBeanDefinition类
public class ScannedGenericBeanDefinition extends GenericBeanDefinition implements AnnotatedBeanDefinition { private final AnnotationMetadata metadata; public ScannedGenericBeanDefinition(AnnotationMetadata metadata) { super(); this.metadata = metadata; setBeanClassName(this.metadata.getClassName()); } @Override public AnnotationMetadata getMetadata() { return this.metadata; } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
对XmlBeanDefinitionReader类的修改
private void parseComponentElement(Element element) { String basePackages = element.attributeValue(BASE_PACKAGE_ATTRIBUTE); ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry); scanner.doScan(basePackages); }
1
2
3
4
5
6
源码地址
Spring
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。