1. 程式人生 > 其它 >spring 學習-bean建立-scan掃描bean

spring 學習-bean建立-scan掃描bean

概述

文章分析註解方式注入bean 的程式碼過程。本篇將介紹 AnnotationConfigApplicationContext 該類的關於掃描註釋關於 bean 的過程。

前言

我們使用過spring框架 ,知道了生成 bean 的方式可以有 XML 配置檔案, 也可以通過註解。我們分析原始碼前可以思考假如給你一個XML檔案最終要裝配到容器中去,你的步驟應該是如何的 :

  • 讀取
  • 解析
  • 註冊

spring 中也是遵循這個思路的, 我們先看一下 BeanFactory 和 Context 的關係 ,並且瞭解一下他們的繼承關係 , 這兩個類是生成 bean 重要的兩個介面。

註釋方式注入Bean 方式

註解式生成bean的過程,總的來說就是 :

  1. 初始化一個Context

  2. 掃描生成 BeanDefinition

  3. 呼叫 refresh 方法

其中第一個步驟由於是註解類,利用第一步初始化生成的 Scan 類進行掃描特定包下的 bean ,然後根據屬性,例如作用域,單例模式還是模板模式等屬性生成 BeanDefinition ,最後呼叫 上下文 context 父類的refresh 方法,進行呼叫(BeanFactoryPostProcess)後置處理器處理方法和BeanPostProcess處理器處理方法。

Demo 1

    public static void main(String[] args) {
        AnnotationConfigApplicationContext consumerContext = new AnnotationConfigApplicationContext();
        //這裡看到我們註冊了一個 configuration 的類進行配置,然後重新整理一下容器得到最新的結果
        consumerContext.register(ConsumerConfiguration.class);
        consumerContext.refresh();

        //從容器中獲取類
        FooServiceConsumer service = consumerContext.getBean(FooServiceConsumer.class);

        ...

    }   

Demo 2

idea 中new一個新的 spring專案,然後編寫兩個類 。

@Component
public class MyService {
    public String sayHello(){
        return "hello world ";
    }


}


public class SpringTest {

    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext("main");//A
        MyService myService = ctx.getBean(MyService.class);
        String s = myService.sayHello();
        System.out.println("s : "+ s);
    }
}

我們以例子二為原始碼閱讀,從 A 處 debug 進去,看一下 bean 的載入和建立

AnnotationConfigApplicationContext 的建立過程概述

構造方法很清晰 : 初始化,掃描,重新整理三個步驟。

	public AnnotationConfigApplicationContext(String... basePackages) {
		//初始化
        this();
        //掃描包
		scan(basePackages);
        //重新整理 
		refresh();
	}

原始碼分析

需要知道的類 :

  • Resoure 對應讀取操作
  • BeanDefinitionRead 對應解析操作
  • Registry 對應註冊操作。

AnnotationConfigApplicationContext為例子,檢視類圖主要包含三個型別的介面

  • AppContext介面 : 上下文相關
  • Lifecycle介面 : 生命週期相關
  • Registry介面 : 用於註冊 BeanDefinition

AnnotationConfigApplicationContext 的初始化建立的兩個類作用如下 :

  • AnnotatedBeanDefinitionReader :註冊幾個後置處理器
  • ClassPathBeanDefinitionScanner :掃描包內的bean
	public AnnotationConfigApplicationContext(String... basePackages) {
        //初始化
		this();
        //掃描包
		scan(basePackages);
        //重新整理 
		refresh();
	}


	/**
	 * Create a new AnnotationConfigApplicationContext that needs to be populated
	 * through {@link #register} calls and then manually {@linkplain #refresh refreshed}.
     * 
     * 構造方法,從註釋也可以看到,構造完,需要呼叫  register 方法進行填充和 refresh 方法進行重新整理
	 */
	public AnnotationConfigApplicationContext() {
		this.reader = new AnnotatedBeanDefinitionReader(this);
		this.scanner = new ClassPathBeanDefinitionScanner(this);
	}


	//============ 父類初始化  ==============

    //==========================
	//GenericApplicationContext
	public GenericApplicationContext() {
		this.beanFactory = new DefaultListableBeanFactory();
	}


	//AbstractApplicationContext
	public AbstractApplicationContext() {
		this.resourcePatternResolver = getResourcePatternResolver();
	}


	//DefaultResourceLoader
	public DefaultResourceLoader() {
		this.classLoader = ClassUtils.getDefaultClassLoader();
	}

	public static ClassLoader getDefaultClassLoader() {
		ClassLoader cl = null;
		try {
			cl = Thread.currentThread().getContextClassLoader();
		}
		catch (Throwable ex) {
			// Cannot access thread context ClassLoader - falling back...
		}
		if (cl == null) {
			// No thread context class loader -> use class loader of this class.
			cl = ClassUtils.class.getClassLoader();
			if (cl == null) {
				// getClassLoader() returning null indicates the bootstrap ClassLoader
				try {
					cl = ClassLoader.getSystemClassLoader();
				}
				catch (Throwable ex) {
					// Cannot access system ClassLoader - oh well, maybe the caller can live with null...
				}
			}
		}
		return cl;
	}


	//============ 父類初始化  ==============

	//============ AnnotatedBeanDefinitionReader 類初始化  ==============

	
	public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
		Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
		Assert.notNull(environment, "Environment must not be null");
		//儲存父類欄位
		this.registry = registry;
		//步驟一 : 用於解析 @Condition 註解相關
		this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
		//步驟二 : 最終會呼叫 register&registerBeanDefinition方法註冊入幾個預設的 BeanDefinition 
		AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
	}

	


其中步驟二中注入的類包括 :

作用:內部託管配置註釋處理器。
對應的類:ConfigurationClassPostProcessor.class

作用:內部管理的自動注入註解處理器
對應的類:AutowiredAnnotationBeanPostProcessor.class

作用:內部管理的JSR-250註釋處理器
對應的類:CommonAnnotationBeanPostProcessor.class

作用:內部管理的JPA註釋處理器(不一定注入)。
對應的類:PersistenceAnnotationBeanPostProcessor.class

作用:內部管理的@EventListener註釋處理器
對應的類:EventListenerMethodProcessor.class

作用:內部管理的EventListenerFactory。
對應的類:DefaultEventListenerFactory.class

BeanDefinition 是個介面,我們看一下 ClassPathBeanDefinitionScanner 會掃描我們專案中的 bean

該類註解 : 

A bean definition scanner that detects bean candidates on the classpath, registering corresponding bean definitions with a given registry (BeanFactory or ApplicationContext).
Candidate classes are detected through configurable type filters. The default filters include classes that are annotated with Spring's @Component, @Repository, @Service, or @Controller stereotype.

下面我們看一下核心的scan 方法,它的呼叫棧挺長的,我們只需要知道scan 中會進行 :

  • 掃描所有 bean
  • 新增幾個postprocess(後置處理器)作為 beanDefinition ,方便後續的
	

    @Override
	public void scan(String... basePackages) {
		Assert.notEmpty(basePackages, "At least one base package must be specified");
        //使用上面構造方法生成的 scan 物件,執行 scan 方法 
		this.scanner.scan(basePackages);
	}

	public int scan(String... basePackages) {
		int beanCountAtScanStart = this.registry.getBeanDefinitionCount();

		doScan(basePackages);

		// Register annotation config processors, if necessary.
		if (this.includeAnnotationConfig) {
			AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
		}

		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) {
			//這裡會把配置檔案中的 bean 讀取出來,然後進行註冊
			Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
			for (BeanDefinition candidate : candidates) {
				ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
				candidate.setScope(scopeMetadata.getScopeName());
				String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
				if (candidate instanceof AbstractBeanDefinition) {
					//為每個 BeanDefinition 填充作為 BeanDefinition 的屬性值 
					postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
				}
				if (candidate instanceof AnnotatedBeanDefinition) {
					AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
				}
				//檢查這個 BeanDefinition 是否一件存在,因為預設是單例,可以想象得到肯定是去 DefaultListableBeanFactory 中判斷尋找
				if (checkCandidate(beanName, candidate)) {
					//封裝成 BeanDefinitionHolder 物件
					BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
					definitionHolder =
							AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
					beanDefinitions.add(definitionHolder);
					// register 註冊  
					registerBeanDefinition(definitionHolder, this.registry);
				}
			}
		}
		return beanDefinitions;
	}		



	protected void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) {
		//這裡呼叫的一個工具類的靜態方法
        BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry);
	}	

	//BeanDefinitionReaderUtils 類方法 
	public static void registerBeanDefinition(
			BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
			throws BeanDefinitionStoreException {

		// Register bean definition under primary name.
		String beanName = definitionHolder.getBeanName();
		//最終呼叫的是 registry 的 registerBeanDefinition 方法 
		registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

		// Register aliases for bean name, if any.
		String[] aliases = definitionHolder.getAliases();
		if (aliases != null) {
			for (String alias : aliases) {
				registry.registerAlias(beanName, alias);
			}
		}
	}	

繼續看 , 也就是說我們掃描到的類跑到了 registry ,而這個 registry 是在哪裡傳進來的呢?

public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry {

	private final AnnotatedBeanDefinitionReader reader;

	private final ClassPathBeanDefinitionScanner scanner;


	/**
	 * Create a new AnnotationConfigApplicationContext that needs to be populated
	 * through {@link #register} calls and then manually {@linkplain #refresh refreshed}.
	 */
	public AnnotationConfigApplicationContext() {
		StartupStep createAnnotatedBeanDefReader = this.getApplicationStartup().start("spring.context.annotated-bean-reader.create");
		this.reader = new AnnotatedBeanDefinitionReader(this);
		createAnnotatedBeanDefReader.end();
		// context 把自己傳給了我們上面的掃描物件 
		this.scanner = new ClassPathBeanDefinitionScanner(this);
	}
    
    ...
}


	//看一下我們剛才上面分析的掃描物件 
	public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,
			Environment environment, @Nullable ResourceLoader resourceLoader) {

		Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
		this.registry = registry;

		if (useDefaultFilters) {
			registerDefaultFilters();
		}
		setEnvironment(environment);
		setResourceLoader(resourceLoader);
	}
	...
}

ok, scan 方法我們就瞭解這麼多,下篇我們看一下 refresh方法。

總結

該篇文章我們介紹了 AnnotationConfigApplicationContext 的初始化過程,剩下的 refresh 方法將會留到下一篇文章進行講解。

參考資料

參考資料