首页 > 代码库 > 源码跟读,Spring是如何解析和加载xml中配置的beans

源码跟读,Spring是如何解析和加载xml中配置的beans

Spring版本基于:
技术分享

跟踪代码源码基于:
https://github.com/deng-cc/KeepLearning
commit id:c009ce47bd19e1faf9e07f12086cd440b7799a63
 

1、配置启动Spring所需的监听器

web.xml中配置监听器
  1. <listener>
  2. <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  3. </listener>
这是一个典型的 ServletContextListener,Servlet 容器(如 Tomcat 等)在启动时会找到 ContextLoaderListener 并执行其 contextInitialized(ServletContextEvent event) 方法。

  1. ContextLoaderListener.java extends ContextLoader implements ServletContextListener

  2. public void contextInitialized(ServletContextEvent event) {
  3. this.contextLoader = createContextLoader();
  4. if (this.contextLoader == null) {
  5. this.contextLoader = this;
  6. }
  7. this.contextLoader.initWebApplicationContext(event.getServletContext());
  8. }
从这里开始,Spring 将会进行 Bean Definition的解析、Bean Processors 设置和处理、Beans 实例化等工作。从而将程序会用到的 Java 对象定义并根据该定义创建好,提供给开发人员去使用。 

这里 Spring 首先需要处理的就是 Bean 的定义。经过不断的发展和演化,Bean 的定义方式有:
  • 基于 XML 文件的配置方式
  • 基于 Annotation 的配置方式
  • 基于 Java Code 的配置方式
  • 用户自定义的配置方式


这里就基于 XML 配置 Bean Definition 的源码进行解读学习

2、监听器都做了些什么?

Servlet 容器启动时如果 web.xml 配置了 ContextLoaderListener,则会调用该对象的初始化方法。根据 Java 语法规定,ContextLoaderListener 的父类 ContextLoader 有一段 static 的代码会更早被执行。
  1. ContextLoader.java

  2. static {
  3. // Load default strategy implementations from properties file.
  4. // This is currently strictly internal and not meant to be customized
  5. // by application developers.
  6. try {
  7. ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class); 
  8. //private static final String DEFAULT_STRATEGIES_PATH = "ContextLoader.properties";
  9. defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
  10. }
  11. catch (IOException ex) {
  12. throw new IllegalStateException("Could not load ‘ContextLoader.properties‘: " + ex.getMessage());
  13. }
  14. }

这里的ContextLoader.properties(spring-web-x.x.x.RELEASE.jar --> org.springframework.web.context.support --> ContextLoader.properties)内容为
  1. ContextLoader.properties

  2. org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext

这段代码配置了 XML 默认使用的 Context 为 org.springframework.web.context.WebApplicationContext = org.springframework.web.context.support.XmlWebApplicationContext。根据该定义,如果开发人员没有从 web.xml 指定 contextClass 参数,则默认使用 XmlWebApplicationContext 作为 root WebApplicationContext 工具类。 

好了,我们回到刚才的核心方法 initWebApplicationContext() 中去,在这个方法中,有个核心方法
  1. ContextLoader.java

  2. // Store context in local instance variable, to guarantee that
  3. // it is available on ServletContext shutdown.
  4. this.context = createWebApplicationContext(servletContext, parent);

我们再继续往里探索
  1. ContextLoader.java

  2. protected WebApplicationContext createWebApplicationContext(ServletContext sc, ApplicationContext parent) {
  3. //step1
  4. Class<?> contextClass = determineContextClass(sc);
  5. if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
  6. throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
  7. "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
  8. }
  9. //step2
  10. ConfigurableWebApplicationContext wac =
  11. (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
  12. // Assign the best possible id value.
  13. if (sc.getMajorVersion() == 2 && sc.getMinorVersion() < 5) {
  14. // Servlet <= 2.4: resort to name specified in web.xml, if any.
  15. String servletContextName = sc.getServletContextName();
  16. wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
  17. ObjectUtils.getDisplayString(servletContextName));
  18. }
  19. else {
  20. // Servlet 2.5‘s getContextPath available!
  21. try {
  22. String contextPath = (String) ServletContext.class.getMethod("getContextPath").invoke(sc);
  23. wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
  24. ObjectUtils.getDisplayString(contextPath));
  25. }
  26. catch (Exception ex) {
  27. throw new IllegalStateException("Failed to invoke Servlet 2.5 getContextPath method", ex);
  28. }
  29. }
  30. //step3
  31. wac.setParent(parent);
  32. wac.setServletContext(sc);
  33. wac.setConfigLocation(sc.getInitParameter(CONFIG_LOCATION_PARAM));
  34. customizeContext(sc, wac);
  35. //step4
  36. wac.refresh();
  37. return wac;
  38. }

3、createWebApplicationContext

3.1 //step1

Class<?> contextClass = determineContextClass(sc);
  1. ContextLoader.java

  2. protected Class<?> determineContextClass(ServletContext servletContext) {
  3. String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM); //CONTEXT_CLASS_PARAM = "contextClass"
  4. if (contextClassName != null) {
  5. try {
  6. return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
  7. }
  8. catch (ClassNotFoundException ex) {
  9. throw new ApplicationContextException(
  10. "Failed to load custom context class [" + contextClassName + "]", ex);
  11. }
  12. }
  13. else {
  14. contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName()); 
  15. //defaultStrategies = PropertiesLoaderUtils.loadProperties(resource); defaultStrategies也就是静态代码块中初始化的默认工具类XmlWebApplicationContext
  16. try {
  17. return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
  18. }
  19. catch (ClassNotFoundException ex) {
  20. throw new ApplicationContextException(
  21. "Failed to load default context class [" + contextClassName + "]", ex);
  22. }
  23. }
  24. }
首先determineContextClass()方法查明具体的Context类,他会读取servletContext的初始化参数contextClass,此参数我们一般不配置。

所以Spring就会读取跟org.springframework.web.context.WebApplicationContext同一个包下面的ContextLoader.properties文件读取默认设置,反射出org.springframework.web.context.support.XmlWebApplicationContext类来。

以图友网项目为例(添加链接),此时返回的为  return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
技术分享
 

3.2 //step2

ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);

接下来就是通过BeanUtils的方法把新创建的XmlWebApplicationContext进行初始化。
技术分享
注意看类别的变化

3.3 //step3

对于得到的这个 (ConfigurableWebApplicationContext) XmlWebApplicationContext
然后对之中的属性进行一系列的设置,首先会设置一个默认ID,即org.springframework.web.context.WebApplicationContext:+你项目的ContextPath。

然后再设置其他属性
wac.setParent(parent);
wac.setServletContext(sc);

其中还需要设置
wac.setConfigLocation(sc.getInitParameter(CONFIG_LOCATION_PARAM));  
//CONFIG_LOCATION_PARAM = "contextConfigLocation"
//这里设置了你在web.xml中对于applicationContext.xml的地址配置
//e.g.
  1. <context-param>
  2. <param-name>contextConfigLocation</param-name>
  3. <param-value>classpath:applicationContext.xml</param-value>
  4. </context-param>
也就是说,你在配置文件中的那些beans,这时候容器已经知道要准备哪些bean了,虽然还没有进行实例化,就像一个工厂已经得到了产品的加工图纸,但是还没有去加工实际产品出来。
技术分享
 

customizeContext(sc, wac);
接下来就是customizeContext(sc, wac)方法,此方法会根据用户配置的globalInitializerClasses参数来初始化一些用户自定义的属性,一般我们不配置,所以这里什么也不做。

3.4 //step4

wac.refresh();
最后登场的就是最核心的方法了,在这个方法里,会完成资源文件的加载、配置文件解析、Bean定义的注册、组件的初始化等核心工作。

  1. AbstractApplicationContext.java

  2. @Override
  3. public void refresh() throws BeansException, IllegalStateException {
  4. synchronized (this.startupShutdownMonitor) {
  5. // Prepare this context for refreshing.
  6. //此方法做一些准备工作,如记录开始时间,输出日志,initPropertySources();和getEnvironment().validateRequiredProperties();一般没干什么事
  7. prepareRefresh();
  8. // Tell the subclass to refresh the internal bean factory.
  9. //step4.1
  10. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
  11. // Prepare the bean factory for use in this context.
  12. prepareBeanFactory(beanFactory);
  13. try {
  14. // Allows post-processing of the bean factory in context subclasses.
  15. postProcessBeanFactory(beanFactory);
  16. // Invoke factory processors registered as beans in the context.
  17. invokeBeanFactoryPostProcessors(beanFactory);
  18. // Register bean processors that intercept bean creation.
  19. registerBeanPostProcessors(beanFactory);
  20. // Initialize message source for this context.
  21. initMessageSource();
  22. // Initialize event multicaster for this context.
  23. initApplicationEventMulticaster();
  24. // Initialize other special beans in specific context subclasses.
  25. onRefresh();
  26. // Check for listener beans and register them.
  27. registerListeners();
  28. // Instantiate all remaining (non-lazy-init) singletons.
  29. //step4.2
  30. finishBeanFactoryInitialization(beanFactory);
  31. // Last step: publish corresponding event.
  32. finishRefresh();
  33. }
  34. catch (BeansException ex) {
  35. logger.warn("Exception encountered during context initialization - cancelling refresh attempt", ex);
  36. // Destroy already created singletons to avoid dangling resources.
  37. destroyBeans();
  38. // Reset ‘active‘ flag.
  39. cancelRefresh(ex);
  40. // Propagate exception to caller.
  41. throw ex;
  42. }
  43. }
  44. }

3.4.1 //step4.1 refresh()的核心 obtainFreshBeanFactory()

ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
初始化BeanFactory,是整个refresh()方法的核心,其中完成了配置文件的加载、解析、注册。
  1. AbstractApplicationContext.java

  2. protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
  3. refreshBeanFactory();
  4. ConfigurableListableBeanFactory beanFactory = getBeanFactory();
  5. if (logger.isDebugEnabled()) {
  6. logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
  7. }
  8. return beanFactory;
  9. }
跟进一下refreshBeanFactory();
  1. AbstractRefreshableApplicationContext.java

  2. protected final void refreshBeanFactory() throws BeansException {
  3. if (hasBeanFactory()) {
  4. destroyBeans();
  5. closeBeanFactory();
  6. }
  7. try {
  8. DefaultListableBeanFactory beanFactory = createBeanFactory();
  9. beanFactory.setSerializationId(getId());
  10. customizeBeanFactory(beanFactory);
  11. loadBeanDefinitions(beanFactory);
  12. synchronized (this.beanFactoryMonitor) {
  13. this.beanFactory = beanFactory;
  14. }
  15. }
  16. catch (IOException ex) {
  17. throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
  18. }
  19. }
DefaultListableBeanFactory beanFactory = createBeanFactory(); 
在这个beanFactory中有个beanDefinitionMap,此时size=0

再跟进loadBeanDefinitions(beanFactory);
  1. AbstractXmlApplicationContext.java

  2. protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
  3. // Create a new XmlBeanDefinitionReader for the given BeanFactory.
  4. XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
  5. // Configure the bean definition reader with this context‘s
  6. // resource loading environment.
  7. beanDefinitionReader.setEnvironment(this.getEnvironment());
  8. beanDefinitionReader.setResourceLoader(this);
  9. beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
  10. // Allow a subclass to provide custom initialization of the reader,
  11. // then proceed with actually loading the bean definitions.
  12. initBeanDefinitionReader(beanDefinitionReader);
  13. loadBeanDefinitions(beanDefinitionReader);
  14. }

我们跟进这个方法
  1. protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
  2. String[] configLocations = getConfigLocations();
  3. if (configLocations != null) {
  4. for (String configLocation : configLocations) {
  5. reader.loadBeanDefinitions(configLocation);
  6. }
  7. }
  8. }
在3.3中我们提到,configLocations已经被得到,那么此时可以看到:
技术分享

这里设计了层层调用,有很多重载方法,主要就是加载Spring所有的配置文件(可能会有多个),以备后面解析,注册之用。

然后追踪到 org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.doRegisterBeanDefinitions(Element root) 中
  1. DefaultBeanDefinitionDocumentReader.java

  2. protected void doRegisterBeanDefinitions(Element root) {
  3. // Any nested <beans> elements will cause recursion in this method. In
  4. // order to propagate and preserve <beans> default-* attributes correctly,
  5. // keep track of the current (parent) delegate, which may be null. Create
  6. // the new (child) delegate with a reference to the parent for fallback purposes,
  7. // then ultimately reset this.delegate back to its original (parent) reference.
  8. // this behavior emulates a stack of delegates without actually necessitating one.
  9. BeanDefinitionParserDelegate parent = this.delegate;
  10. this.delegate = createDelegate(this.readerContext, root, parent);
  11. if (this.delegate.isDefaultNamespace(root)) {
  12. String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
  13. if (StringUtils.hasText(profileSpec)) {
  14. Assert.state(this.environment != null, "Environment must be set for evaluating profiles");
  15. String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
  16. profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
  17. if (!this.environment.acceptsProfiles(specifiedProfiles)) {
  18. return;
  19. }
  20. }
  21. }

  22. preProcessXml(root);

  23. //xml解析和加载类
  24. parseBeanDefinitions(root, this.delegate);
  25. postProcessXml(root);
  26. this.delegate = parent;
  27. }

BeanDefinitionParserDelegate parent = this.delegate;
这里创建了一个BeanDefinitionParserDelegate实例,解析XML的过程就是委托它完成的。实际上你跟进该类,可以发现里面定义了大量的常量,这些常量实际上就是我们在xml中使用到的节点和属性名。

e.g.
  1. ...
  2. public static final String PROPERTY_ELEMENT = "property";
  3. public static final String REF_ATTRIBUTE = "ref";
  4. public static final String VALUE_ATTRIBUTE = "value";
  5. ...
技术分享
技术分享

这里的delegate实际上也是 BeanDefinitionParserDelegate 类,所以我们看到,在 parseBeanDefinitions(root, this.delegate); 中,是将该类作为一个参数引入了方法,实际上,它在方法中就发挥着解析xml的作用。

//xml解析和加载类
parseBeanDefinitions(root, this.delegate);
  1. DefaultBeanDefinitionDocumentReader.java

  2. protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
  3. if (delegate.isDefaultNamespace(root)) {
  4. NodeList nl = root.getChildNodes(); //将节点获取存入collection
  5. for (int i = 0; i < nl.getLength(); i++) { //对collection中存储的节点进行依次遍历
  6. Node node = nl.item(i); //返回当前序号的节点
  7. if (node instanceof Element) { //判断节点是否属于元素类(我们不需要文本型)(参考文章《Java是如何解析xml文件的(DOM)》)
  8. Element ele = (Element) node;
  9. //判断是否为默认的命名空间,其实就是根据配置文件的命名空间来判定
  10. //如果是beans下的则认为是默认的命名空间,如果不是则认为是自定义的,我们使用的Aop、Tx等都是属于自定义标签的范畴
  11. if (delegate.isDefaultNamespace(ele)) {
  12. parseDefaultElement(ele, delegate); //进行解析
  13. }
  14. else {
  15. delegate.parseCustomElement(ele);
  16. }
  17. }
  18. }
  19. }
  20. else {
  21. delegate.parseCustomElement(root);
  22. }
  23. }

最终,我们可以看到解析XML的是 parseDefaultElement(ele, delegate); 方法,它会判断并调用对应的解析,这里我们是bean
  1. DefaultBeanDefinitionDocumentReader.java

  2. private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
  3. if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
  4. importBeanDefinitionResource(ele);
  5. }
  6. else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
  7. processAliasRegistration(ele);
  8. }
  9. else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
  10. processBeanDefinition(ele, delegate);
  11. }
  12. else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
  13. // recurse
  14. doRegisterBeanDefinitions(ele);
  15. }
  16. }
然后进一步
  1. DefaultBeanDefinitionDocumentReader.java

  2. protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
  3. BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
  4. if (bdHolder != null) {
  5. bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
  6. try {
  7. // Register the final decorated instance.
  8. BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
  9. }
  10. catch (BeanDefinitionStoreException ex) {
  11. getReaderContext().error("Failed to register bean definition with name ‘" +
  12. bdHolder.getBeanName() + "‘", ele, ex);
  13. }
  14. // Send registration event.
  15. getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
  16. }
  17. }
这个方法如果进一步深入,你可以发现它实际上最核心的两个步骤是:
  • 把beanName放到队列里
  • 把BeanDefinition放到map中
(关于这段注册bean的方法的源码跟进,可以参考博文:《Spring Ioc 源码分析(四)--parseBeanDefinitions()与BeanDefinitionParserDelegate》)

技术分享
好了,执行完 parseBeanDefinitions 这个方法,我们看看现在的 delegate 里面多了些什么?
技术分享
技术分享

到此,bean的注册就完成了(当然,这里是指所有的bean都注册完)。在后面实例化的时候,就是把beanDefinitionMap中的BeanDefinition取出来,逐一实例化。

obtainFreshBeanFactory() 总算结束了,我们继续看refresh()方法中另一个核心方法,它是将bean进行实例化的重要角色。

3.4.2 //step4.2 refresh()的核心finishBeanFactoryInitialization(beanFactory);

我想到这里估计已经晕乎了,如果不太清楚我们现在走到了哪里,请查看目录中的 3.4 //step4

经过obtainFreshBeanFactory() 这个方法,我们的beanFactory就准备好了,接下来我们主要围绕finishBeanFactoryInitialization(beanFactory)方法,聊聊Spring是如何实例化bean的。
  1. AbstractApplicationContext.java

  2. protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
  3. // Initialize conversion service for this context.
  4. if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
  5. beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
  6. beanFactory.setConversionService(
  7. beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
  8. }
  9. // Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
  10. String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
  11. for (String weaverAwareName : weaverAwareNames) {
  12. getBean(weaverAwareName);
  13. }
  14. // Stop using the temporary ClassLoader for type matching.
  15. beanFactory.setTempClassLoader(null);
  16. // Allow for caching all bean definition metadata, not expecting further changes.
  17. beanFactory.freezeConfiguration();
  18. // Instantiate all remaining (non-lazy-init) singletons.
  19. beanFactory.preInstantiateSingletons();
  20. }
这个方法,就是为了实例化非懒加载的单例bean,我们走进 beanFactory.preInstantiateSingletons(); 看一看
(注意,这里实例化单例,而Struts中Action是每次请求都创建,所以Action并不是单例的)

  1. DefaultListableBeanFactory.java

  2. public void preInstantiateSingletons() throws BeansException {
  3. if (this.logger.isDebugEnabled()) {
  4. this.logger.debug("Pre-instantiating singletons in " + this);
  5. }
  6. List<String> beanNames;
  7. synchronized (this.beanDefinitionMap) {
  8. // Iterate over a copy to allow for init methods which in turn register new bean definitions.
  9. // While this may not be part of the regular factory bootstrap, it does otherwise work fine.
  10. beanNames = new ArrayList<String>(this.beanDefinitionNames);
  11. }
  12. // Trigger initialization of all non-lazy singleton beans...
  13. for (String beanName : beanNames) { //将加载进来的beanDefinitionNames循环分析
  14. RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
  15. if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) { //如果不是抽象类, 且是单例, 且不是延迟加载
  16. if (isFactoryBean(beanName)) {
  17. final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
  18. boolean isEagerInit;
  19. if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
  20. isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
  21. @Override
  22. public Boolean run() {
  23. return ((SmartFactoryBean<?>) factory).isEagerInit();
  24. }
  25. }, getAccessControlContext());
  26. }
  27. else {
  28. isEagerInit = (factory instanceof SmartFactoryBean &&
  29. ((SmartFactoryBean<?>) factory).isEagerInit());
  30. }
  31. if (isEagerInit) {
  32. getBean(beanName);
  33. }
  34. }
  35. else {
  36. getBean(beanName);
  37. }
  38. }
  39. }
  40. // Trigger post-initialization callback for all applicable beans...
  41. for (String beanName : beanNames) {
  42. Object singletonInstance = getSingleton(beanName);
  43. if (singletonInstance instanceof SmartInitializingSingleton) {
  44. final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
  45. if (System.getSecurityManager() != null) {
  46. AccessController.doPrivileged(new PrivilegedAction<Object>() {
  47. @Override
  48. public Object run() {
  49. smartSingleton.afterSingletonsInstantiated();
  50. return null;
  51. }
  52. }, getAccessControlContext());
  53. }
  54. else {
  55. smartSingleton.afterSingletonsInstantiated();
  56. }
  57. }
  58. }
  59. }
因为Struts项目中Action并不满足条件 “不是抽象类, 且是单例, 且不是延迟加载”,所以该方法对我们自定义的Action几乎没有用,我们一直循环直到单例的对象出现,再来看这个代码。

我们把这小段代码提出来单独看
  1. for (String beanName : beanNames) { //将加载进来的beanDefinitionNames循环分析
  2. RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
  3. if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) { //如果不是抽象类, 且是单例, 且不是延迟加载
  4. if (isFactoryBean(beanName)) { //是否实现FactoryBean接口
  5. final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
  6. boolean isEagerInit;
  7. if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
  8. isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
  9. @Override
  10. public Boolean run() {
  11. return ((SmartFactoryBean<?>) factory).isEagerInit();
  12. }
  13. }, getAccessControlContext());
  14. }
  15. else {
  16. isEagerInit = (factory instanceof SmartFactoryBean &&
  17. ((SmartFactoryBean<?>) factory).isEagerInit());
  18. }
  19. if (isEagerInit) {
  20. getBean(beanName);
  21. }
  22. }
  23. else {
  24. getBean(beanName);
  25. }
  26. }
  27. }
  • 判断这个bean是否是抽象类,是否是单例,是否延迟加载
  • 如果不是抽象类, 且是单例, 且不是延迟加载,那么判断是否实现 FactoryBean 接口
  • 如果实现了 FactoryBean,则 getBean(FACTORY_BEAN_PREFIX + beanName),否则 getBean(beanName)
(参考链接:)

如果我们跟进 getBean 这个方法,发现它调用了 doGetBean 这个方法,我们再跟进,这个方法非常长(这里就不贴出来了)

在这个方法中,你可以不断地去跟进(这里不再做具体展开),你会发现大概的步骤差不多是
  • 创建一个bean的实例
  • 将这个实例封装到BeanWrapper中
技术分享

而这里bean的实例化方法,其实是 beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
这个instantiate 方法在 package org.springframework.beans.factory.support; --> SimpleInstantiationStrategy.java
在这之中采用反射机制将对象进行了实例化。

其实还涉及到bean实例化以后,Spring是如何将bean的属性进行注入的,这里暂时不做进一步的展开了。

可以知道的是,最终属性的注入是利用反射机制,通过setter赋值的。


4、参考链接

  • 看看Spring的源码(一)——Bean加载过程
  • 看看Spring源码(二)——bean实例化
  • spring加载过程,源码带你理解从初始化到bean注入
  • spring的加载过程(web) (10)--preInstantiateSingletons
  • Spring Ioc 源码分析(四)--parseBeanDefinitions()与BeanDefinitionParserDelegate
  • BeanFactory和FactoryBean




null


源码跟读,Spring是如何解析和加载xml中配置的beans