SpringMVC通过一个配置文件描述Bean以及Bean之间的依赖关系,利用Java的反射机制实例化Bean并建立Bean之间的依赖关系。IOC容器在完成这些底层工作的基础还提供了Bean的实例缓、生命周期、Bean实例代理等。BeanFacory是SpringMVC框架最核心的接口,他提供了最高级IOC的配置机制。ApplicationContext由BeanFactory派生而来,这也说明了 springMVC容器中运行的主体对象是 Bean,另外 ApplicationContext 继承了 ResourceLoader 接口,使得 ApplicationContext 可以访问到任何外部资源,提供了更多面向实际应用的功能。SpringMVC初始化时在什么时候读取配置我们配置好的bean的文件,怎么解析和注册Bean?接下来我们带着问题来分析。

上一篇,我们在介绍DispatcherServlet初始化的介绍时,父类FrameworkServlet在创建上下文时,调用了一个重启上下文时,并初始化Bean。如图所示:

ConfigurableWebApplicationContext wac;调用了wac.refresh();方法,完成了对ApplicationContext的初始化,注册啊,封装bean的工作,由ConfigurableWebApplicationContext 的子类AbstractApplicationContext实现了bean初始化。我们来了解一下上下文(容器)整个继承关系。这边的箭头表示继承或者实现的,如图所示:

从上图我们可以看出,ApplicationContext也是继承BeanFactory,ApplicationContext 也继承了 ResourceLoader 接口,使得 ApplicationContext 可以访问到任何外部资源,提供了更多面向实际应用的功能,我们通过refresh为入口点,分析Bean整个初始化过程,并了解ApplicationContext的子类AbstractApplicationContext做了哪些工作并了解父类实现了哪些功能?每个接口都有他使用的场合,它主要是为了区分在 SpringMVC内部在操作过程中对象的传递和转化过程中,对对象的数据访问所做的限制。我们现在先头脑中有这个继承的关系图,以后深度的了解,比较不会那么晕,这样思路就比较清晰,我们以refresh为入口点,带着这些问题进行解析。

refresh方法的源代码如下:

  1. public void refresh() throws BeansException, IllegalStateException {
  2. synchronized (this.startupShutdownMonitor) {
  3. // 准备对上下文进行刷新
  4. prepareRefresh();
  5. // 初始化beanFactory
  6. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
  7. // 为BeanFactory配置容器特性,例如类加载器、事件处理器等
  8. prepareBeanFactory(beanFactory);
  9. try {
  10. // 为容器的某些子类指定特殊的BeanPost事件处理器
  11. postProcessBeanFactory(beanFactory);
  12. // 调用beanFactory处理器
  13. invokeBeanFactoryPostProcessors(beanFactory);
  14. // 注册beanFactory处理器
  15. registerBeanPostProcessors(beanFactory);
  16. // 初始化消息源
  17. initMessageSource();
  18. // 初始化上下文事件广播.
  19. initApplicationEventMulticaster();
  20. // 初始化其它特殊bean,由子类来实现
  21. onRefresh();
  22. // 注册事件监听器.
  23. registerListeners();
  24. // 初始化所有单实例的bean,使用懒加载除外
  25. finishBeanFactoryInitialization(beanFactory);
  26. // 完成刷新并发布容器刷新事件
  27. finishRefresh();
  28. }catch (BeansException ex) {
  29. // Destroy already created singletons to avoid dangling resources.
  30. destroyBeans();
  31. // Reset 'active' flag.
  32. cancelRefresh(ex);
  33. // Propagate exception to caller.
  34. throw ex;
  35. }
  36. }
  37. }

这个方法做了很多事情,主要有初始化beanFactory 、注册BeanFactory处理器等工作。我们接下来对里面重要的功能进行解析,它怎么读取我们配置文件并解析文件和注册配置文件里的bean。

初始化beanFactory工厂时,就对读取我们配置文件并解析文件和注册配置文件里的Bean,是由AbstractApplicationContext.obtainFreshBeanFactory方法实现的,源代码如下:

  1. **
  2. * Tell the subclass to refresh the internal bean factory.
  3. * @return the fresh BeanFactory instance
  4. * @see #refreshBeanFactory()
  5. * @see #getBeanFactory()
  6. */
  7. protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
  8. refreshBeanFactory();
  9. ConfigurableListableBeanFactory beanFactory = getBeanFactory();
  10. if (logger.isDebugEnabled()) {
  11. logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
  12. }
  13. return beanFactory;
  14. }
  1. @Override
  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. }

第一步:

refreshBeanFactory 先销毁所有Bean,关闭BeanFactory,然后重新创建一个BeanFactory,并将其赋给BeanFactory实例变量。我们先看一下它怎么创建BeanFactory?

createBeanFactory这个方法是创建新的BeanBeanFactory ,而BeanFactory 的原始对象是 DefaultListableBeanFactory,这个非常关键,因为他设计到后面对这个对象的多种操作,DefaultListableBeanFactory是整个Bean加载的核心部分,是SpringMVC注册及加载Bean的默认实现。

  1. protected DefaultListableBeanFactory createBeanFactory() {
  2. return new DefaultListableBeanFactory(getInternalParentBeanFactory());
  3. }

初始化BeanFactory时,判断是否有父BeanFactory,如果有设置,如果没有就为null。

  1. protected BeanFactory getInternalParentBeanFactory() {
  2. return (getParent() instanceof ConfigurableApplicationContext) ?
  3. ((ConfigurableApplicationContext) getParent()).getBeanFactory() : getParent();
  4. }

获取父BeanFactory

  1. public DefaultListableBeanFactory(BeanFactory parentBeanFactory) {
  2. super(parentBeanFactory);
  3. }
  4. public AbstractAutowireCapableBeanFactory(BeanFactory parentBeanFactory) {
  5. this();
  6. setParentBeanFactory(parentBeanFactory);
  7. }

setParentBeanFactory设置父BeanFactory。

我们对BeanFactory继承关系了解一下,为后面的解析提供比较清晰的路线,如图所示:

现在BeanFactory创建好了,ApplicationContext 继承了BeanFactory,还继承了 ResourceLoader 接口,使得 ApplicationContext 可以访问到任何外部资源,提供了更多面向实际应用的功能,接下来我们进行第二步,分析解析配置文件和注册Bean。

第二步:先转码在读取配置文件

loadBeanDefinitions(beanFactory) 将找到答案,这个方法将开始加载、解析 Bean 的定义,也就是把用户定义的数据结构转化为 Ioc 容器中的特定数据结构。我们要分析一下这个方法。

  1. @Override
  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. }

XmlBeanDefinitionReader 来读取并解析 xml 文件,XmlBeanDefinitionReader 是 BeanDefinitionReader 接口的实现。我们来看一下继承关系,如图所示:

BeanDefinitionReader 读取Resource所指向的配置文件资源,然后解析配置文件,配置文件中每一个Bean解析成一个BeanDefinition对象,并保存到BeanDefinitionRegistry中。我们这边先了解一下。先实现通过BeanFactory创建XmlBeanDefinitionReader对象,然后为XmlBeanDefinitionReader 配置ResourceLoader,因为DefaultResourceLoader是父类,所以this可以直接被使用,ResourceLoader对资源的加载。 如图所示:

我们现在获取外面的资源也设置好了,我们进行深入解析,loadBeanDefinitions方法源代码如下:

  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. }
  1. @Override
  2. public String[] getConfigLocations() {
  3. return super.getConfigLocations();
  4. }
  1. protected String[] getConfigLocations() {
  2. return (this.configLocations != null ? this.configLocations : getDefaultConfigLocations());
  3. }

由 AbstractRefreshableWebApplicationContext父类AbstractRefreshableConfigApplicationContext实现getConfigLocations方法,获取了web.xml配置的param-value的值,然后getConfigLocations保存在数组里面。如图所示:

现在获取了param-value的值,接下去就开始读取配置文件,具体实现时由XmlBeanDefinitionReader父类AbstractBeanDefinitionReader实现的,

reader.loadBeanDefinitions(configLocation);源代码:

  1. public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
  2. return loadBeanDefinitions(location, null);
  3. }
  4. public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
  5. ResourceLoader resourceLoader = getResourceLoader();
  6. if (resourceLoader == null) {
  7. throw new BeanDefinitionStoreException(
  8. "Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
  9. }
  10. if (resourceLoader instanceof ResourcePatternResolver) {
  11. // Resource pattern matching available.
  12. try {
  13. Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
  14. int loadCount = loadBeanDefinitions(resources);
  15. if (actualResources != null) {
  16. for (Resource resource : resources) {
  17. actualResources.add(resource);
  18. }
  19. }
  20. if (logger.isDebugEnabled()) {
  21. logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
  22. }
  23. return loadCount;
  24. }
  25. catch (IOException ex) {
  26. throw new BeanDefinitionStoreException(
  27. "Could not resolve bean definition resource pattern [" + location + "]", ex);
  28. }
  29. }
  30. else {
  31. // Can only load single resources by absolute URL.
  32. Resource resource = resourceLoader.getResource(location);
  33. int loadCount = loadBeanDefinitions(resource);
  34. if (actualResources != null) {
  35. actualResources.add(resource);
  36. }
  37. if (logger.isDebugEnabled()) {
  38. logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
  39. }
  40. return loadCount;
  41. }
  42. }

这个方法有一个ResourcePatternResolver加载了web.xml配置的param-value对应的资源,例如classpath:xxx*.xml 加载了xxx下为xml为后缀的文件资源,loadBeanDefinitions进行对加载进来的资源进行解析。在解析之前先进行转码,是通过EncodedResource对资源文件进行转码,源代码如下:

  1. public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
  2. return loadBeanDefinitions(new EncodedResource(resource));
  3. }

对资源文件转码完,就开始读进来,以流的形式,源代码如下:

  1. public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
  2. Assert.notNull(encodedResource, "EncodedResource must not be null");
  3. if (logger.isInfoEnabled()) {
  4. logger.info("Loading XML bean definitions from " + encodedResource.getResource());
  5. }
  6. Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
  7. if (currentResources == null) {
  8. currentResources = new HashSet<EncodedResource>(4);
  9. this.resourcesCurrentlyBeingLoaded.set(currentResources);
  10. }
  11. if (!currentResources.add(encodedResource)) {
  12. throw new BeanDefinitionStoreException(
  13. "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
  14. }
  15. try {
  16. InputStream inputStream = encodedResource.getResource().getInputStream();
  17. try {
  18. InputSource inputSource = new InputSource(inputStream);
  19. if (encodedResource.getEncoding() != null) {
  20. inputSource.setEncoding(encodedResource.getEncoding());
  21. }
  22. return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
  23. }
  24. finally {
  25. inputStream.close();
  26. }
  27. }
  28. catch (IOException ex) {
  29. throw new BeanDefinitionStoreException(
  30. "IOException parsing XML document from " + encodedResource.getResource(), ex);
  31. }
  32. finally {
  33. currentResources.remove(encodedResource);
  34. if (currentResources.isEmpty()) {
  35. this.resourcesCurrentlyBeingLoaded.remove();
  36. }
  37. }
  38. }

说明:

(1)getValidationModeForResource(resource);获取对XML文件的验证模式

(2)Document doc = this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());加载XML文件,并获取对应的Document。这边是把配置文件读进来,以Document
形式存储。

(3)registerBeanDefinitions(doc, resource);解析并注册bean。

第三步:解析并注册bean,解析 Bean 的定义,也就是把用户定义的数据结构转化为 Ioc 容器中的特定数据结构。

我们继续第二步中先把文件读进来,接下来进行解析和注册是由registerBeanDefinitions(doc, resource);这个方法实现的,源代码如下:

  1. public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
  2. BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
  3. documentReader.setEnvironment(this.getEnvironment());
  4. int countBefore = getRegistry().getBeanDefinitionCount();//<span style="color:#ff0000;">统计前BeanDefinition加载个数</span>
  5. documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
  6. return getRegistry().getBeanDefinitionCount() - countBefore;
  7. }
  8. <span style="color:#ff0000;">//解析并注册bean</span>
  9. public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
  10. this.readerContext = readerContext;
  11. logger.debug("Loading bean definitions");
  12. Element root = doc.getDocumentElement();
  13. doRegisterBeanDefinitions(root);
  14. }
  15. protected void doRegisterBeanDefinitions(Element root) {
  16. String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
  17. if (StringUtils.hasText(profileSpec)) {
  18. Assert.state(this.environment != null, "Environment must be set for evaluating profiles");
  19. String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
  20. profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
  21. if (!this.environment.acceptsProfiles(specifiedProfiles)) {
  22. return;
  23. }
  24. }
  25. BeanDefinitionParserDelegate parent = this.delegate;
  26. this.delegate = createDelegate(this.readerContext, root, parent);
  27. preProcessXml(root);
  28. parseBeanDefinitions(root, this.delegate);
  29. postProcessXml(root);
  30. this.delegate = parent;
  31. }

说明:

(1)public static final String PROFILE_ATTRIBUTE = "profile";判断bean节点是否定义了profile属性,如果有,就要到环境变量中去寻找

(2)BeanDefinitionParserDelegate是对BeanDefinition的解析的,这里就不具体分析了。

(2) parseBeanDefinitions(root, this.delegate);把bean注册到BeanDefinition对象中。

这个 parseBeanDefinitions(root, this.delegate)开始注册了,我们解析到重点了,源代码如下:

  1. protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
  2. if (delegate.isDefaultNamespace(root)) {
  3. NodeList nl = root.getChildNodes();
  4. for (int i = 0; i < nl.getLength(); i++) {
  5. Node node = nl.item(i);
  6. if (node instanceof Element) {
  7. Element ele = (Element) node;
  8. if (delegate.isDefaultNamespace(ele)) {
  9. parseDefaultElement(ele, delegate);
  10. }
  11. else {
  12. delegate.parseCustomElement(ele);
  13. }
  14. }
  15. }
  16. }
  17. else {
  18. delegate.parseCustomElement(root);
  19. }
  20. }
  21. private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
  22. //对IMPORT标签进行处理的
  23. if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
  24. importBeanDefinitionResource(ele);
  25. }
  26. //对ALIAS标签进行处理的
  27. else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
  28. processAliasRegistration(ele);
  29. }
  30. //对BEAN标签进行处理的
  31. else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
  32. processBeanDefinition(ele, delegate);
  33. }
  34. else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
  35. // recurse
  36. doRegisterBeanDefinitions(ele);
  37. }
  38. }
  39. //我们来对处理bean标签进行分析
  40. protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
  41. BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
  42. if (bdHolder != null) {
  43. bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
  44. try {
  45. // Register the final decorated instance.
  46. BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
  47. }
  48. catch (BeanDefinitionStoreException ex) {
  49. getReaderContext().error("Failed to register bean definition with name '" +
  50. bdHolder.getBeanName() + "'", ele, ex);
  51. }
  52. // Send registration event.
  53. getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
  54. }
  55. }

解析Bean的id、name等元素设置到BeanDefinition然后放到放到BeanDefinitionHolder中,这个是由parseBeanDefinitionElement进行处理的,源代码如下:

  1. public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
  2. //解析ID属性
  3. String id = ele.getAttribute(ID_ATTRIBUTE);
  4. //解析name属性
  5. String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
  6. List<String> aliases = new ArrayList<String>();
  7. if (StringUtils.hasLength(nameAttr)) {
  8. String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
  9. aliases.addAll(Arrays.asList(nameArr));
  10. }
  11. String beanName = id;
  12. if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
  13. beanName = aliases.remove(0);
  14. if (logger.isDebugEnabled()) {
  15. logger.debug("No XML 'id' specified - using '" + beanName +
  16. "' as bean name and " + aliases + " as aliases");
  17. }
  18. }
  19. if (containingBean == null) {
  20. checkNameUniqueness(beanName, aliases, ele);
  21. }
  22. //对其它标签进行解析
  23. AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
  24. if (beanDefinition != null) {
  25. if (!StringUtils.hasText(beanName)) {
  26. try {
  27. if (containingBean != null) {
  28. beanName = BeanDefinitionReaderUtils.generateBeanName(
  29. beanDefinition, this.readerContext.getRegistry(), true);
  30. }
  31. else {
  32. beanName = this.readerContext.generateBeanName(beanDefinition);
  33. // Register an alias for the plain bean class name, if still possible,
  34. // if the generator returned the class name plus a suffix.
  35. // This is expected for Spring 1.2/2.0 backwards compatibility.
  36. String beanClassName = beanDefinition.getBeanClassName();
  37. if (beanClassName != null &&
  38. beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
  39. !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
  40. aliases.add(beanClassName);
  41. }
  42. }
  43. if (logger.isDebugEnabled()) {
  44. logger.debug("Neither XML 'id' nor 'name' specified - " +
  45. "using generated bean name [" + beanName + "]");
  46. }
  47. }
  48. catch (Exception ex) {
  49. error(ex.getMessage(), ele);
  50. return null;
  51. }
  52. }
  53. String[] aliasesArray = StringUtils.toStringArray(aliases);
  54. return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
  55. }
  56. return null;
  57. }
  58. //对其它标签进行解析
  59. public AbstractBeanDefinition parseBeanDefinitionElement(
  60. Element ele, String beanName, BeanDefinition containingBean) {
  61. this.parseState.push(new BeanEntry(beanName));
  62. //对class属性进行解析
  63. String className = null;
  64. if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
  65. className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
  66. }
  67. try {
  68. //对parent属性进行解析
  69. String parent = null;
  70. if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
  71. parent = ele.getAttribute(PARENT_ATTRIBUTE);
  72. }
  73. AbstractBeanDefinition bd = createBeanDefinition(className, parent);
  74. //解析默认bean的各种属性
  75. parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
  76. bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
  77. //解析元数据
  78. parseMetaElements(ele, bd);
  79. parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
  80. parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
  81. //解析构造函数
  82. parseConstructorArgElements(ele, bd);
  83. //解析Property
  84. parsePropertyElements(ele, bd);
  85. parseQualifierElements(ele, bd);
  86. bd.setResource(this.readerContext.getResource());
  87. bd.setSource(extractSource(ele));
  88. return bd;
  89. }
  90. catch (ClassNotFoundException ex) {
  91. error("Bean class [" + className + "] not found", ele, ex);
  92. }
  93. catch (NoClassDefFoundError err) {
  94. error("Class that bean class [" + className + "] depends on not found", ele, err);
  95. }
  96. catch (Throwable ex) {
  97. error("Unexpected failure during bean definition parsing", ele, ex);
  98. }
  99. finally {
  100. this.parseState.pop();
  101. }
  102. return null;
  103. }

对bean的id、name、class等属性的解析并设置到BeanDefinition然后放到放到BeanDefinitionHolder中,并放入到IOC容器中建立对应的数据结构。

现在IOC容器中已经建立对应的数据结构,接下来开始对对bean进行注册,

BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());源代码如下:

  1. public static void registerBeanDefinition(
  2. BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
  3. throws BeanDefinitionStoreException {
  4. // Register bean definition under primary name.
  5. String beanName = definitionHolder.getBeanName();
  6. registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
  7. // Register aliases for bean name, if any.
  8. String[] aliases = definitionHolder.getAliases();
  9. if (aliases != null) {
  10. for (String aliase : aliases) {
  11. registry.registerAlias(beanName, aliase);
  12. }
  13. }
  14. }
  15. public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
  16. throws BeanDefinitionStoreException {
  17. Assert.hasText(beanName, "Bean name must not be empty");
  18. Assert.notNull(beanDefinition, "BeanDefinition must not be null");
  19. if (beanDefinition instanceof AbstractBeanDefinition) {
  20. try {
  21. ((AbstractBeanDefinition) beanDefinition).validate();
  22. }
  23. catch (BeanDefinitionValidationException ex) {
  24. throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
  25. "Validation of bean definition failed", ex);
  26. }
  27. }
  28. synchronized (this.beanDefinitionMap) {
  29. Object oldBeanDefinition = this.beanDefinitionMap.get(beanName);
  30. if (oldBeanDefinition != null) {
  31. if (!this.allowBeanDefinitionOverriding) {
  32. throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
  33. "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
  34. "': There is already [" + oldBeanDefinition + "] bound.");
  35. }
  36. else {
  37. if (this.logger.isInfoEnabled()) {
  38. this.logger.info("Overriding bean definition for bean '" + beanName +
  39. "': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");
  40. }
  41. }
  42. }
  43. else {
  44. this.beanDefinitionNames.add(beanName);
  45. this.frozenBeanDefinitionNames = null;
  46. }
  47. this.beanDefinitionMap.put(beanName, beanDefinition);
  48. }
  49. resetBeanDefinition(beanName);
  50. }

先检查是不是有相同名字的BeanDefinition已经在IoC容器中注册了,如果有相同名字的BeanDefinition,但又不允许覆盖,那么会抛出异常,如果可以可以覆盖,或者IOC容器中没注册,然后把Bean的名字存入到beanDefinitionNames的同时,把beanName作为Map的key,把beanDefinition作为value存入到IoC容器持有的BeanDefinitionMap中去。

总结:

(1)SpringMVC先创建BeanFactory时先销毁所有Bean,关闭BeanFactory,然后重新创建一个BeanFactory,并将其赋给BeanFactory实例变量。

(2)创建好了BeanFactory,并设置好环境,ApplicationContext 继承了 ResourceLoader 接口,并通过ResourcePatternResolver加载了web.xml配置的param-value对应的资源,例如classpath:xxx*.xml 加载了xxx下为xml为后缀的文件资源,loadBeanDefinitions进行对加载进来的资源进行解析,在解析之前先进行转码,是通过EncodedResource对资源文件进行转码

(3)对读取进来的配置文件进行验证,是否是XML格式的,然后配置文件中的Bean的id、name、class等属性的解析并设置到BeanDefinition然后放到放到BeanDefinitionHolder中,并放入到IOC容器中建立对应的数据结构。是以Document形式的存储的。

(4)在IOC容器中已经建立对应的数据结构,解析 Bean 的定义和注册,先检查是不是有相同名字的BeanDefinition已经在IoC容器中注册了,如果有相同名字的BeanDefinition,但又不允许覆盖,那么会抛出异常,如果可以可以覆盖,或者IOC容器中没注册,然后把Bean的名字存入到beanDefinitionNames的同时,把beanName作为Map的key,把beanDefinition作为value存入到IoC容器持有的BeanDefinitionMap中去。

我们现在注册好,要实例化,还有Spring最核心的IOC依赖注入,怎么实例化并依赖注入,带着这些问题?下一篇我们继续解析。

SpringMVC 源代码深度解析 IOC容器(Bean 解析、注册)的更多相关文章

  1. Spring源码解析-ioc容器的设计

    Spring源码解析-ioc容器的设计 1 IoC容器系列的设计:BeanFactory和ApplicatioContext 在Spring容器中,主要分为两个主要的容器系列,一个是实现BeanFac ...

  2. [spring源码] 小白级别的源码解析IOC容器的依赖注入(三)

    上一篇介绍了ioc容器的初始化过程,主要完成了ioc容器建立beanDefinition数据映射.并没有看到ioc容器对bean依赖关系进行注入. 接口getbean就是出发依赖注入发生的地方.下面从 ...

  3. Spring源码解析-IOC容器的实现-ApplicationContext

    上面我们已经知道了IOC的建立的基本步骤了,我们就可以用编码的方式和IOC容器进行建立过程了.其实Spring已经为我们提供了很多实现,想必上面的简单扩展,如XMLBeanFacroty等.我们一般是 ...

  4. Spring源码解析-IOC容器的实现

    1.IOC容器是什么? IOC(Inversion of Control)控制反转:本来是由应用程序管理的对象之间的依赖关系,现在交给了容器管理,这就叫控制反转,即交给了IOC容器,Spring的IO ...

  5. spring源码学习之路---深度分析IOC容器初始化过程(四)

    作者:zuoxiaolong8810(左潇龙),转载请注明出处,特别说明:本博文来自博主原博客,为保证新博客中博文的完整性,特复制到此留存,如需转载请注明新博客地址即可. 最近由于工作和生活,学习耽搁 ...

  6. @PostConstruct - 静态方法调用IOC容器Bean对象

    需求:工具类里面引用IOC容器Bean,强迫症患者在调用工具类时喜欢用静态方法的方式而非注入的方式去调用,但是spring 不支持注解注入静态成员变量. 静态变量/类变量不是对象的属性,而是一个类的属 ...

  7. IoC容器-Bean管理注解方式(创建对象)

    IoC操作Bean管理(基于注解方式) 1,什么是注解 (1)注解是代码特殊标记,格式:@注解名称(属性名称=属性值,属性名称=属性值...) (2)使用注解,注解作用在类上面,方法上面,属性上面 ( ...

  8. IoC容器-Bean管理XML方式(引入外部属性文件)

    IoC操作Bean管理(引入外部属性文件) 1,直接配置数据库信息 (1)配置德鲁伊连接池 (2)引入德鲁伊连接池依赖jar包 2,通过引入外部属性文件配置数据库连接池 (1)创建外部属性文件,pro ...

  9. IoC容器-Bean管理XML方式(自动装配)

    IoC操作Bean管理(XML自动装配) 1,什么是自动装配 (1)根据指定装配规则(属性名称或者属性类型),Spring自动将匹配的属性值进行注入 2,演示自动装配过程 (1)根据属性名称自动注入 ...

随机推荐

  1. 在ServletFilter层返回多字段提示给前端

    0.背景:在由于不想在小项目中引入SpringSecurity这种重量级安全框架,我自定义了一个LoginFilter来处理认证+授权逻辑.对于认证或授权失败的情况,最初是在filter中抛出异常,并 ...

  2. 谷歌BBR拥塞算法内核更新

    为什么想到这个呢,算法什么的又不太懂,这是 因为搭建VPN + BBR 与之简直绝配 有的人搭建SSR ,配一个什么锐速,还需要降内核版本, 而且还容易出错,降了之后更加容易出现兼容性问题,所以偶尔看 ...

  3. .net全栈开发-c#面向对象与工控自动化分拣上位机

    一.前言 开始做了两年web.期间也整了一段时间winform.后来做了两年工控上位机,也就是做工控这两年发现机器跟面向对象真是如此贴切,也是我从处理数据和流程的思维转变为面向对象思维的开始.这对我后 ...

  4. 一个抓猫的游戏 消遣GAME 持续更新中!

    一个抓猫的游戏 版本 Catch_Cat_V0.30 https://files-cdn.cnblogs.com/files/send-off-a-friend/Catch_Cat_V0.3.rar ...

  5. opencv —— 调用摄像头采集图像 VideoCapture capture(0);

    如果要调用摄像头进行视频采集,将代码 VideoCapture capture("C:/Users/齐明洋/Desktop/1.mp4"); 中的 "C:/Users/齐 ...

  6. github访问过慢

    转:https://baijiahao.baidu.com/s?id=1608100091125662190&wfr=spider&for=pc https://www.cnblogs ...

  7. .net core3.0 webapi搭建(一)

    一.创建WebApi项目: 生成项目之后,控制器默认生成了一个WeatherForecastController 我们可以直接启动项目,F5调试,默认进入WeatherForecastControll ...

  8. javascript当中arguments用法

    8)arguments 例 3.8.1<head>    <meta http-equiv="content-type" content="text/h ...

  9. gitlab持续集成,自动部署

    写这篇文章的目的是,实现提交代码到gitlab上的一个项目时,自动打包,并将包发布到另一台服务器上,重启tomat.如有问题,可以联系本人QQ:409838567 gitlab-ci,是基于gitla ...

  10. sudo的简单用法

    su: Switch User, 以管理员身份运行某些命令: su -l root -c 'COMMAND' 但是想要限制某个用户只拥有一部分管理员权限,而不是拥有全部权限,这就需要用到sudo su ...