这一次搞懂Spring自定义标签以及注解解析原理( 三 )

这个方法实现很复杂 , 首先是扫描找到符合条件的类并封装成BeanDefinition对象 , 接着去设置该对象是否可作为根据类型自动装配的标记 , 然后解析@Lazy、@Primary、@DependsOn等注解 , 最后才将其注册到容器中 。 需要注意的是和xml解析不同的是在扫描过程中 , 创建的是ScannedGenericBeanDefinition对象:
这一次搞懂Spring自定义标签以及注解解析原理文章插图
该类是GenericBeanDefinition对象的子类 , 并持有了AnnotationMetadata对象的引用 , 进入下面这行代码:
MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);我们可以发现AnnotationMetadata实际上是AnnotationMetadataReadingVisitor对象:
这一次搞懂Spring自定义标签以及注解解析原理文章插图
从上图中我们可以看到该对象具有很多属性 , 基本上包含了我们类的所有信息 , 所以后面在对象实例化时需要的信息都是来自于这里 。 以上就是Spring注解的扫描解析过程 , 现在还剩一个方法registerComponents , 它是做什么的呢?
protected void registerComponents(XmlReaderContext readerContext, Set beanDefinitions, Element element) {Object source = readerContext.extractSource(element);CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), source);for (BeanDefinitionHolder beanDefHolder : beanDefinitions) {compositeDef.addNestedComponent(new BeanComponentDefinition(beanDefHolder));}// Register annotation config processors, if necessary.boolean annotationConfig = true;if (element.hasAttribute(ANNOTATION_CONFIG_ATTRIBUTE)) {annotationConfig = Boolean.valueOf(element.getAttribute(ANNOTATION_CONFIG_ATTRIBUTE));}if (annotationConfig) {Set processorDefinitions =AnnotationConfigUtils.registerAnnotationConfigProcessors(readerContext.getRegistry(), source);for (BeanDefinitionHolder processorDefinition : processorDefinitions) {compositeDef.addNestedComponent(new BeanComponentDefinition(processorDefinition));}}readerContext.fireComponentRegistered(compositeDef); }在该标签中有一个属性annotation-config , 该属性的作用是 , 当配置为true时 , 才会去注册一个个BeanPostProcessor类 , 这个类非常重要 , 比如:ConfigurationClassPostProcessor支持@Configuration注解 , AutowiredAnnotationBeanPostProcessor支持@Autowired注解 , CommonAnnotationBeanPostProcessor支持@Resource、@PostConstruct、@PreDestroy等注解 。 这里只是简单提提 , 详细分析留待后篇 。 至此 , 自定义标签和注解的解析原理就分析完了 , 下面就看看如何定义我们自己的标签 。
定义我们自己的标签通过上面的分析 , 我相信对于定义自己的标签流程应该大致清楚了 , 如下:

  1. 首先定义一个标签 , 并在classpath/META-INF文件夹下创建一个spring.schema文件 , 在文件中指定标签url和xsd文件的对应关系(xsd是定义标签规范的文件)
  2. 其次定义其NamespaceHandler类 , 让它继承NamespaceHandlerSupport类;
  3. 然后定义标签对应的解析器 , 并实现parse方法 , 在parse方法中解析我们的标签 , 将其封装为BeanDefinition对象并注册到容器中;
  4. 最后在classpath/META-INF文件夹下创建一个spring.handler文件 , 并定义标签的命名空间和NamespaceHandler的映射关系 。
这就是我们从之前的源码分析中理解到的 , 但这里实际还忽略了一个步骤 , 这也是之前分析时没讲到的 , 你能想到是什么么?我们设计的标签需不需要一个规范?不可能让其他人随便写 , 否则怎么识别呢?因此需要一个规范约束 。 同样 , 在Spring的META-INF文件夹下都会有一个spring.schemas文件 , 该文件和spring.handler文件一样 , 定义了约束文件和约束命名空间的映射关系 , 下面就是context的spring.schemas文件部分内容:
http\://www.springframework.org/schema/context/spring-context-2.5.xsd=org/springframework/context/config/spring-context.xsd......http\://www.springframework.org/schema/cache/spring-cache.xsd=org/springframework/cache/config/spring-cache.xsd但是这个文件是在什么时候被读取的呢?是不是应该在解析xml之前就把规范设置好?实际上就是在调用XmlBeanDefinitionReader的doLoadDocument方法时读取的该文件:
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,getValidationModeForResource(resource), isNamespaceAware()); } protected EntityResolver getEntityResolver() {if (this.entityResolver == null) {// Determine default EntityResolver to use.ResourceLoader resourceLoader = getResourceLoader();if (resourceLoader != null) {this.entityResolver = new ResourceEntityResolver(resourceLoader);}else {this.entityResolver = new DelegatingEntityResolver(getBeanClassLoader());}}return this.entityResolver; } public DelegatingEntityResolver(@Nullable ClassLoader classLoader) {this.dtdResolver = new BeansDtdResolver();this.schemaResolver = new PluggableSchemaResolver(classLoader); } public static final String DEFAULT_SCHEMA_MAPPINGS_LOCATION = "META-INF/spring.schemas"; public PluggableSchemaResolver(@Nullable ClassLoader classLoader) {this.classLoader = classLoader;this.schemaMappingsLocation = DEFAULT_SCHEMA_MAPPINGS_LOCATION; }