從Jetty web容器啟動啟動注意到
for (ServletContextListener listener : _servletContextListeners)
{
//呼叫對應配置的listener的contextInitialized方法
callContextInitialized(listener,event);
_destroySerletContextListeners.add(listener);
}
複製程式碼
容器啟動會執行ServletContextListener的contextInitialized方法,對於Spring來說,它就是執ContextLoaderInitialized方法。
ContextLoaderListener
它是一個Bootstrap listener,用來啟動和關閉Spring的根WebApplicationContext
@Overridepublic void contextInitialized(ServletContextEvent event) {
initWebApplicationContext(event.getServletContext());
}
複製程式碼
進入初始化後,可以看到spring專案中啟動的時候,經常看到的啟動時間位置
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
...
servletContext.log("Initializing Spring root WebApplicationContext");
Log logger = LogFactory.getLog(ContextLoader.class);
if (logger.isInfoEnabled()) {
logger.info("Root WebApplicationContext: initialization started");
}
long startTime = System.currentTimeMillis();
…
if (logger.isInfoEnabled()) {
long elapsedTime = System.currentTimeMillis() - startTime;
logger.info("Root WebApplicationContext initialized in " + elapsedTime + " ms");
}
...
}
catch (RuntimeException | Error ex) {
logger.error("Context initialization failed", ex);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
throw ex;
}
複製程式碼
ServletContext: servlet用來和servlet容器互動的類
可以看到這就是啟動的地方了!
首先會檢查是不是已經有root WebApplicationContext
,如果存在,就會報錯
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
throw new IllegalStateException(
"Cannot initialize context because there is already a root application context present - " +
"check whether you have multiple ContextLoader* definitions in your web.xml!");
}
…
// Store context in local instance variable, to guarantee that// it is available on ServletContext shutdown.
if (this.context == null) {
this.context = createWebApplicationContext(servletContext);
}
…
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
…
return this.context;
...
}
複製程式碼
建立一個context分為兩個步驟
-
獲取到底使用哪種型別的class。
獲取class,可以在web.xml中通過 ‘contextClass’標籤,定義在context_param中public static final String CONTEXT_CLASS_PARAM = "contextClass"; … //web.xml中初始化會載入存放到servletContext中 String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM); 複製程式碼
自定義的類必須是ConfigurableWebApplicationContext 的子類,否則丟擲異常
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) { throw new ApplicationContextException("Custom context class [" + contextClass.getName() + "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]"); } 複製程式碼
沒有定義則直接在spring自帶的ContextLoader.properties中取找到對應的預設載入類
//ContextLoader.properties自定義內容如下 org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext ... private static final String DEFAULT_STRATEGIES_PATH = "ContextLoader.properties"; … ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class); defaultStrategies = PropertiesLoaderUtils.loadProperties(resource); ... contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName()); 複製程式碼
-
初始化。使用獲取對應建構函式執行初始化
return instantiateClass(clazz.getDeclaredConstructor());
,預設就是XmlWebApplicationContext
初始化之後,對於剛啟動的專案來說,它肯定需要載入對應的東西,首先會去載入它的父context,這對於spring 5來說,只是返回null。
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
if (!cwac.isActive()) {
if (cwac.getParent() == null) {
//載入父上下文的主要理由是,允許多個 root web application的應用能夠都成為他們所共享的 EAR或者EJB 的子類。如果沒有對應的場景,父上下文並沒有什麼重要性
ApplicationContext parent = loadParentContext(servletContext);
cwac.setParent(parent);
}
//讀取配置更新spring web容器
configureAndRefreshWebApplicationContext(cwac, servletContext);
}
複製程式碼
讀取配置,而配置的來源就是在web.xml中配置的contextConfigLocation
。
String CONFIG_LOCATION_DELIMITERS = ",; \t\n";
...
public static final String CONFIG_LOCATION_PARAM = "contextConfigLocation";
...
sc.getInitParameter(CONFIG_LOCATION_PARAM);
...
setConfigLocations(StringUtils.tokenizeToStringArray(location, CONFIG_LOCATION_DELIMITERS));
…
public static final String GLOBAL_INITIALIZER_CLASSES_PARAM = "globalInitializerClasses";
public static final String CONTEXT_INITIALIZER_CLASSES_PARAM = "contextInitializerClasses”;
…
AnnotationAwareOrderComparator.sort(this.contextInitializers);
for (ApplicationContextInitializer<ConfigurableApplicationContext> initializer : this.contextInitializers) {
//按照順序執行自定義的initializer的初始化,給新建的 webapplicationcontext新增特定的資源
initializer.initialize(wac);
}
複製程式碼
最後真正的開始執行配置的載入與更新,即IOC容器的初始化開始。IOC容器初始化結束之後,設定為root WebApplicationContext
,至此web專案的初始化結束
下面以預設的 XmlWebApplicationContext為例闡述springmvc初始化的過程
IoC的含義
IoC意指控制反轉,對於龐大的專案來說,如果合作的物件引用或依賴關係的管理由具體的物件完成,會導致程式碼的高度耦合和可測試性降低,這種現象的解決之道就是把物件之間的依賴關係注入交給IoC容器來執行,讓IoC容器來處理一代物件之間的關係
Spring本身實現了IoC容器的規範,它的具體實現就是BeanFactory
。
- 獲取bean
- 獲取bean的別名
- 獲取bean的型別
- 校驗bean的型別
- 識別容器是否包含指定的bean
- 判斷bean是singleton還是prototype
要使得IoC容器使用起來,可以想到,必定會經過如下過程
- 建立IoC抽象的配置資源,對於Spring來說,就是程式執行起來需要哪些bean,通過配置去告知框架
- 建立一個bean容器,對於Spring來時,他就是BeanFactory的一些實現
- 建立一個讀取配置資源的工具,對於使用者指定的配置檔案,需要被載入,以Spring來說就是要把配置檔案轉成對應的BeanDefinition
- 對都的配置資源進行解析,完成載入和註冊
至此開始使用IoC容器。Spring為了這整個過程更加的便捷,提供了一個更高階形態的Ioc容器ApplicationContext
- EnvironmentCapable:擁有獲取應用執行環境的能力
- ListableBeanFactory: 擁有列舉所有bean例項的能力
- HierarchicalBeanFactory:能夠獲取父bennFactory的能力 -MessageSource:能夠提供message的引數胡和國際化的能力
- ApplicationEventPublisher:擁有釋出訊息的能力
- ResourcePatternResolver:擁有能夠從給定路徑提取資源並載入資源的能力
在spring的預設啟動中,使用的XmlWebApplicationContext就實現了ApplicationContext介面。
IOC容器XmlWebApplicationContext初始化
繼續回到初始化的流程,它會執行對應的refresh方法,整體過程如下
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
prepareRefresh();
// 新建beanFactory,載入bean配置
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 配置一個beanFactory的標準特徵
prepareBeanFactory(beanFactory);
...
//針對不同的beanFactory,配置不同的特徵
postProcessBeanFactory(beanFactory);
// 呼叫BeanFactory的後置處理器,這些後置處理器都是在bean定義中向容器註冊的
invokeBeanFactoryPostProcessors(beanFactory);
...
// 初始化ApplicationEventMulticaster,如果沒有指定bean ‘applicationEventMulticaster',新建一個SimpleApplicationEventMulticaster,用來給註冊的所有listener釋出事件
initApplicationEventMulticaster();
...
// 把所有實現了ApplicationListener的介面當做listener,新增到ApplicationEventMulticaster的listener中
registerListeners();
// 初始化所有的非懶載入的bean
finishBeanFactoryInitialization(beanFactory);
// 清除資源的快取,初始化 lifecycleProcessor,如果沒有bean 'lifecycleProcessor’存在,預設新建DefaultLifecycleProcessor,並啟動,最後釋出ContextRefreshedEvent,如果需要處理MBEAN相關,註冊進MBEAN server
finishRefresh();
….
}finally {
// 清除掉之前用的所有快取,包括反射、註解、類載入
resetCommonCaches();
}
}
}
複製程式碼
prepareRefresh
設定啟動的時間,標識已經啟動了,對於xmlWebApplicationContext來說, 會把對應的Servlet配置替換成整整的servletContext和servletConfig例項,並校驗已經標為必須要有的properties是能夠獲取到的
public static final String SERVLET_CONTEXT_PROPERTY_SOURCE_NAME = "servletContextInitParams";
public static final String SERVLET_CONFIG_PROPERTY_SOURCE_NAME = "servletConfigInitParams”;
...
String name = StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME;
...
sources.replace(name, new ServletContextPropertySource(name, servletContext));
...
name = StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME;
...
sources.replace(name, new ServletConfigPropertySource(name, servletConfig));
...
複製程式碼
obtainFreshBeanFactory
- 檢視之前是否存在BeanFactory,有的話關閉
- 建一個新的beanFactory.載入對應的Bean資源
建立beanFactory核心如下
new DefaultListableBeanFactory(getInternalParentBeanFactory());
…
loadBeanDefinitions(beanFactory);
…
複製程式碼
開始執行載入
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
//載入xml的類
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
...
loadBeanDefinitions(beanDefinitionReader);
}
複製程式碼
載入配置檔案,首先就是要獲取配置檔案的位置,
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
...
//它就是web.xml中配置的“contextConfigLocation”
String[] configLocations = getConfigLocations();
if (configLocations != null) {
reader.loadBeanDefinitions(configLocations);
}
}
複製程式碼
然後通過XmlBeanDefinitionReader依次載入所有的配置檔案
//AbstractBeanDefinitonReader.loadBeanDefinitions
//首先是根據路徑去獲取對應的檔案,轉為Resource
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
//然後載入進來
int count = loadBeanDefinitions(resources);
...
複製程式碼
檔案查詢的方法
以PathMatchingResourcePatternResolver為例
String CLASSPATH_ALL_URL_PREFIX = "classpath*:";
...
public Resource[] getResources(String locationPattern) throws IOException {
//找到對應的字首,classpath
if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) {
if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) {
//匹配 classpath*:開始查詢
return findPathMatchingResources(locationPattern);
}
else {
//去掉字首開始查詢
return findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()));
}
}else{
//對於不是war開頭的,找到對應的冒號座標
int prefixEnd = (locationPattern.startsWith("war:") ? locationPattern.indexOf("*/") + 1 :
locationPattern.indexOf(':') + 1);
if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) {
//符合Ant風格的方式,開始查詢
return findPathMatchingResources(locationPattern);
}
。。。
}
}
複製程式碼
具體查詢檔案的方式為
protected Set<Resource> doFindAllClassPathResources(String path) throws IOException {
Set<Resource> result = new LinkedHashSet<>(16);
ClassLoader cl = getClassLoader();
//通過classLoader去查詢對應的檔案
Enumeration<URL> resourceUrls = (cl != null ? cl.getResources(path) : ClassLoader.getSystemResources(path));
while (resourceUrls.hasMoreElements()) {
URL url = resourceUrls.nextElement();
result.add(convertClassLoaderURL(url));
}
...
return result;
}
複製程式碼
載入檔案
找到檔案的位置之後,開始載入檔案
//XmlBeanDefinitionReader.doLoadBeanDefinitions
…
//使用DefaultDocumentLoader來載入
Document doc = doLoadDocument(inputSource, resource);
//載入到檔案便開始註冊bean
int count = registerBeanDefinitions(doc, resource);
...
複製程式碼
註冊bean核心內容如下
protected void doRegisterBeanDefinitions(Element root) {
BeanDefinitionParserDelegate parent = this.delegate;
//建立一個委託類,並填充預設的配置到DocumentDefaultsDefinition,比如 lazy-init,autowire等等,然後觸發預設的屬性已經註冊完畢
this.delegate = createDelegate(getReaderContext(), root, parent);
...
parseBeanDefinitions(root, this.delegate);
...
this.delegate = parent;
}
複製程式碼
- 建立代理,它會填充的預設配置,處理如下
protected void populateDefaults(DocumentDefaultsDefinition defaults, @Nullable DocumentDefaultsDefinition parentDefaults, Element root) { //獲取xml檔案的根節點 //獲取 default-lazy-init 屬性 String lazyInit = root.getAttribute(DEFAULT_LAZY_INIT_ATTRIBUTE); if (DEFAULT_VALUE.equals(lazyInit)) { //如果屬性中是預設值:default,如果有父設定,沿用之前的,否則,設定為 false lazyInit = (parentDefaults != null ? parentDefaults.getLazyInit() : FALSE_VALUE); } defaults.setLazyInit(lazyInit); 。。。 //獲取 default-autowire 屬性 String autowire = root.getAttribute(DEFAULT_AUTOWIRE_ATTRIBUTE); if (DEFAULT_VALUE.equals(autowire)) { //如果是預設值 default,就設定為 no autowire = (parentDefaults != null ? parentDefaults.getAutowire() : AUTOWIRE_NO_VALUE); } defaults.setAutowire(autowire); 。。。 //如果存在 default-init-method ,那麼獲取這個預設的init-method並設定成對應的initMethod if (root.hasAttribute(DEFAULT_INIT_METHOD_ATTRIBUTE)) { defaults.setInitMethod(root.getAttribute(DEFAULT_INIT_METHOD_ATTRIBUTE)); } else if (parentDefaults != null) { defaults.setInitMethod(parentDefaults.getInitMethod()); } ... defaults.setSource(this.readerContext.extractSource(root)); } 複製程式碼
- 代理會去讀到這個檔案的名稱空間Uri,對不同的名稱空間,分別解析
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) { String namespaceUri = getNamespaceURI(ele); if (namespaceUri == null) { return null; } NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri); .... return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd)); } 複製程式碼
NamspaceHandler存在各種對應的實現
通過獲取對應的名稱空間,來解析對應的配置
-
ContextNamespaceHandler
public void init() { ... registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser()); //熟悉的componenet-scan registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser()); ... } 複製程式碼
-
TaskNamespaceHandler
public void init() { this.registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser()); this.registerBeanDefinitionParser("executor", new ExecutorBeanDefinitionParser()); this.registerBeanDefinitionParser("scheduled-tasks", new ScheduledTasksBeanDefinitionParser()); this.registerBeanDefinitionParser("scheduler", new SchedulerBeanDefinitionParser()); } 複製程式碼
-
UtilNamespaceHandler
public void init() { registerBeanDefinitionParser("constant", new ConstantBeanDefinitionParser()); registerBeanDefinitionParser("property-path", new PropertyPathBeanDefinitionParser()); registerBeanDefinitionParser("list", new ListBeanDefinitionParser()); registerBeanDefinitionParser("set", new SetBeanDefinitionParser()); registerBeanDefinitionParser("map", new MapBeanDefinitionParser()); registerBeanDefinitionParser("properties", new PropertiesBeanDefinitionParser()); } 複製程式碼
從這裡可以看到,遇到對應的標籤名字,分別對應不同的解析器來解析對應的內容。
解析xml檔案的標籤
以ComponentScanBeanDefinitionParser
為例,解析過程如下
String CONFIG_LOCATION_DELIMITERS = ",; \t\n”;
。。。
public BeanDefinition parse(Element element, ParserContext parserContext) {
//獲取basepackage標籤
String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);
basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);
//多個包可以按照分隔符開區分
String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
//初始化scanner
ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
//執行掃描有bean定義的地方,它會掃描到包下面所有的註冊檔案
Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
//處理一些後置處理器,並觸發通知
registerComponents(parserContext.getReaderContext(), beanDefinitions, element);
return null;
}
複製程式碼
-
初始化scanner
protected ClassPathBeanDefinitionScanner configureScanner(ParserContext parserContext, Element element) { boolean useDefaultFilters = true; //使用預設scanner要掃描的註解 if (element.hasAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE)) { //節點上可以配置是否使用 useDefaultFilters = Boolean.valueOf(element.getAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE)); } //初始化 ClassPathBeanDefinitionScanner scanner = createScanner(parserContext.getReaderContext(), useDefaultFilters); ... } //scanner建構函式 public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters, Environment environment, @Nullable ResourceLoader resourceLoader) { …. if (useDefaultFilters) { //註冊預設要使用的註解 registerDefaultFilters(); } ... } protected void registerDefaultFilters() { //使用包含了Commponent的 this.includeFilters.add(new AnnotationTypeFilter(Component.class)); ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader(); 。。。 //包含了註解 javax.annotation.ManagedBean this.includeFilters.add(new AnnotationTypeFilter( ((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false)); 。。。 //包含了註解 javax.inject.Named this.includeFilters.add(new AnnotationTypeFilter( ((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false)); 。。。 } 複製程式碼
對應從原始碼上可以看到Component註解存在一下幾個別名
- Controller:它是一種特殊的Component,使得用了這個註解的類能夠被自動的通過路徑掃描掃描到,經常與RequestMapping一起使用
- Repository:本身是起源與DDD設計模式,當然對於傳統的Java的DAO層也是適用的。
- Service:本身起源與DDD,同樣適用於業務服務層 -configuration:他表示當前的類會有多個Bean註解的方法,Spring容器會來自動產生它bean的定義,服務在執行時會需要用到這些bean 使用時建議按照自身的語義來分別使用對應的註解
-
掃描
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
...
for (String basePackage : basePackages) {
//根據初始化的過濾器,獲取整個包下面的有對應註解的類,作為bean的定義返回
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
for (BeanDefinition candidate : candidates) {
。。。
if (checkCandidate(beanName, candidate)) {
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
...
//註冊bean
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
...
}
複製程式碼
註冊是通過BeanDefinitionReaderUtils.registerBeanDefinition
工具實現,核心方法為
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
...
BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
if (existingDefinition != null) {
...
this.beanDefinitionMap.put(beanName, beanDefinition);
}
else {
if (hasBeanCreationStarted()) {
synchronized (this.beanDefinitionMap) {
//將bean放入到beanDefinitionMap中,它實際就是一個ConcurrentHashMap
this.beanDefinitionMap.put(beanName, beanDefinition);
...
}
}
else {
// Still in startup registration phase
this.beanDefinitionMap.put(beanName, beanDefinition);
...
}
this.frozenBeanDefinitionNames = null;
}
...
}
複製程式碼
至此bean註冊到beanFactory結束
prepareBeanFactory
配置一個beanFactory的標準特徵,比如類載入器,後置處理器等,這些都是bean宣告週期中必須存在的
...
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
...
beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
...
複製程式碼
postProcessBeanFactory
設定beanFactory的後置處理器。對於xmlwebapplicationcontext,會繼續載入Servlet相關,以及web請求的作用域(request/session)等等
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
...
beanFactory.ignoreDependencyInterface(ServletContextAware.class);
beanFactory.ignoreDependencyInterface(ServletConfigAware.class);
...
}
複製程式碼
bean宣告週期的全部初始化方法和標準順序如下,執行到對應的位置,會呼叫對應的方法
- BeanNameAware's {@code setBeanName}
- BeanClassLoaderAware's {@code setBeanClassLoader}
- BeanFactoryAware's {@code setBeanFactory}
- EnvironmentAware's {@code setEnvironment}
- EmbeddedValueResolverAware's {@code setEmbeddedValueResolver}
- ResourceLoaderAware's {@code setResourceLoader}(only applicable when running in an application context)
- ApplicationEventPublisherAware's {@code setApplicationEventPublisher}(only applicable when running in an application context)
- MessageSourceAware's {@code setMessageSource}(only applicable when running in an application context)
- ApplicationContextAware's {@code setApplicationContext}(only applicable when running in an application context)
- ServletContextAware's {@code setServletContext}(only applicable when running in a web application context)
- {@code postProcessBeforeInitialization} methods of BeanPostProcessors
- InitializingBean's {@code afterPropertiesSet}
- a custom init-method definition
- {@code postProcessAfterInitialization} methods of BeanPostProcessors
finishBeanFactoryInitialization
初始化所有剩下的非懶載入的bean
String FACTORY_BEAN_PREFIX = "&”;
…
for (String beanName : beanNames) {
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
//對於有懶載入標記的,都不初始化
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
if (isFactoryBean(beanName)) {
//單獨處理FactoryBean,工廠bean的會自動新增一個字首 &
Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
if (bean instanceof FactoryBean) {
final FactoryBean<?> factory = (FactoryBean<?>) bean;
...
//需要初始化,則執行初始化
getBean(beanName);
...
}
}
else {
//非工廠bean的初始化
getBean(beanName);
}
}
}
...
複製程式碼
getBean實現在AbstractBeanFactory的doGetBean方法
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
final String beanName = transformedBeanName(name);
//這個bean本身
Object bean;
//bean工廠
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
//bean工廠存在。
...
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
else {
if (isPrototypeCurrentlyInCreation(beanName)) {
//有個相同名字的bean正在建立,這裡就可以看到迴圈引用的一個提示了~ `Requested bean is currently in creation: Is there an unresolvable circular reference?`
throw new BeanCurrentlyInCreationException(beanName);
}
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
//存在父工廠,就從它這兒獲取bean
}
if (!typeCheckOnly) {
//標記開始建立bean了
markBeanAsCreated(beanName);
}
...
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
//獲取這個bean依賴的bean
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dep : dependsOn) {
if (isDependent(beanName, dep)) {
//如果正在建立的bean和它依賴的bean存在依賴關係,說明有迴圈引用
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
registerDependentBean(dep, beanName);
try {
//獲取依賴的bean
getBean(dep);
}
catch (NoSuchBeanDefinitionException ex) {
//如果依賴的bean不存在,那麼就會丟擲bean依賴不存在的異常
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
}
}
}
//最後建立bean本身,根據bean的作用域的不同,建立方式各異
if (mbd.isSingleton()) {
//對於單例,使用synchronized 方法鎖定真個方法,使它同步,然後從快取中檢視bean是否存在,不存在通過傳入的工廠函式獲取能夠產生bean的對應的工廠,這裡的函式式就是工廠,產生bean的工廠方法就是createBean
sharedInstance = getSingleton(beanName, () -> {
。。。
return createBean(beanName, mbd, args);
。。。
});
//從工廠中獲取bean
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
else if (mbd.isPrototype()) {
//如果是prototype則每次建立一個新的bean
Object prototypeInstance = null;
try {
beforePrototypeCreation(beanName);
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
else {
String scopeName = mbd.getScope();
final Scope scope = this.scopes.get(scopeName);
if (scope == null) {
//如果配置的bean的作用域有問題,則丟擲異常,無法識別的bean scop
throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
}
//執行對應scope的初始化
Object scopedInstance = scope.get(beanName, () -> {
beforePrototypeCreation(beanName);
try {
return createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
});
...
}
}
...
}
...
}
return (T) bean;
}
複製程式碼
FactoryBean
對於實現它的類,都是作為一個工廠來使用。它的例項包括 ProxyFactoryBean,用來作為AOP的代理bean生產者
在bean的建立過程中,方法 getObjectForBeanInstance
這是bean與bean工廠在IoC啟動容器的過程中產生交匯
protected Object getObjectForBeanInstance(
Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
//beanINstance表示bean的例項,name可能包含原始的工廠字首,beanName純粹bean的名字
。。。
if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
//如果name表明,它本身是一個工廠,那麼這個工廠本身就是需要建立的bean
return beanInstance;
}
//走到這裡,說明要建立的bean不是一個工廠,從工廠中建立
Object object = null;
if (mbd == null) {
//從快取中獲取工廠bean
object = getCachedObjectForFactoryBean(beanName);
}
if (object == null) {
//快取中沒有,將工廠向上轉型
FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
if (mbd == null && containsBeanDefinition(beanName)) {
mbd = getMergedLocalBeanDefinition(beanName);
}
boolean synthetic = (mbd != null && mbd.isSynthetic());
//從FactoryBean中獲取bean
object = getObjectFromFactoryBean(factory, beanName, !synthetic);
}
return object;
}
複製程式碼
從FactoryBean中獲取bean,它的核心實現就是執行 object = factory.getObject();
,比如在上面初始化過程中,就是呼叫匿名工廠bean的方法,裡面就是呼叫了creatBean方法,對應的代理類,則執行代理類的getObject方法
建立bean
建立bean過程實現在它的子類AbstractAutowireCapableBeanFactory中
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
。。。
//先嚐試從初始化之前的處理器獲得bean
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if (bean != null) {
return bean;
}
…
//建立例項,如果有工廠就從建立,否則使用建構函式,autowire等等方式構建
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
...
}
複製程式碼
至此bean的初始化完畢
整個啟動過程結束。