Spring-如何自定义扫描包下面的接口

目录

第一步先介绍基于正常的扫描包的流(ConfigurationClassParser是如何扫描包)

1.invokeBeanFactoryPostProcessors

2.扫描一个包下的所有的class

3.是否设置了过滤器(这是一个大坑)

 match逻辑

4.判断是否符合候选组件条件

 针对上述过程分析

思路

代码演示


目录

第一步先介绍基于正常的扫描包的流(ConfigurationClassParser是如何扫描包)

1.invokeBeanFactoryPostProcessors

2.扫描一个包下的所有的class

3.是否设置了过滤器

 4.判断是否符合候选组件条件

 针对上述过程分析


第一步先介绍基于正常的扫描包的流(ConfigurationClassParser是如何扫描包)

1.invokeBeanFactoryPostProcessors

  • 刚进来时只有org.springframework.context.annotation.internalConfigurationAnnotationProcessor符合要求用来解析配置类

Spring-如何自定义扫描包下面的接口_第1张图片

2.扫描一个包下的所有的class

  • 因为走的是正常的流程,扫描包的时候,扫描到的元数据是一个接口,会返回false,这是一个扩展点,需要在这个地方如果是接口的话返回ture.

Spring-如何自定义扫描包下面的接口_第2张图片

3.是否设置了过滤器(这是一个大坑)

  • 必须要自己定义一个包含包含过滤器

Spring-如何自定义扫描包下面的接口_第3张图片

 match逻辑

@Override
	public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
			throws IOException {

		//是否标注@Component注解;如果是接口的话不会走这个逻辑
		if (matchSelf(metadataReader)) {
			return true;
		}
		ClassMetadata metadata = metadataReader.getClassMetadata();
		if (matchClassName(metadata.getClassName())) {
			return true;
		}
        
        //是否考虑接口;不管是默认的excludeFilters还是includeFilters  
        //里面的considerInherited和considerInterfaces都是false,都不会走这个逻辑
		if (this.considerInterfaces) {
			for (String ifc : metadata.getInterfaceNames()) {
				// Optimization to avoid creating ClassReader for super class
				Boolean interfaceMatch = matchInterface(ifc);
				if (interfaceMatch != null) {
					if (interfaceMatch.booleanValue()) {
						return true;
					}
				}
				else {
					// Need to read interface to determine a match...
					try {
						if (match(ifc, metadataReaderFactory)) {
							return true;
						}
					}
					catch (IOException ex) {
						if (logger.isDebugEnabled()) {
							logger.debug("Could not read interface [" + ifc + "] for type-filtered class [" +
									metadata.getClassName() + "]");
						}
					}
				}
			}
		}

		return false;
	}

4.判断是否符合候选组件条件

Spring-如何自定义扫描包下面的接口_第4张图片

  • 接口的话会返回false;如果返回false就不能加到候选组件里面 

Spring-如何自定义扫描包下面的接口_第5张图片

  • metada.isIndependent  顶级类,嵌套类,静态内部类
  • metada.isConcrete  非接口,非抽象类
  • metada.isAbstract   抽象类 

 针对上述过程分析

思路

主要解决一下问题

  • 什么时候开始扫描
    • 通过上述分析我们不难看到扫描包加载Bean定义是通过
      BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistryConfigurationClassPostProcessor#processConfigBeanDefinitions
      ClassPathBeanDefinitionScanner#doScan
    • 需要自定义一个BeanDefinitionRegistryPostProcessor和ClassPathBeanDefinitionScanner
  • 自定义扫描器必须能够包含接口          
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
   for (TypeFilter tf : this.excludeFilters) {
      if (tf.match(metadataReader, getMetadataReaderFactory())) {
         return false;
      }
   }
   //自定义扫描器,会在这里循环遍历所有的扫描器
   for (TypeFilter tf : this.includeFilters) {
      if (tf.match(metadataReader, getMetadataReaderFactory())) {
         return isConditionMatch(metadataReader);
      }
   }
   return false;
}
  • 判断是否是候选组件的时候,接口必须能够通过

扫描的过程中

  • isCandidateComponent(metadataReader)
    • 确定给定类是否与任何排除筛选器不匹配
    • 是否与至少一个包含筛选器匹配。
    • 如果包含筛选器匹配未能匹配上只能返回false,所以需要自定义包含筛选器匹配(重点)
  • isCandidateComponent(sbd) 
    • 判断是否是候选组件的时候,接口必须能够通过

代码演示

  • 本质和spring整合mybatis原理一样(dao同样是接口)

只牵涉到springIOC的过程,动态代理那一块会省略部分步骤

package com.nieyp;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;

public class CustomBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
    private String packages;

    public CustomBeanDefinitionRegistryPostProcessor(String packages) {
        this.packages = packages;
    }

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        new CustomClassPathBeanDefinitionScanner(registry).doScan(packages);
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

    }
}
package com.nieyp;

import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.context.annotation.ScannedGenericBeanDefinition;
import org.springframework.core.type.ClassMetadata;

import java.util.Set;

public class CustomClassPathBeanDefinitionScanner extends ClassPathBeanDefinitionScanner {

    public CustomClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry) {
        super(registry);
    }

    /**
     * 如果组件是接口的话返回true
     *
     * AnnotationMetadata metadata = beanDefinition.getMetadata();
     * 		return (metadata.isIndependent() && (metadata.isConcrete() ||
     * 				(metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName()))));
     *
     * 		metadata里面的isxxxx能够判断当前的bean定义的所属类型
     * @param beanDefinition
     * @return
     */
    @Override
    protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
        return beanDefinition.getMetadata().isInterface();
    }
    
    @Override
    protected Set doScan(String... basePackages) {
        addIncludeFilter((metadataReader, metadataReaderFactory) -> {
            ClassMetadata metadata = metadataReader.getClassMetadata();
            if (metadata.isInterface()) {
                return true;
            }
            return false;
        });
        Set beanDefinitionHolders = super.doScan(basePackages);

查看第一步的结果beanDefinitionHolders里面的bean定义:已经扫描到了某一个包下的接口

Spring-如何自定义扫描包下面的接口_第6张图片

 下面会重构bean定义:偷天换日(生成代理对象)

@Override
    protected Set doScan(String... basePackages) {
        //增加包含过滤器,元数据为接口的话,返回true
        addIncludeFilter((metadataReader, metadataReaderFactory) -> {
            ClassMetadata metadata = metadataReader.getClassMetadata();
            if (metadata.isInterface()) {
                return true;
            }
            return false;
        });
        Set beanDefinitionHolders = super.doScan(basePackages);
        for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {


            /** 不能转为RootBeanDefinition
             * RootBeanDefinition beanDefinition = (RootBeanDefinition) beanDefinitionHolder.getBeanDefinition();
             *      class org.springframework.context.annotation.ScannedGenericBeanDefinition cannot be cast to class org.springframework.beans.factory.support.RootBeanDefinition
             */
            ScannedGenericBeanDefinition beanDefinition = (ScannedGenericBeanDefinition) beanDefinitionHolder.getBeanDefinition();
            String beanClassName = beanDefinition.getBeanClassName();
            //重构bean定义
            /**
             * 如果采用definition.getPropertyValues()方式的话,
             *  类似definition.getPropertyValues().add("interfaceType", beanClazz);
             *  则要求在FactoryBean(本应用中即CustomFactoryBean)提供setter方法,否则会注入失败
             *  如果采用definition.getConstructorArgumentValues(),
             *  则FactoryBean中需要提供包含该属性的构造方法,否则会注入失败
             */

            //这一部分可以参考mybatis源码 设置ConstructorArgumentValues()会通过构造器初始化对象
            beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName);
            //这一步不再介绍牵涉到aop的知识,后续会给出详细代码
            beanDefinition.setBeanClass(CustomFactoryBean.class);
            //设置通过类型自动装配,这里采用的是byType方式注入,类似的还有byName等
            beanDefinition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE);
        }
        return beanDefinitionHolders;
    }

你可能感兴趣的:(spring源码,spring)