从零开始造Spring05---实现spring注解-1

举报
码农飞哥 发表于 2021/05/29 11:22:36 2021/05/29
【摘要】 前言 前几篇我们实现了配置的方式实现Bean的生成和注入,接下来我们将要通过注解的方式来实现这一过程。这是学习刘欣老师《从零开始造Spring》课程的学习笔记。在实现的过程中我们需要用到ASM的技术,具体可以参考ASM技术 实现思路 读取XML文件对指定的package 进行扫描(scan) ,找到那些标记为@Component的类,创建BeanDefinitio...

前言

前几篇我们实现了配置的方式实现Bean的生成和注入,接下来我们将要通过注解的方式来实现这一过程。这是学习刘欣老师《从零开始造Spring》课程的学习笔记。在实现的过程中我们需要用到ASM的技术,具体可以参考ASM技术

实现思路

  1. 读取XML文件
  2. 对指定的package 进行扫描(scan) ,找到那些标记为@Component的类,创建BeanDefinition
    2.1 把一个package下面的class变成resource
    2.2 使用ASM读取Resource中的注解
    2.3 创建BeanDefinition
  3. 通过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. * <p>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<File> matchingFiles = retrieveMatchingFiles(rootDir); Resource[] result = new Resource[matchingFiles.size()]; int i = 0; for (File file : matchingFiles) { result[i++] = new FileSystemResource(file); } return result; }
  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

调试如下:
PackageResourceLoader调试结果

实现两个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<String> annotationSet = new LinkedHashSet<String>(4); private final Map<String, AnnotationAttributes> attributeMap = new LinkedHashMap<String, AnnotationAttributes>(4); public AnnotationMetadataReadingVisitor() { } @Override public AnnotationVisitor visitAnnotation(final String desc, boolean visible) { String className = Type.getType(desc).getClassName(); this.annotationSet.add(className); return new AnnotationAttributesReadingVisitor(className, this.attributeMap); } @Override public Set<String> getAnnotationTypes() { return this.annotationSet; } @Override public boolean hasAnnotation(String annotationType) { return this.annotationSet.contains(annotationType); } @Override public AnnotationAttributes getAnnotationAttributes(String annotationType) { return this.attributeMap.get(annotationType); } @Override public boolean hasSuperClass() { return false; }
}
  
 
  • 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<BeanDefinition> doScan(String packagesToScan) { String[] basePackages = StringUtils.tokenizeToStringArray(packagesToScan, ","); Set<BeanDefinition> beanDefinitions = new LinkedHashSet<BeanDefinition>();
// 对每一个basePackages进行循环 for (String basePackage : basePackages) { Set<BeanDefinition> candidates = findCandidateComponents(basePackage); for (BeanDefinition candidate : candidates) { beanDefinitions.add(candidate); registry.registerBeanDefinition(candidate.getID(), candidate); } } return beanDefinitions; } /** * * @param basePackage * @return */ public Set<BeanDefinition> findCandidateComponents(String basePackage) { Set<BeanDefinition> candidates = new LinkedHashSet<BeanDefinition>(); try {
// 先拿到Resource Resource[] resources = this.resourceLoader.getResources(basePackage); for (Resource resource : resources) { try { MetadataReader metadataReader = new SimpleMetadataReader(resource); //  通过metadataReader 获取AnnotationMetadata 看看其是否有Component注解 if (metadataReader.getAnnotationMetadata().hasAnnotation(Component.class.getName())) {
//实例化一个ScannedGenericBeanDefinition ScannedGenericBeanDefinition sbd=new ScannedGenericBeanDefinition(metadataReader.getAnnotationMetadata()); String beanName = this.beanNameGenerator.generateBeanName(sbd, this.registry); sbd.setId(beanName); candidates.add(sbd); } } catch (Throwable ex) { throw new BeanDefinitionStoreException("Failed to read candidate component class:" + resource, ex ); } } } catch (IOException e) { throw new BeanDefinitionStoreException("I/O failure during classpath scanning", e); } return candidates; }
}

  
 
  • 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

源码地址

文章来源: feige.blog.csdn.net,作者:码农飞哥,版权归原作者所有,如需转载,请联系作者。

原文链接:feige.blog.csdn.net/article/details/82315756

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。