如何实现一个简易版的 Spring - 如何实现 @Component 注解 (4)

为了使之前定义好的 BeanDefinition 结构保持纯粹不被破坏,这里我们再增加一个针对注解的 AnnotatedBeanDefinition 接口继承自 BeanDefinition 接口,接口比较简单只有一个获取注解元数据的方法,定义如下所示:

/** * @author mghio * @since 2021-02-14 */ public interface AnnotatedBeanDefinition extends BeanDefinition { AnnotationMetadata getMetadata(); }

同时增加一个该接口的实现类,表示从扫描注解生成的 BeanDefinition,将其命名为 ScannedGenericBeanDefinition,代码实现如下:

/** * @author mghio * @since 2021-02-14 */ public class ScannedGenericBeanDefinition extends GenericBeanDefinition implements AnnotatedBeanDefinition { private AnnotationMetadata metadata; public ScannedGenericBeanDefinition(AnnotationMetadata metadata) { super(); this.metadata = metadata; setBeanClassName(this.metadata.getClassName()); } @Override public AnnotationMetadata getMetadata() { return this.metadata; } }

还有一个问题就是使用注解的方式时该如何生成 Bean 的名字,这里我们采用和 Spring 一样的策略,当在注解指定 Bean 的名字时使用指定的值为 Bean 的名字,否则使用类名的首字母小写为生成 Bean 的名字, 很明显这只是其中的一种默认实现策略,因此需要提供一个生成 Baen 名称的接口供后续灵活替换生成策略,接口命名为 BeanNameGenerator ,接口只有一个生成 Bean 名称的方法,其定义如下:

/** * @author mghio * @since 2021-02-14 */ public interface BeanNameGenerator { String generateBeanName(BeanDefinition bd, BeanDefinitionRegistry registry); }

其默认的生成策略实现如下:

/** * @author mghio * @since 2021-02-14 */ public class AnnotationBeanNameGenerator implements BeanNameGenerator { @Override public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) { if (definition instanceof AnnotatedBeanDefinition) { String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition); if (StringUtils.hasText(beanName)) { return beanName; } } return buildDefaultBeanName(definition); } private String buildDefaultBeanName(BeanDefinition definition) { String shortClassName = ClassUtils.getShortName(definition.getBeanClassName()); return Introspector.decapitalize(shortClassName); } private String determineBeanNameFromAnnotation(AnnotatedBeanDefinition definition) { AnnotationMetadata metadata = definition.getMetadata(); Set<String> types = metadata.getAnnotationTypes(); String beanName = null; for (String type : types) { AnnotationAttributes attributes = metadata.getAnnotationAttributes(type); if (attributes.get("value") != null) { Object value = attributes.get("value"); if (value instanceof String) { String stringVal = (String) value; if (StringUtils.hasLength(stringVal)) { beanName = stringVal; } } } } return beanName; } }

最后我们再定义一个扫描器类组合以上的功能提供一个将包路径下的类读取并转换为对应的 BeanDefinition 方法,将该类命名为 ClassPathBeanDefinitionScanner,其代码实现如下:

/** * @author mghio * @since 2021-02-14 */ public class ClassPathBeanDefinitionScanner { public static final String SEMICOLON_SEPARATOR = ","; private final BeanDefinitionRegistry registry; private final PackageResourceLoader resourceLoader = new PackageResourceLoader(); private final BeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator(); public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry) { this.registry = registry; } public Set<BeanDefinition> doScanAndRegistry(String packageToScan) { String[] basePackages = StringUtils.tokenizeToStringArray(packageToScan, SEMICOLON_SEPARATOR); Set<BeanDefinition> beanDefinitions = new HashSet<>(); for (String basePackage : basePackages) { Set<BeanDefinition> candidates = findCandidateComponents(basePackage); for (BeanDefinition candidate : candidates) { beanDefinitions.add(candidate); registry.registerBeanDefinition(candidate.getId(), candidate); } } return beanDefinitions; } private Set<BeanDefinition> findCandidateComponents(String basePackage) { Set<BeanDefinition> candidates = new HashSet<>(); try { Resource[] resources = this.resourceLoader.getResources(basePackage); for (Resource resource : resources) { MetadataReader metadataReader = new SimpleMetadataReader(resource); if (metadataReader.getAnnotationMetadata().hasAnnotation(Component.class.getName())) { ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader.getAnnotationMetadata()); String beanName = this.beanNameGenerator.generateBeanName(sbd, registry); sbd.setId(beanName); candidates.add(sbd); } } } catch (IOException ex) { throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex); } return candidates; } }

到这里就已经把读取到的 @Component 注解信息转换为 BeanDefinition 了。

根据创建出来的 BeanDefinition 创建对应的 Bean 实例

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zyjyjz.html