Spring源碼解析之BeanFactoryPostProcessor(二)

上一章,咱們介紹了在AnnotationConfigApplicationContext初始化的時候,會建立AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner兩個對象:java

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

 

咱們已經知道AnnotatedBeanDefinitionReader對象建立的大體流程,AnnotatedBeanDefinitionReader是用來註冊配置類的。如今咱們要來學習ClassPathBeanDefinitionScanner,首先從ClassPathBeanDefinitionScanner的名字咱們大概能夠知道,這個類是用來掃描BeanDefinition的類路徑的,那麼,咱們要怎麼使用這個類來掃描類路徑呢?來看下面的測試用例:spring

    @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)方法來完成類的掃描。bash

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對象僅僅用來幫助咱們添加掃描路徑,而實際的開發中不多用到。app

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

  

下面,咱們來看看ClassPathBeanDefinitionScanner的scan(String... basePackages)方法都作了些什麼:ide

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)方法,因此咱們繼續追蹤到這個方法:post

	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:\F\java_space\spring-source\spring-beanFactoryPostProcessor\target\classes\org\example\service 的目錄

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……。

相關文章
相關標籤/搜索