Spring IOC源码:obtainFreshBeanFactory 详解(上)

本文涉及的产品
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
简介: Spring IOC源码:obtainFreshBeanFactory 详解(上)

文章目录

Spring源码系列:

前言

正文

方法1:prepareRefresh

方法2:obtainFreshBeanFactory

方法3:refreshBeanFactory

方法4:createBeanFactory

方法5:new DefaultListableBeanFactory

方法6:customizeBeanFactory

方法7:loadBeanDefinitions

方法8:loadBeanDefinitions

方法9:loadBeanDefinitions

方法10:doLoadBeanDefinitions

方法11:doLoadBeanDefinitions

方法12:registerBeanDefinitions

总结

Spring源码系列:

Spring IOC源码:简单易懂的Spring IOC 思路介绍

Spring IOC源码:核心流程介绍

Spring IOC源码:ApplicationContext刷新前准备工作

Spring IOC源码:obtainFreshBeanFactory 详解(上)

Spring IOC源码:obtainFreshBeanFactory 详解(中)

Spring IOC源码:obtainFreshBeanFactory 详解(下)

Spring IOC源码:<context:component-scan>源码详解

Spring IOC源码:invokeBeanFactoryPostProcessors 后置处理器详解

Spring IOC源码:registerBeanPostProcessors 详解

Spring IOC源码:实例化前的准备工作

Spring IOC源码:finishBeanFactoryInitialization详解

Spring IoC源码:getBean 详解

Spring IoC源码:createBean( 上)

Spring IoC源码:createBean( 中)

Spring IoC源码:createBean( 下)

Spring IoC源码:finishRefresh 完成刷新详解

前言

上篇文章我们讲解了refresh()方法前的准备工作,主要是初始化一些缓存容器、环境属性,还有对配置文件路径进行解析,查找和替换占位符等。Spring IOC源码:ApplicationContext刷新前准备工作

这节文章介绍refresh方法中的prepareRefresh()跟obtainFreshBeanFactory()方法。prepareRefresh比较简单主要是设置一下上下文的状态、开始时间等。obtainFreshBeanFactory是一个比较核心的方法,主要解析配置文件,封装成BeanDefinition,并放入上下文缓存中。

几个主要的缓存:

beanDefinitionNames缓存:所有被加载到 BeanFactory 中的 bean 的 beanName 集合,后面实例化遍历此集合。

beanDefinitionMap缓存:所有被加载到 BeanFactory 中的 bean 的 beanName 和 BeanDefinition 映射,后续通过beanName获取。

aliasMap缓存:所有被加载到 BeanFactory 中的 bean 的 beanName 和别名映射。

正文

方法1:prepareRefresh

  protected void prepareRefresh() {
    //设置当前上下文状态
    this.startupDate = System.currentTimeMillis();
    this.closed.set(false);
    this.active.set(true);
    if (logger.isDebugEnabled()) {
      if (logger.isTraceEnabled()) {
        logger.trace("Refreshing " + this);
      }
      else {
        logger.debug("Refreshing " + getDisplayName());
      }
    }
    //初始化当前上下文属性的上下文,该方法内容为空,主要提供给子类进行实现
    initPropertySources();
    //验证所有标记为必需的属性都是可解析的
    getEnvironment().validateRequiredProperties();
    //下面是初始化监听器和事件源集合
    // Store pre-refresh ApplicationListeners...
    if (this.earlyApplicationListeners == null) {
      this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
    }
    else {
      // Reset local application listeners to pre-refresh state.
      this.applicationListeners.clear();
      this.applicationListeners.addAll(this.earlyApplicationListeners);
    }
    // Allow for the collection of early ApplicationEvents,
    // to be published once the multicaster is available...
    this.earlyApplicationEvents = new LinkedHashSet<>();
  }

initPropertySources拓展方法案例:

    @Override
    protected void initPropertySources() {
        System.out.println("扩展initPropertySource");
        //这里添加了一个name属性到Environment里面,以方便我们在后面用到
        getEnvironment().getSystemProperties().put("name","bobo");
        //这里要求Environment中必须包含username属性,如果不包含,则抛出异常
        getEnvironment().setRequiredProperties("username");
    }

方法2:obtainFreshBeanFactory

  protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
    refreshBeanFactory();
    return getBeanFactory();
  }

重点在于refreshBeanFactory方法,见方法3

方法3:refreshBeanFactory

  protected final void refreshBeanFactory() throws BeansException {
    //判断一下当前上下文是否有实例化beanFactory,存在则销毁,后面会重新构建
    if (hasBeanFactory()) {
      destroyBeans();
      closeBeanFactory();
    }
    try {
      //实例化一个beanFactory
      DefaultListableBeanFactory beanFactory = createBeanFactory();
      //设置ID
      beanFactory.setSerializationId(getId());
      // 定制beanFactory,设置相关属性,包括是否允许覆盖同名称的不同定义的对象以及循环依赖.属于拓展方法
      customizeBeanFactory(beanFactory);
      // 初始化documentReader,并进行XML文件读取及解析,默认命名空间的解析,自定义标签的解析
      //封装BeanDefinition对象用于后续实例化初始化等工作做准备
      loadBeanDefinitions(beanFactory);
      synchronized (this.beanFactoryMonitor) {
        this.beanFactory = beanFactory;
      }
    }
    catch (IOException ex) {
      throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
    }
  }

createBeanFactory(),实例化BeanFactory工厂对象,见方法4

customizeBeanFactory(beanFactory),设置相关属性,包括是否允许覆盖同名称的不同定义的对象以及循环依赖. 见方法

loadBeanDefinitions(beanFactory),见方法7详解

方法4:createBeanFactory

  protected DefaultListableBeanFactory createBeanFactory() {
    return new DefaultListableBeanFactory(getInternalParentBeanFactory());
  }

new DefaultListableBeanFactory:见方法5

getInternalParentBeanFactory:获取父类内部的BeanFactory,如果该父类实现了ConfigurableApplicationContext ,则返回其BeanFactory属性,不然返回父类本身。这里因为父类也是AbstractApplicationContext的子类,而AbstractApplicationContext又实现了BeanFactoty,所以本身也是一个BeanFactoty。


dc7b3e7490612cc67de536cd9eedad9f_62eca1addff144299c67b94bcc8b263b.png

dc7b3e7490612cc67de536cd9eedad9f_62eca1addff144299c67b94bcc8b263b.png

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

方法5:new DefaultListableBeanFactory

实例化一个BeanFactory时,会调用多层父类的构造方法。

调用父类AbstractAutowireCapableBeanFactory有参构造方法时,会父类的BeanFactory设置到当前属性值中

  public AbstractAutowireCapableBeanFactory(@Nullable BeanFactory parentBeanFactory) {
    this();
    setParentBeanFactory(parentBeanFactory);
  }

调用父类AbstractAutowireCapableBeanFactory无参构造方法时,会设置忽略些属性值

  public AbstractAutowireCapableBeanFactory() {
    super();
    //忽略属性注入时,不设置该值。一般是通过postProcessor进行注入的
    ignoreDependencyInterface(BeanNameAware.class);
    ignoreDependencyInterface(BeanFactoryAware.class);
    ignoreDependencyInterface(BeanClassLoaderAware.class);
  }

方法6:customizeBeanFactory

这个方法可以在子类中进行拓展,设置其属性值,案例如下:l

    @Override
    protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
        super.setAllowBeanDefinitionOverriding(false);
        super.setAllowCircularReferences(false);
        super.customizeBeanFactory(beanFactory);
    }

方法7:loadBeanDefinitions

这个方法比较复杂,主要是用来解析配置、封装等;

  protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
    // 将BeanDefinition封装成XmlBeanDefinitionReader,后续对其操作都是使用XmlBeanDefinitionReader
    XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
    //给其设置些属性
    beanDefinitionReader.setEnvironment(this.getEnvironment());
    beanDefinitionReader.setResourceLoader(this);
    beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
    //提供拓展方法,供子类进行拓展,可以对beanDefinitionReader进行一些初始化操作
    initBeanDefinitionReader(beanDefinitionReader);
    //解析配置
    loadBeanDefinitions(beanDefinitionReader);
  }

loadBeanDefinitions(beanDefinitionReader);见方法8

方法8:loadBeanDefinitions

这里我们可以看都有两个配置文件格式,这些都是在进入refresh前进行占位符替换,赋值的。不管是哪种格式,最终都是以Resource格式进行的,String数组在后续步骤中也会转为Resource;接下来我们看下reader.loadBeanDefinitions(configLocations),见方法9

  protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
    //获取配置文件路径,refresh方法前做的操作,这里有两个类型一种是Resource一种是String
    Resource[] configResources = getConfigResources();
    if (configResources != null) {
      reader.loadBeanDefinitions(configResources);
    }
    String[] configLocations = getConfigLocations();
    if (configLocations != null) {
      reader.loadBeanDefinitions(configLocations);
    }
  }

方法9:loadBeanDefinitions

  public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
    Assert.notNull(locations, "Location array must not be null");
    int count = 0;
    for (String location : locations) {
      count += loadBeanDefinitions(location);
    }
    return count;
  }


遍历执行loadBeanDefinitions(location);

  public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
    return loadBeanDefinitions(location, null);
  }

再接下看loadBeanDefinitions(location, null);

  public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
    //获取上下文的资源加载器
    ResourceLoader resourceLoader = getResourceLoader();
    if (resourceLoader == null) {
      throw new BeanDefinitionStoreException(
          "Cannot load bean definitions from location [" + location + "]: no ResourceLoader available");
    }
    //判断加载器类型
    if (resourceLoader instanceof ResourcePatternResolver) {
      // Resource pattern matching available.
      try {
        //将传进来的配置文件路径进行解析,判断路径中是否含有classPath*: 或war:等,对其解析成多个Resource
        Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
        //解析处理
        int count = loadBeanDefinitions(resources);
        if (actualResources != null) {
          Collections.addAll(actualResources, resources);
        }
        if (logger.isTraceEnabled()) {
          logger.trace("Loaded " + count + " bean definitions from location pattern [" + location + "]");
        }
        return count;
      }
      catch (IOException ex) {
        throw new BeanDefinitionStoreException(
            "Could not resolve bean definition resource pattern [" + location + "]", ex);
      }
    }
    else {
      // 通过路径封装成一个Resource 
      Resource resource = resourceLoader.getResource(location);
      int count = loadBeanDefinitions(resource);
      if (actualResources != null) {
        actualResources.add(resource);
      }
      if (logger.isTraceEnabled()) {
        logger.trace("Loaded " + count + " bean definitions from location [" + location + "]");
      }
      return count;
    }
  }

封装完Resource后,继续进行配置文件的解析操作 loadBeanDefinitions(resources);

  public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
    return loadBeanDefinitions(new EncodedResource(resource));
  }
  public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
    Assert.notNull(encodedResource, "EncodedResource must not be null");
    if (logger.isTraceEnabled()) {
      logger.trace("Loading XML bean definitions from " + encodedResource);
    }
    //加载获取EncodedResource,如果为空则创建一个空集合
    Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
    if (currentResources == null) {
      currentResources = new HashSet<>(4);
      this.resourcesCurrentlyBeingLoaded.set(currentResources);
    }
    if (!currentResources.add(encodedResource)) {
      throw new BeanDefinitionStoreException(
          "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
    }
    try {
      //获取到当前Resource的输入流
      InputStream inputStream = encodedResource.getResource().getInputStream();
      try {
        //封装成InputSource,方便对其类型进行解析
        InputSource inputSource = new InputSource(inputStream);
        if (encodedResource.getEncoding() != null) {
          inputSource.setEncoding(encodedResource.getEncoding());
        }
        //在Spring中看到do开头的,代表是真正的逻辑处理的地方了
        return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
      }
      finally {
        inputStream.close();
      }
    }
    catch (IOException ex) {
      throw new BeanDefinitionStoreException(
          "IOException parsing XML document from " + encodedResource.getResource(), ex);
    }
    finally {
      currentResources.remove(encodedResource);
      if (currentResources.isEmpty()) {
        this.resourcesCurrentlyBeingLoaded.remove();
      }
    }
  }

doLoadBeanDefinitions(inputSource, encodedResource.getResource()); 见方法10

方法10:doLoadBeanDefinitions

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
      throws BeanDefinitionStoreException {
    try {
      //根据inputSource和resource加载XML文件,并封装成Document
      Document doc = doLoadDocument(inputSource, resource);
      //解析并封装和注册成BeanDefinition
      int count = registerBeanDefinitions(doc, resource);
      if (logger.isDebugEnabled()) {
        logger.debug("Loaded " + count + " bean definitions from " + resource);
      }
      return count;
    }
    catch (BeanDefinitionStoreException ex) {
      throw ex;
    }
    catch (SAXParseException ex) {
      throw new XmlBeanDefinitionStoreException(resource.getDescription(),
          "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
    }
    catch (SAXException ex) {
      throw new XmlBeanDefinitionStoreException(resource.getDescription(),
          "XML document from " + resource + " is invalid", ex);
    }
    catch (ParserConfigurationException ex) {
      throw new BeanDefinitionStoreException(resource.getDescription(),
          "Parser configuration exception parsing XML from " + resource, ex);
    }
    catch (IOException ex) {
      throw new BeanDefinitionStoreException(resource.getDescription(),
          "IOException parsing XML document from " + resource, ex);
    }
    catch (Throwable ex) {
      throw new BeanDefinitionStoreException(resource.getDescription(),
          "Unexpected exception parsing XML document from " + resource, ex);
    }
  }

doLoadDocument(inputSource, resource);见方法11

registerBeanDefinitions(doc, resource);见方法12

方法11:doLoadBeanDefinitions

  protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
     // getValidationModeForResource(resource): 获取XML配置文件的验证模式
     // documentLoader.loadDocument: 加载XML文件,解析封装成 Document
    return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
        getValidationModeForResource(resource), isNamespaceAware());
  }
  protected int getValidationModeForResource(Resource resource) {
    //获取当前的验证模式标识
    int validationModeToUse = getValidationMode();
    //如果为指定则返回指定的
    if (validationModeToUse != VALIDATION_AUTO) {
      return validationModeToUse;
    }
    //解析出配置文件的验证模式
    int detectedMode = detectValidationMode(resource);
    //如果检测出的验证模式不为 VALIDATION_AUTO, 则返回检测出来的验证模式
    if (detectedMode != VALIDATION_AUTO) {
      return detectedMode;
    }
    // Hmm, we didn't get a clear indication... Let's assume XSD,
    // since apparently no DTD declaration has been found up until
    // detection stopped (before finding the document's root tag).
    return VALIDATION_XSD;
  }
  public int getValidationMode() {
    return this.validationMode;
  }
  protected int detectValidationMode(Resource resource) {
    if (resource.isOpen()) {
      throw new BeanDefinitionStoreException(
          "Passed-in Resource [" + resource + "] contains an open stream: " +
          "cannot determine validation mode automatically. Either pass in a Resource " +
          "that is able to create fresh streams, or explicitly specify the validationMode " +
          "on your XmlBeanDefinitionReader instance.");
    }
    InputStream inputStream;
    try {
      inputStream = resource.getInputStream();
    }
    catch (IOException ex) {
      throw new BeanDefinitionStoreException(
          "Unable to determine validation mode for [" + resource + "]: cannot open InputStream. " +
          "Did you attempt to load directly from a SAX InputSource without specifying the " +
          "validationMode on your XmlBeanDefinitionReader instance?", ex);
    }
    try {
      //解析检查出当前的验证标识
      return this.validationModeDetector.detectValidationMode(inputStream);
    }
    catch (IOException ex) {
      throw new BeanDefinitionStoreException("Unable to determine validation mode for [" +
          resource + "]: an error occurred whilst reading from the InputStream.", ex);
    }
  }

一般Spring常用的验证文件是声明方法有两种DTD和XSD,DTD在Spring中已经停止使用了

DTD:(Document type Definition )文档类型定义,是一种xml约束模式语言,是xml文件的验证机制,属于xml文件组成的一部分。保证xml文档格式正确的有效方法。


XSD:(XML Schemas Definition),描述了XML文档的结构。可以用一个指定的XML Schema来验证某个XML文档,以检查XML文档是否符合要求。

获取各个参数完成后,进入加载Document步骤

  @Override
  public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
      ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
    //创建DocumentBuilderFactory 工厂对象
    DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
    if (logger.isTraceEnabled()) {
      logger.trace("Using JAXP provider [" + factory.getClass().getName() + "]");
    }
    //创建解析器
    DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
    // 使用DocumentBuilder解析inputSource返回Document对象
    return builder.parse(inputSource);
  }

方法12:registerBeanDefinitions

  public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
    //通过反射创建默认的DefaultBeanDefinitionDocumentReader,其为BeanDefinitionDocumentReader的子类
    BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
    //记录统计前BeanDefinition的加载个数
    int countBefore = getRegistry().getBeanDefinitionCount();
    //封装BeanDefinition对象,并加入缓存集合中
    documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
    return getRegistry().getBeanDefinitionCount() - countBefore;
  }

documentReader.registerBeanDefinitions(doc, createReaderContext(resource));执行前需要获取参数值,先看下该方法逻辑createReaderContext(resource);

  //创建XmlReaderContext ,用于解析配置文件
  public XmlReaderContext createReaderContext(Resource resource) {
    return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
        this.sourceExtractor, this, getNamespaceHandlerResolver());
  }
  //获取命名空间
  public NamespaceHandlerResolver getNamespaceHandlerResolver() {
    if (this.namespaceHandlerResolver == null) {
      this.namespaceHandlerResolver = createDefaultNamespaceHandlerResolver();
    }
    return this.namespaceHandlerResolver;
  }
  //实例化默认的命名空间
  protected NamespaceHandlerResolver createDefaultNamespaceHandlerResolver() {
    ClassLoader cl = (getResourceLoader() != null ? getResourceLoader().getClassLoader() : getBeanClassLoader());
    return new DefaultNamespaceHandlerResolver(cl);
  }

命名空间记录了加载配置文件的解析器类路径,我们在解析文件的时候,不同的xml文件解析可能不一样的,那如果使用不同的处理器来解析呢,这里就需要用到命名空间了,根据配置文件的命名空间来找到命名空间文件,该文件记录了解析该配置文件的多个解析器类路径。Spring需要用这些来解析处理,所以我们需要先获取。

36c781b107296cc0c34befa8c3188452_8ae982f697f34cf1801615baad952efb.png

2276ef79371ad0fd19ad946afc9bfb64_1138c92f4ff24081a24b06f6ac469e65.png

接下来进入documentReader.registerBeanDefinitions(doc, createReaderContext(resource));

  public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
    this.readerContext = readerContext;
    doRegisterBeanDefinitions(doc.getDocumentElement());
  }

看到do开头,就证明是真正开始核心处理的地方doRegisterBeanDefinitions 见方法13

  protected void doRegisterBeanDefinitions(Element root) {
    BeanDefinitionParserDelegate parent = this.delegate;
    this.delegate = createDelegate(getReaderContext(), root, parent);
    //校验root节点的命名空间是否为默认的命名空间(默认命名空间http://www.springframework.org/schema/beans)
    if (this.delegate.isDefaultNamespace(root)) {
      //获取profile属性
      String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
      if (StringUtils.hasText(profileSpec)) {
        String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
            profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
        // We cannot use Profiles.of(...) since profile expressions are not supported
        // in XML config. See SPR-12458 for details.
        if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
          if (logger.isDebugEnabled()) {
            logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
                "] not matching: " + getReaderContext().getResource());
          }
          return;
        }
      }
    }
    //拓展方法,没有实现,主要留给子类拓展使用
    preProcessXml(root);
    parseBeanDefinitions(root, this.delegate);
    //拓展方法,没有实现,主要留给子类拓展使用
    postProcessXml(root);
    this.delegate = parent;
  }

profile属性主要用于环境开发,比如xml文件中不同环境dev、sit、uat、prod设置不同的配置。

进入 parseBeanDefinitions(root, this.delegate)方法

  protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
    //判断当前命名空间是否为默认
    if (delegate.isDefaultNamespace(root)) {
      NodeList nl = root.getChildNodes();
      for (int i = 0; i < nl.getLength(); i++) {
        Node node = nl.item(i);
        if (node instanceof Element) {
          Element ele = (Element) node;
          if (delegate.isDefaultNamespace(ele)) {
            //默认命名空间节点的处理,例如: <bean id="test" class="" />
            parseDefaultElement(ele, delegate);
          }
          else {
            // 自定义命名空间节点的处理,例如:<context:component-scan/>、<aop:aspectj-autoproxy/>
            delegate.parseCustomElement(ele);
          }
        }
      }
    }
    else {
      //非默认命名空间走其它逻辑
      delegate.parseCustomElement(root);
    }
  }

这里首先会判断当前配置的命名空间,如果不是默认的,则走自定义的逻辑,如 <context:component-scan /> 、<aop:aspectj-autoproxy />。否则是默认命令空间,则获取其子元素遍历判断,调用其默认处理逻辑,如 。

parseDefaultElement(ele, delegate) 和 delegate.parseCustomElement(ele) 方法是解析 bean 定义的两个核心方法,在后续文章后会对其进行讲解。

总结

这节内容主要讲解了配置文件解析前的处理及解析处理过程:


1.创建一个新的 BeanFactory:DefaultListableBeanFactory。

2.根据 web.xml 中 contextConfigLocation 配置的路径,读取 Spring配置文件,验证配置文件的内容,并封装成 Resource。

3.根据 Resource 加载 XML 配置文件,并解析成 Document 对象 。

4.拿到 Document 中的根节点,遍历根节点和所有子节点。

对于xml文件有默认处理方法和自定义处理方法,Spring会根据命名空间判断选择不同的对应逻辑。


目录
相关文章
|
1月前
|
XML Java 测试技术
Spring IOC—基于注解配置和管理Bean 万字详解(通俗易懂)
Spring 第三节 IOC——基于注解配置和管理Bean 万字详解!
135 26
|
3月前
|
XML Java 数据格式
【SpringFramework】Spring IoC-基于XML的实现
本文主要讲解SpringFramework中IoC和DI相关概念,及基于XML的实现方式。
123 69
|
8天前
|
存储 监控 数据可视化
SaaS云计算技术的智慧工地源码,基于Java+Spring Cloud框架开发
智慧工地源码基于微服务+Java+Spring Cloud +UniApp +MySql架构,利用传感器、监控摄像头、AI、大数据等技术,实现施工现场的实时监测、数据分析与智能决策。平台涵盖人员、车辆、视频监控、施工质量、设备、环境和能耗管理七大维度,提供可视化管理、智能化报警、移动智能办公及分布计算存储等功能,全面提升工地的安全性、效率和质量。
|
3月前
|
Java Spring 容器
【SpringFramework】Spring IoC-基于注解的实现
本文主要记录基于Spring注解实现IoC容器和DI相关知识。
70 21
|
3月前
|
设计模式 XML Java
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
本文详细介绍了Spring框架的核心功能,并通过手写自定义Spring框架的方式,深入理解了Spring的IOC(控制反转)和DI(依赖注入)功能,并且学会实际运用设计模式到真实开发中。
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
|
2月前
|
监控 JavaScript 数据可视化
建筑施工一体化信息管理平台源码,支持微服务架构,采用Java、Spring Cloud、Vue等技术开发。
智慧工地云平台是专为建筑施工领域打造的一体化信息管理平台,利用大数据、云计算、物联网等技术,实现施工区域各系统数据汇总与可视化管理。平台涵盖人员、设备、物料、环境等关键因素的实时监控与数据分析,提供远程指挥、决策支持等功能,提升工作效率,促进产业信息化发展。系统由PC端、APP移动端及项目、监管、数据屏三大平台组成,支持微服务架构,采用Java、Spring Cloud、Vue等技术开发。
115 7
|
3月前
|
存储 Java 应用服务中间件
【Spring】IoC和DI,控制反转,Bean对象的获取方式
IoC,DI,控制反转容器,Bean的基本常识,类注解@Controller,获取Bean对象的常用三种方式
|
3月前
|
存储 缓存 Java
Spring面试必问:手写Spring IoC 循环依赖底层源码剖析
在Spring框架中,IoC(Inversion of Control,控制反转)是一个核心概念,它允许容器管理对象的生命周期和依赖关系。然而,在实际应用中,我们可能会遇到对象间的循环依赖问题。本文将深入探讨Spring如何解决IoC中的循环依赖问题,并通过手写源码的方式,让你对其底层原理有一个全新的认识。
88 2
|
10月前
|
Java 关系型数据库 数据库连接
Spring源码解析--深入Spring事务原理
本文将带领大家领略Spring事务的风采,Spring事务是我们在日常开发中经常会遇到的,也是各种大小面试中的高频题,希望通过本文,能让大家对Spring事务有个深入的了解,无论开发还是面试,都不会让Spring事务成为拦路虎。
127 1
|
5月前
|
Java Spring
Spring底层架构源码解析(三)
Spring底层架构源码解析(三)
260 5