zoukankan      html  css  js  c++  java
  • Spring源码解析之BeanFactoryPostProcessor(二)

    上一章,我们介绍了在AnnotationConfigApplicationContext初始化的时候,会创建AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner两个对象:

    public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry {
    ……
    	public AnnotationConfigApplicationContext() {
    		this.reader = new AnnotatedBeanDefinitionReader(this);
    		this.scanner = new ClassPathBeanDefinitionScanner(this);
    	}
    ……
    }
    

    我们已经知道AnnotatedBeanDefinitionReader对象创建的大致流程,AnnotatedBeanDefinitionReader是用来注册配置类的。现在我们要来学习ClassPathBeanDefinitionScanner,首先从ClassPathBeanDefinitionScanner的名字我们大概可以知道,这个类是用来扫描BeanDefinition的类路径的,那么,我们要怎么使用这个类来扫描类路径呢?来看下面的测试用例:

        @Test
        public void test04() {
            AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
            ac.scan("org.example.service");
            ac.refresh();
            System.out.println(ac.getBean("orderService"));
        }
    

        

    运行结果:

    org.example.service.OrderService@4ba2ca36
    

      

    上面的测试用例,我们不再像之前在配置类上用@ComponentScan标记要扫描的类路径,并将配置类作为参数传给AnnotationConfigApplicationContext创建对象。而是在调用AnnotationConfigApplicationContext无参构造方法创建对象后,再调用ac.scan(String... basePackages)将类路径传入,而ac.scan(String... basePackages)方法也是调用scanner.scan(String... basePackages)方法来完成类的扫描。

    public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry {
    ……
    	private final ClassPathBeanDefinitionScanner scanner;
    …… @Override public void scan(String... basePackages) { Assert.notEmpty(basePackages, "At least one base package must be specified"); this.scanner.scan(basePackages); } …… }

      

    <1>和<2>两块代码最终效果看起来一样,都能扫描我们设定的路径,根据类生成BeanDefinition再生成bean,可能有人会怀疑,扫描用@ComponentScan所标记的类路径,是否是AnnotationConfigApplicationContext的scanner对象? 这里笔者可以告诉大家:扫描@ComponentScan标记的类路径需要用到ClassPathBeanDefinitionScanner类,但并非用AnnotationConfigApplicationContext的scanner对象,而是在代码的某处创建了ClassPathBeanDefinitionScanner对象再调用scan(String... basePackages)方法扫描@ComponentScan标记的路径,至于是哪里创建新的ClassPathBeanDefinitionScanner对象再扫描@ComponentScan标记的路径后面会讲,只是这里我们要知道AnnotationConfigApplicationContext的scanner对象仅仅用来帮助我们添加扫描路径,而实际的开发中很少用到。

    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(MyConfig.class);//<1>
    ————————————————————————————————————————————————————————————————————————————————————————————————
    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();//<2>
    ac.scan("org.example.service");
    ac.refresh();
    

      

    下面,我们来看看ClassPathBeanDefinitionScanner的scan(String... basePackages)方法都做了些什么:

    public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateComponentProvider {
    
    	private final BeanDefinitionRegistry registry;
    	……
    	public int scan(String... basePackages) {
    		int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
    
    		doScan(basePackages);//<1>
    
    		// Register annotation config processors, if necessary.
    		if (this.includeAnnotationConfig) {
    			AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);//<2>
    		}
    
    		return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
    	}
    	protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
    		Assert.notEmpty(basePackages, "At least one base package must be specified");
    		Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
    		for (String basePackage : basePackages) {
    			Set<BeanDefinition> candidates = findCandidateComponents(basePackage);//<3>
    			for (BeanDefinition candidate : candidates) {
    				ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
    				candidate.setScope(scopeMetadata.getScopeName());
    				String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);//<4>
    				if (candidate instanceof AbstractBeanDefinition) {
    					postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
    				}
    				if (candidate instanceof AnnotatedBeanDefinition) {
    					AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);//<5>
    				}
    				if (checkCandidate(beanName, candidate)) {
    					BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);//<6>
    					definitionHolder =
    							AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
    					beanDefinitions.add(definitionHolder);
    					registerBeanDefinition(definitionHolder, this.registry);//<7>
    				}
    			}
    		}
    		return beanDefinitions;
    	}
    	……
    }
    

      

    • 进入到scan(String... basePackages)方法后,会在<1>处将要扫描的类路径传递给doScan(String... basePackages),由doScan(String... basePackages)代替其完成扫描。
    • includeAnnotationConfig字段默认为true,会进入到AnnotationConfigUtils.registerAnnotationConfigProcessors(BeanDefinitionRegistry registry)方法,这个方法在上一章有讲过,会预先注册一些BeanDefinition到BeanDefinitionRegistry。
    • 进入到doScan(String... basePackages)后,在<3>处会调用父类ClassPathScanningCandidateComponentProvider的findCandidateComponents(String basePackage)方法,这个方法可以针对我们传入的一个类路径,扫描路径下所有组件并返回其BeanDefinition,这里返回的是BeanDefinition的集合。
    • 在<3>处拿到BeanDefinition集合后会循环每一个BeanDefinition,在<4>处用beanName生成器根据BeanDefinition生成beanName。
    • 在代码<5>处会判断BeanDefinition能否转型成AnnotatedBeanDefinition(注解BeanDefinition),如果可以则会将BeanDefinition传入到AnnotationConfigUtils.processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd),在这个方法中会获取AnnotatedBeanDefinition的元信息并设置其属性,比如这个类是否标记了@Lazy、@Primary、@DependsOn、@Description……等。
    • 最后,在<6>处会根据BeanDefinition和beanName生成一个BeanDefinitionHolder对象,在<7>处将BeanDefinitionHolder和registry对象传入registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)方法,将BeanDefinition和beanName注册到registry。

    从上面的代码我们得知,spring扫描类路径是调用ClassPathScanningCandidateComponentProvider.findCandidateComponents(String basePackage)方法,所以我们继续追踪到这个方法:

    	public Set<BeanDefinition> findCandidateComponents(String basePackage) {
    		if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
    			return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
    		}
    		else {
    			return scanCandidateComponents(basePackage);//<1>
    		}
    	}
    	
    	private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
    		Set<BeanDefinition> candidates = new LinkedHashSet<>();
    		try {
    			String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
    					resolveBasePackage(basePackage) + '/' + this.resourcePattern;//<2>
    			Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);//<3>
    			……
    			for (Resource resource : resources) {
    				if (resource.isReadable()) {
    					try {
    						MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);//<4>
    						if (isCandidateComponent(metadataReader)) {//<5>
    							ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);//<6>
    							sbd.setSource(resource);//<7>
    							if (isCandidateComponent(sbd)) {//<8>
    								candidates.add(sbd);
    							}
    						}
    						……
    					}
    					catch (Throwable ex) {
    						throw new BeanDefinitionStoreException(
    								"Failed to read candidate component class: " + resource, ex);
    					}
    				}
    			}
    		}
    		catch (IOException ex) {
    			throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
    		}
    		return candidates;
    	}
    

      

    • 在findCandidateComponents(String basePackage)方法中,一般是调用<1>处的代码进行类扫描,即else分支,if分支一般是使用spring注解@Index进行扫描性能的提升,否则不会进入。
    • 在scanCandidateComponents(String basePackage)方法的<2>处会根据类路径生成一个spring自定义的表达式packageSearchPath,在<3>处spring可以解析这个表达式并返回一个Resource数组,每个Resource元素都代表类路径下的一个class文件的路径。
    • 之后会循环每个Resource元素,在<4>处获取其元数据,并在<5>处判断通过父类ClassPathScanningCandidateComponentProvider的isCandidateComponent(MetadataReader metadataReader)方法判断Resource元素所对应的class是否是一个组件,比如标记了@Component、@Service、@Controller……等。
    • 通过<5>处的判断如果一个类是一个组件,在<6>处会根据元数据生成一个ScannedGenericBeanDefinition对象,这里我们又看到一个BeanDefinition的实现,然后在<7>处设置BeanDefinition的元信息,即:sbd.setSource(resource),之前说过,一个对象的元信息是类,一个类的元信息是类文件路径,而BeanDefinition是用于描述类的,所以它的元信息也是类文件路径。
    • 最后,会在<7>处调用父类ClassPathScanningCandidateComponentProvider的重载方法isCandidateComponent(AnnotatedBeanDefinition beanDefinition),在这个方法决定描述类的BeanDefinition是否有资格加入到<8>处candidates集合,什么样的类才可以加入到candidates集合呢?比如:这个类是一个顶级类或者静态嵌套内部类,这个类不需要借助其他类来构造实例;或者这个类并不是一个接口类,或者这个类是个抽象类,但内部方法有用@Lookup注解来标记。这里不理解的不要心急,下面还会讲到ClassPathScanningCandidateComponentProvider的isCandidateComponent(MetadataReader metadataReader)和isCandidateComponent(AnnotatedBeanDefinition beanDefinition)这两个重载方法。

    现在,我们来调试一下上面的代码,看看packageSearchPath和resources的内容,首先我们来看我们类路径下的文件:

     D:Fjava_spacespring-sourcespring-beanFactoryPostProcessor	argetclassesorgexampleservice 的目录
    
    2020/11/19  08:22    <DIR>          .
    2020/11/19  08:22    <DIR>          ..
    2020/11/19  08:22               490 HelloService$BarService.class
    2020/11/19  08:22               609 HelloService$FooService.class
    2020/11/19  08:22               318 HelloService$Hello.class
    2020/11/19  08:22               524 HelloService.class
    2020/11/19  08:22               559 OrderService.class
    2020/11/19  08:22             1,783 Test1BeanFactoryPostProcessor.class
    2020/11/19  08:22               555 UserService.class
                   7 个文件          4,838 字节
                   2 个目录 102,708,711,424 可用字节
    

    然后调试进入上面的代码,可以看到packageSearchPath的内容为:classpath*:org/example/service/**/*.class,之前说过,这里spring会自定义表达式,通过表达式可以扫描这个路径下的类文件。

    现在我们来看下ClassPathScanningCandidateComponentProvider的isCandidateComponent(MetadataReader metadataReader)和isCandidateComponent(AnnotatedBeanDefinition beanDefinition)两个重载方法的实现:

    public class ClassPathScanningCandidateComponentProvider implements EnvironmentCapable, ResourceLoaderAware {
    	……
    	private final List<TypeFilter> includeFilters = new LinkedList<>();
    
    	private final List<TypeFilter> excludeFilters = new LinkedList<>();
    	……
    	protected void registerDefaultFilters() {
    		this.includeFilters.add(new AnnotationTypeFilter(Component.class));//<1>
    		ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
    		try {
    			this.includeFilters.add(new AnnotationTypeFilter(
    					((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));
    			logger.trace("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");
    		}
    		catch (ClassNotFoundException ex) {
    			// JSR-250 1.1 API (as included in Java EE 6) not available - simply skip.
    		}
    		try {
    			this.includeFilters.add(new AnnotationTypeFilter(
    					((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false));
    			logger.trace("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
    		}
    		catch (ClassNotFoundException ex) {
    			// JSR-330 API not available - simply skip.
    		}
    	}
    	……
    	protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
    		for (TypeFilter tf : this.excludeFilters) {//<2>
    			if (tf.match(metadataReader, getMetadataReaderFactory())) {
    				return false;
    			}
    		}
    		for (TypeFilter tf : this.includeFilters) {//<3>
    			if (tf.match(metadataReader, getMetadataReaderFactory())) {
    				return isConditionMatch(metadataReader);
    			}
    		}
    		return false;
    	}
    	
    	protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
    		AnnotationMetadata metadata = beanDefinition.getMetadata();
    		return (metadata.isIndependent() && (metadata.isConcrete() ||
    				(metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName()))));//<4>
    	}
    	……
    }
    

      

    • 在创建ClassPathScanningCandidateComponentProvider对象时,一般会调用到registerDefaultFilters()方法,在这个方法中会往includeFilters字段加入需要扫描的注解,如:在<1>处加入对标记了@Component类的过滤。
    • isCandidateComponent(MetadataReader metadataReader)在<2>和<3>会按照我们在@CommponScan设定的excludeFilters和includeFilters来过滤要扫描的类。
    • <4>处的isCandidateComponent(AnnotatedBeanDefinition beanDefinition)会获取beanDefinition的元数据,根据元数据判断这个beanDefinition是否是一个候选组件,比如:metadata.isIndependent()要求一个类必须是顶级类或者是静态内部嵌套类,即这个类不需要依赖其他类来生成,而内部嵌套类必须依赖外部类来生成对象;metadata.isConcrete()判断一个类是否是抽象类或者接口;metadata.isAbstract()判断是否是一个抽象类,metadata.hasAnnotatedMethods(Lookup.class.getName())则判断这个类中是否有标记了Lookup的方法。如果一个抽象类中没有标记Lookup的方法,则不能成为候选组件。

    现在,我们来分析下下面的类哪些可以成为候选组件。

    package org.example.service;
    
    import org.springframework.stereotype.Component;
    
    
    public class HelloService {
        @Component
        public class FooService {
        }
    
        @Component
        public static class BarService {
        }
    
        @Component
        public interface Hello {
            void sayHello();
        }
    }
    

      

    • FooService是嵌套内部类,需要依赖HelloService来生成对象,所以它无法成为一个候选组件。
    • BarService是静态嵌套内部类,可以独立生成对象,所以它可以成为一个候选组件。
    • Hello是接口,无法独立生成对象,所以它无法成为一个候选组件。

    现在,我们回到AnnotationConfigApplicationContext(Class<?>... componentClasses)的构造方法:

    public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry {
    
    	private final AnnotatedBeanDefinitionReader reader;
    
    	private final ClassPathBeanDefinitionScanner scanner;
    	……
    	public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
    		this();//<1>
    		register(componentClasses);//<2>
    		refresh();
    	}	
    	……
    	@Override
    	public void register(Class<?>... componentClasses) {
    		Assert.notEmpty(componentClasses, "At least one component class must be specified");
    		this.reader.register(componentClasses);//<3>
    	}
    	……
    }
    

      

    在<1>处,会初始化reader和scanner,我们可以调用AnnotationConfigApplicationContext.scan(String... basePackages)传入类路径,这个方法会进而根据传入的类路径用scanner来扫描组件。当然,我们一般不用这种方式,而是在创建AnnotationConfigApplicationContext对象时将配置类传入,让spring自行读取配置类里的类路径。之前笔者已经大致讲解完<1>处this()方法,现在我们要来学习<2>处的配置类注册方法,如我们所见,<2>处会进而将配置类传给<3>处的reader对象。reader对象会根据配置类生成对应的BeanDefinition注册进spring容器。

    AnnotatedBeanDefinitionReader.register(Class<?>... componentClasses)经过一系列的调用,会来到下面的AnnotatedBeanDefinitionReader.doRegisterBean(...)方法,这里我们又看到一个BeanDefinition的实现——AnnotatedGenericBeanDefinition,这段代码在<1>处将配置类生成一个对应的AnnotatedGenericBeanDefinition,在<2>处生成beanName,在<3>处将beanName和BeanDefinition包装成一个BeanDefinitionHolder对象,最后在<4>处将beanName和BeanDefinition注册进原先的AnnotationConfigApplicationContext对象。

    public class AnnotatedBeanDefinitionReader {
    	……
    	private <T> void doRegisterBean(Class<T> beanClass, @Nullable String name,
    			@Nullable Class<? extends Annotation>[] qualifiers, @Nullable Supplier<T> supplier,
    			@Nullable BeanDefinitionCustomizer[] customizers) {
    
    		AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);//<1>
    		……
    		abd.setScope(scopeMetadata.getScopeName());
    		String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));//<2>
    		……
    		BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);//<3>
    		definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
    		BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);//<4>
    	}
    	……
    }
    

      

    在生成配置类对应的BeanDefinition并注册进spring容器后,AnnotationConfigApplicationContext就会调用父类的refresh()方法,我们先大致看一下refresh()方法:、

    public abstract class AbstractApplicationContext extends DefaultResourceLoader
    		implements ConfigurableApplicationContext {
    	……
    	@Override
    	public void refresh() throws BeansException, IllegalStateException {
    		synchronized (this.startupShutdownMonitor) {
    			……
    			// Tell the subclass to refresh the internal bean factory.
    			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();//<1>
    			……
    			try {
    				……
    				// Invoke factory processors registered as beans in the context.
    				invokeBeanFactoryPostProcessors(beanFactory);//<2>
    				……
    				// Instantiate all remaining (non-lazy-init) singletons.
    				finishBeanFactoryInitialization(beanFactory);//<3>
    				……
    			}
    
    			catch (BeansException ex) {
    				……
    				// Destroy already created singletons to avoid dangling resources.
    				destroyBeans();
    				……
    			}
    			……
    		}
    	}
    }
    

      

    代码<1>处会返回AnnotationConfigApplicationContext父类GenericApplicationContext的beanFactory属性,其类型为DefaultListableBeanFactory。之后将beanFactory传给<2>处和<3>处的方法,从方法注释可以看到,<2>处会调用bean工厂后置处理器,<3>处会用beanFactory来初始化剩余的非懒加载单例对象,即我们编写的dao、service、controller……。

  • 相关阅读:
    复合文字(C99)
    复浮点数(C99)
    字符串的数组形式与指针形式
    《设计模式之禅》学习笔记(十)
    旧关键字的新位置(C99)
    C的存储类、链接和内存管理
    scanf( )函数的格式化输入
    《设计模式之禅》学习笔记(十三)
    yum软件包管理器
    《设计模式之禅》学习笔记(十五)
  • 原文地址:https://www.cnblogs.com/beiluowuzheng/p/13962017.html
Copyright © 2011-2022 走看看