從servlet容器到Spring mvc 5.1.1.RELEASE IoC 啓動源碼分析

Jetty web容器啓動啓動注意到java

for (ServletContextListener listener : _servletContextListeners)
{
//調用對應配置的listener的contextInitialized方法
    callContextInitialized(listener,event);
    _destroySerletContextListeners.add(listener);
}
複製代碼

容器啓動會執行ServletContextListener的contextInitialized方法,對於Spring來講,它就是執ContextLoaderInitialized方法。web

ContextLoaderListener

它是一個Bootstrap listener,用來啓動和關閉Spring的根WebApplicationContextspring

@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容器交互的類緩存

能夠看到這就是啓動的地方了!bash

首先會檢查是否是已經有root WebApplicationContext,若是存在,就會報錯session

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分爲兩個步驟mvc

  1. 獲取到底使用哪一種類型的class。
    獲取class,能夠在web.xml中經過 ‘contextClass’標籤,定義在context_param中app

    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());
    複製代碼
  2. 初始化。使用獲取對應構造函數執行初始化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容器使用起來,能夠想到,一定會通過以下過程

  1. 建立IoC抽象的配置資源,對於Spring來講,就是程序運行起來須要哪些bean,經過配置去告知框架
  2. 建立一個bean容器,對於Spring來時,他就是BeanFactory的一些實現
  3. 建立一個讀取配置資源的工具,對於用戶指定的配置文件,須要被加載,以Spring來講就是要把配置文件轉成對應的BeanDefinition
  4. 對都的配置資源進行解析,完成載入和註冊

至此開始使用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

  1. 查看以前是否存在BeanFactory,有的話關閉
  2. 建一個新的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;
}
複製代碼
  1. 建立代理,它會填充的默認配置,處理以下
    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));
    }
    
    複製代碼
  2. 代理會去讀到這個文件的命名空間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; } 複製代碼
  1. 初始化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 使用時建議按照自身的語義來分別使用對應的註解
  2. 掃描

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的初始化完畢

整個啓動過程結束。

相關文章
相關標籤/搜索