Spring IOC容器初始化主体流程

2023-11-16

Spring IOC容器初始化主体流程

Spring IOC的容器体系

IOC容器时Spring的核心模块,是抽象了对象管理、依赖关系管理的框架解决方案。

Spring 提供了很多的容器,其中BeanFactory是顶层容器(根容器),不能被实例化,它定义了所有的IOC容器必须遵从的一套原则,具体的容器实现可以增加额外的功能。

比如我们常用到的ApplicationContext,其下更具体的实现如 ClassPathXMLApplicationContext包含了解析xml等一系列的内容。

AnnotationConfigApplicationContext则是包含了注解解析等一系列的内容。Spring IOC容器继承体系设计得非常游侠,需要使用哪个接口实现哪个接口即可,不必实现一个功能复杂的大接口。

BeanFactory顶级接口的方法如下:
在这里插入图片描述
BeanFactory 容器继承体系:
在这里插入图片描述
通过其接口设计,我们可以看到我们一贯使用的ApplicationContext除了继承BeanFactory的子接口,还继承了ResourceLoader、MessageSource等接口,因此其提供的功能也就更丰富了。

下面我们以ClassPathXmlApplicationContext 为例,深入源码说明IOC容器的初始化流程

Bean生命周期关键时机点

思路:创建一个MyBean,让其实现几个特殊的接口,并分别在接口实现的构造器、接口方法中断点,观察线程调用栈,分析出Bean对象创建和管理关键点的触发时机

MyBean类:

public class MyBean implements InitializingBean {

	private int id;

	private String name;

	public MyBean() {
		System.out.println("构造器执行了......");
	}

	@Override
	public void afterPropertiesSet() throws Exception {
		System.out.println("afterPropertiesSet执行了......");
	}
}

MyBeanFactoryPostProcessor 后置处理器工厂实现类:

public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

	public MyBeanFactoryPostProcessor(){
		System.out.println("MyBeanFactoryPostProcessor的构造函数......");
	}

	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		System.out.println("MyBeanFactoryPostProcessor的实现方法调用了......");
	}
}

MyBeanPostProcessor 后置处理器实现类:

public class MyBeanPostProcessor implements BeanPostProcessor {

	public MyBeanPostProcessor() {
		System.out.println("MyBeanPostProcessor构造方法调用了......");
	}

	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		if("myBean".equals(beanName)){
			System.out.println("MyBeanPostProcessor的before方法调用了......");
		}
		return bean;
	}

	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		if("myBean".equals(beanName)){
			System.out.println("MyBeanPostProcessor的after方法调用了......");
		}
		return bean;
	}
}

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	   xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">
	
	<bean id="myBean" class="com.myspring.demo.MyBean">
	</bean>

	<bean id="myBeanPostProcessor" class="com.myspring.demo.MyBeanPostProcessor"/>
	<bean id="myBeanFactoryPostProcessor" class="com.myspring.demo.MyBeanFactoryPostProcessor"/>
	
</beans>

测试代码:

public class MyTest {

	@Test
	public void	MySpringTest(){
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
		MyBean myBean = (MyBean) applicationContext.getBean("myBean");
		System.out.println(myBean);
	}
}

我们在MyTestMyBean myBean = (MyBean) applicationContext.getBean("myBean"); 这行代码打断点:

  • 分析Bean的创建是在容器初始化还是在getBean时:
    在这里插入图片描述

  • 根据断点调试,我们发现,在未设置延迟加载的前提下,Bean的创建是在容器初始化过程中完成的

  • 分析构造函数的调用情况:

    在MyBean的构造函数打上断点,debug运行,我们发现构造函数的调用时机是在 AbstractApplicationContext类中的refresh()方法里面调用了finishBeanFactoryInitialization()方法

  • 分析 MyBean 实现的InitializingBean 接口实现方法afterPropertiesSet 初始化方法调用情况

    System.out.println("afterPropertiesSet执行了......");打上断点,我们观察调用栈,MyBean 实现的InitializingBean 接口实现方法afterPropertiesSet 初始化方法的调用时机也是在AbstractApplicationContext类中的refresh()方法里面调用了finishBeanFactoryInitialization()方法

  • SpringBean工厂的后置处理器的BeanFactoryPostProcessor的构造器、postProcessBeanFactory方法是哪个方法调用的?

    方法同上,在对应代码出打上断点,debug运行,观察调用栈,发现是在

    AbstractApplicationContext#refresh()#invokeBeanFactoryPostProcessors 处调用的

  • SpringBean的后置处理器BeanPostProcessor的构造方法是哪个方法调用的?

    AbstractApplicationContext#refresh()#registerBeanPostProcessors 处调用的

  • SpringBean的后置处理器BeanPostProcessor的Before和after方法是哪个方法调用的?

    AbstractApplicationContext#refresh()#finishBeanFactoryInitialization 处调用的

总结:

根据上面的调试分析,我们发现Bean对象创建的几个关键时机点代码层级的调用都在 AbstractApplicationContext 类 的 refresh 方法中,可见这个方法对于SpringIOC容器初始化来说相当关键,汇总如下:

关键点 触发代码
构造函数 refresh#finishBeanFactoryInitialization(beanFactory)(beanFactory)
BeanFactoryPostProcessor 初始化 refresh#invokeBeanFactoryPostProcessors(beanFactory)
BeanFactoryPostProcessor ⽅法调⽤ refresh#invokeBeanFactoryPostProcessors(beanFactory)
BeanPostProcessor 初始化 registerBeanPostProcessors(beanFactory)
BeanPostProcessor ⽅法调⽤ refresh#finishBeanFactoryInitialization(beanFactory)

SpringIOC容器初始化主流程

由上分析可知,SpringIOC容器初始化的关键环节就在 **AbstractApplicationContext#refresh() **方法中,我们查看refresh方法来俯瞰容器创建的主题流程。

	@Override
	public void refresh() throws BeansException, IllegalStateException {
		// 对象锁加锁  refresh()、registerShutdownHook()、close()时加锁
		synchronized (this.startupShutdownMonitor) {
			/*
				Prepare this context for refreshing.
				刷新前的预处理
				表示在真正做refresh操作之前需要准备做的事情:
					设置Spring容器的启动时间
					开启活跃状态,撤销关闭状态
					验证环境信息里一些必须存在的属性等
			 */
			prepareRefresh();

			/*
				Tell the subclass to refresh the internal bean factory.
				获取BeanFactory,默认实现是DefaultListableBeanFactory
				加载BeanDefinition 并注册到BeanDefinitionRegistry
			 */
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			/*
				Prepare the bean factory for use in this context.
				BeanFactory的预准备工作(BeanFactory进行一些设置,比如context的类加载器等)
			 */
 			prepareBeanFactory(beanFactory);

			try {
				/*
				 	Allows post-processing of the bean factory in context subclasses.
				 	BeanFactory准备工作完成后的后置处理工作
				 */
				postProcessBeanFactory(beanFactory);

				/*
				 	Invoke factory processors registered as beans in the context.
				 	实例化实现了BeanFactoryPostProcessor接口的bean,并调用接口方法
				 */
				invokeBeanFactoryPostProcessors(beanFactory);

				/*
				 	Register bean processors that intercept bean creation.
				 	注册BeanPostProcessor(Bean的后置处理器),在创建Bean的前后等执行
				 */
				registerBeanPostProcessors(beanFactory);

				/*
				 	Initialize message source for this context.
				 	初始化MessageSource组件(做国际化功能:消息绑定,消息解析)
				 */
				initMessageSource();

				/*
				 	Initialize event multicaster for this context.
				 	初始化事件派发器
				 */
				initApplicationEventMulticaster();

				/*
				 	Initialize other special beans in specific context subclasses.
				 	子类重写这个方法,在容器刷新的时候可以自定义逻辑;如创建Tomcat、jetty等web服务器
				 */
				onRefresh();

				/*
				 	Check for listener beans and register them.
				 	注册应用的监听器,就是注册实现了ApplicationListener接口的监听器bean
				 */
				registerListeners();

				/*
				 	Instantiate all remaining (non-lazy-init) singletons.
				 	初始化所有剩下的非延迟加载的单例bean
				 	初始化创建非延迟加载的单例bean实例(未设置属性)
				 	填充属性
				 	初始化方法调用(比如调用afterPropertiesSet方法、init-method方法)
				 	调用BeanPostProcessor(后置处理器)对实例bean进行后置处理
				 */
				finishBeanFactoryInitialization(beanFactory);

				/*
				 	Last step: publish corresponding event.
				 	完成context的刷新。主要调用LifecycleProcessor的onRefresh()方法,并且发布事件(ContextRefreshedEvent)
				 */
				finishRefresh();
			}
            
            ......

		}
	}

BeanFactory创建流程

获取BeanFactory子流程

时序图如下:
在这里插入图片描述

BeanDefinition加载解析及注册子流程

  • 该子流程涉及到如下几个关键步骤:

    • Resource定位:指对BeanDefinition 的资源定位过程。通俗讲就是找到定义JavaBean信息的xml文件,并将其封装成Resource对象
    • BeanDefinition 载入:把用户定义好的JavaBean表示为IOC容器内部的数据结构,这个容器内部是数据结构就是BeanDefinition
    • 注册BeanDefinition 到IOC容器
  • 过程分析

    • step1:子流程入口在 AbstractRefreshableApplicationContext#refreshBeanFactory 方法中

      	@Override
      	protected final void refreshBeanFactory() throws BeansException {
      		// 判断是否已有BeanFactory
      		if (hasBeanFactory()) {
      			// 销毁 beans
      			destroyBeans();
      			// 关闭 BeanFactory
      			closeBeanFactory();
      		}
      		try {
      			// 实例化 DefaultListableBeanFactory
      			DefaultListableBeanFactory beanFactory = createBeanFactory();
      			// 设置序列化ID
      			beanFactory.setSerializationId(getId());
      			// 自定义bean工厂的一些属性(是否覆盖、是否允许循环依赖)
      			customizeBeanFactory(beanFactory);
      			// 加载应用中的BeanDefinition   
      			loadBeanDefinitions(beanFactory);  // 注意这里就是入口
      			// 赋值
      			this.beanFactory = beanFactory;
      		}
      		catch (IOException ex) {
      			throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
      		}
      	}
      
    • step2:依次调用多个类的 loadBeanDefinitions 方法 —> AbstractXmlApplicationContext —> AbstractBeanDefinitionReader —> XmlBeanDefinitionReader ⼀直执行到 XmlBeanDefinitionReader 的 doLoadBeanDefinitions 方法

      protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
          throws BeanDefinitionStoreException {
      
          try {
              // 读取xml信息,将xml信息保存到Document对象
              Document doc = doLoadDocument(inputSource, resource);
              // 解析document对象,封装BeanDefinition对象并进行注册
              int count = registerBeanDefinitions(doc, resource);
              if (logger.isDebugEnabled()) {
                  logger.debug("Loaded " + count + " bean definitions from " + resource);
              }
              return count;
          }
      }
      
    • step3:我们重点观察XMLBeanDefinitionReader 类的 registerBeanDefinition 方法,期间产生了多次重载调用,我们定位到最后一个

      public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
          BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
          // 获取已有BeanDefinition的数量
          int countBefore = getRegistry().getBeanDefinitionCount();
          // 注册BeanDefinition
          documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
          // 返回新注册的BeanDefinition
          return getRegistry().getBeanDefinitionCount() - countBefore;
      }
      

      此处我们关注两个地方:createReaderContextregisterBeanDefinitions

      • createReaderContext

        public XmlReaderContext createReaderContext(Resource resource) {
            return new XmlReaderContext(resource, this.problemReporter, this.eventListener,this.sourceExtractor, this, getNamespaceHandlerResolver());
        }
        
        /**
        	 * Lazily create a default NamespaceHandlerResolver, if not set before.
        	 * @see #createDefaultNamespaceHandlerResolver()
        	 */
        public NamespaceHandlerResolver getNamespaceHandlerResolver() {
            if (this.namespaceHandlerResolver == null) {
                this.namespaceHandlerResolver = createDefaultNamespaceHandlerResolver();
            }
            return this.namespaceHandlerResolver;
        }
        
        /**
        	 * Create the default implementation of {@link NamespaceHandlerResolver} used if none is specified.
        	 * <p>The default implementation returns an instance of {@link DefaultNamespaceHandlerResolver}.
        	 * @see DefaultNamespaceHandlerResolver#DefaultNamespaceHandlerResolver(ClassLoader)
        	 */
        protected NamespaceHandlerResolver createDefaultNamespaceHandlerResolver() {
            ClassLoader cl = (getResourceLoader() != null ? getResourceLoader().getClassLoader() : getBeanClassLoader());
            return new DefaultNamespaceHandlerResolver(cl);
        }
        

        我们可以看到,此处Spring首先完成了 NamespaceHandlerResolve 的初始化

      • 我们再进入 registerBeanDefinitions 方法中追踪,调用了 DefaultBeanDefinitionDocumentReader#registerBeanDefinitions 方法

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

        进入 doRegisterBeanDefinitions 方法:

        protected void doRegisterBeanDefinitions(Element root) {
        
        	......
        
            preProcessXml(root);	
            parseBeanDefinitions(root, this.delegate);	// 重点
            postProcessXml(root);
        
            this.delegate = parent;
        }
        

        进入 parseBeanDefinitions 方法后再进入 parseDefaultElement 方法:

        private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
            // import元素处理
            if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
                importBeanDefinitionResource(ele);
            }
            // alias元素处理
            else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
                processAliasRegistration(ele);
            }
            // bean元素处理
            else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
                processBeanDefinition(ele, delegate);	// 这里就是解析bean元素
            }
            // 嵌套beans处理
            else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
                // recurse
                doRegisterBeanDefinitions(ele);
            }
        }
        

        进入 processBeanDefinition 方法:

        protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
            // 解析bean元素为BeanDefinition,但是此时使用 BeanDefinitionHolder 又包装成了 BeanDefinitionHolder对象
            BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
            if (bdHolder != null) {
                /*
        				<bean id="myBean" class="com.myspring.demo.MyBean">
        					<property name="beanName" value="bean demo"/>
        					<meta key="demo" value="demo"/>
        					<mybean:username="mybean"/>
        				</bean>
        				如果有自定义标签,则处理自定义标签
        			 */
                bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
                try {
                    // Register the final decorated instance.
                    // 完成BeanDefinition的注册
                    BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
                }
                catch (BeanDefinitionStoreException ex) {
        		......
            }
        }
        

至此,注册流程结束,我们发现,所谓的注册就是把封装的XML中定义的Bean信息封装为BeanDefinition对象后放入一个Map中,BeanFactory 是以Map的结构组织这些BeanDefinition的

可以在 DefaultListableBeanFactory#registerBeanDefinition 看到此Map的定义

/** Map of bean definition objects, keyed by bean name. */
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

Bean创建流程

通过最开始的关键时机点分析,我们知道Bean创建子流程入口在 AbstractApplicationContext#refresh()⽅法的finishBeanFactoryInitialization(beanFactory)

@Override
public void refresh() throws BeansException, IllegalStateException {
    ......

    /*
		Instantiate all remaining (non-lazy-init) singletons.
		初始化所有剩下的非懒加载的单例bean
		初始化创建非懒加载的单例bean实例(未设置属性)
		填充属性
		初始化方法调用(比如调用afterPropertiesSet方法、init-method方法)
		调用BeanPostProcessor(后置处理器)对实例bean进行后置处理
	*/
    finishBeanFactoryInitialization(beanFactory); // bean创建入口

    ......

}

进入finishBeanFactoryInitialization 方法

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {

    ......

	// Instantiate all remaining (non-lazy-init) singletons.
	// 实例化所有立即加载的单例bean
	beanFactory.preInstantiateSingletons();
}

继续进入 DefaultListableBeanFactory 类的 preInstantiateSingletons 方法:

@Override
public void preInstantiateSingletons() throws BeansException {

    ......

    if (bean instanceof FactoryBean) {
        final FactoryBean<?> factory = (FactoryBean<?>) bean;
        boolean isEagerInit;
        if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
            isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>)((SmartFactoryBean<?>) factory)::isEagerInit,getAccessControlContext());
        }
        else {
            isEagerInit = (factory instanceof SmartFactoryBean &&
                           ((SmartFactoryBean<?>) factory).isEagerInit());
        }
        if (isEagerInit) {
            getBean(beanName);
        }
    }
    else {
        // 实例化当前bean
        getBean(beanName);
    }
    
    ......

}

可以看到工厂bean或者普通bean,最终都是通过 getBean 的方法获取实例

继续跟踪下去,我们进入到了 AbstractBeanFactory#doGetBean 方法,这个方法中的代码很多,我们直接找到核心部分

// 创建单例bean
if (mbd.isSingleton()) {
    sharedInstance = getSingleton(beanName, () -> {
        try {
            // 创建bean
            return createBean(beanName, mbd, args);
        }
        catch (BeansException ex) {
            // Explicitly remove instance from singleton cache: It might have been put there
            // eagerly by the creation process, to allow for circular reference resolution.
            // Also remove any beans that received a temporary reference to the bean.
            destroySingleton(beanName);
            throw ex;
        }
    });
    bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}

接着进入到 AbstractAutowireCapableBeanFactory#createBean() 方法

try {
    Object beanInstance = doCreateBean(beanName, mbdToUse, args);
    if (logger.isTraceEnabled()) {
        logger.trace("Finished creating instance of bean '" + beanName + "'");
    }
    return beanInstance;
}

进入 doCreateBean 方法看看,该方法我们关注两块重点区域

  • 创建Bean实例,此时未设置属性
if (instanceWrapper == null) {
    // 创建bean实例,但是尚未设置属性
    instanceWrapper = createBeanInstance(beanName, mbd, args);
}
  • 初始化bean
// 初始化bean实例
Object exposedObject = bean;
try {
    // bean属性填充
    populateBean(beanName, mbd, instanceWrapper);
    // 调用初始化方法,应用BeanPostProcessor后置处理器
    exposedObject = initializeBean(beanName, exposedObject, mbd);
}

到此,SpringIOC容器的初始化流程源码大概就看完了,源码内容非常多,调试起来还是有点懵的,建议多debug几次,不用看每一行代码,只需看下每个函数的主要功能即可。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Spring IOC容器初始化主体流程 的相关文章

随机推荐

  • 【计算机毕业设计】242高校图书馆设计与实现

    一 系统截图 需要演示视频可以私聊 摘 要 现代经济快节奏发展以及不断完善升级的信息化技术 让传统数据信息的管理升级为软件存储 归纳 集中处理数据信息的管理方式 本高校图书馆就是在这样的大环境下诞生 其可以帮助管理者在短时间内处理完毕庞大的
  • react-native 中使用echarts 水波图

    echarts liquidfill min js加放到库中 并在tpl html中引用便可
  • 操作符详解上(非常详细)

    目录 二进制介绍 二进制 2进制转10进制 10进制转2进制数字 2进制转8进制和16进制 2进制转8进制 2进制转16进制 原码 反码 补码 移位操作符 左移操作符 右移操作符 位操作符 逗号表达式 二进制介绍 在初学计算机时我们常常会听
  • GO语言gin框架初步介绍

    1 下载gin框架 go get u github com gin gonic gin 当无法下载时 大概率是被墙了 需要配置环境变量 go env w GOPROXY https goproxy io direct go env w GO
  • Hello World程序 Pycharm

    由于电脑原因 进行了系统重装 所以最近把所用的软件给重新装了一遍 最近突然萌生写博客的想法 那就start 下载完成了Python3 7之后 安装Pycharm python3 7 IDLE界面 那么开启pycharm的第一个程序 开启py
  • GAN的学习记录

    最近看了一下神经网络和卷积神经网络 CNN 的基础概念 然后开始看生成对抗网络 GAN 的基础知识 之后会自己写一下代码 用GAN对数据集进行训练 一 12月的计划 1 先看懂GAN的基础理论 2 找一些代码 想办法把轴承的数据集放到GAN
  • 最近收集的9000个英语单词

    wrong adj 错误的 不道德的 不适合的 不正常的adv 错误地n 坏事 不公正的事 错误v 无礼地对待 冤枉 visualize v 想像 设想 形象化 显现 unwise a 无智的 愚笨的 不智的 unlikely adj 不太
  • 机器学习原来这么有趣 Part3: 深度学习与卷积神经网络

    最近看了Adam Geitgey的机器学习系列文章 寻思着闲着也是闲着 干脆翻译以下 顺便学习下英语啥的哈哈哈 第一次做这种事 有不到位的地方欢迎指教噢 前言 你是否已经厌倦了在查阅了无数有关深度学习的文章之后仍然不能参透其中深意的无力感
  • python PyQt5学习笔记 事件和信号 有注释 p2

    事件和信号 事件 所有的应用都是事件驱动的 事件大部分都是由用户的行为产生的 当然也有其他的事件产生方式 比如网络的连接 窗口管理器或者定时器等 调用应用的exec 方法时 应用会进入主循环 主循环会监听和分发事件 在事件模型中 有三个角色
  • opencv实现人脸识别(c++实现)

    1 说明 本文章基于opencv VS2015 实现人脸检测 2 效果 可以直接打开摄像头对人脸进行识别 这些标识框也会跟随你的人脸移动 隐私问题 我这里对图片进行了识别 3 相关类及函数介绍 opencv中文文档 3 1 cv Video
  • redux react ajax,使用React + Redux实现的组件间实时数据绑定同步

    JavaScript 语言 JaveScriptBabelCoffeeScript 确定 Browser Level React Redux Action function changeGreeting text return type C
  • [JDBC]Exception in thread “main“ java.lang.ClassNotFoundException: com.mysql.cj.jdbc.Driver[我的报错日常]

    问题描述 Java项目连接MySQL数据库时报错 Exception in thread main java lang ClassNotFoundException com mysql jdbc Driver java 问题分析 找不到驱动
  • 华南农业大学数据结构oj 8580 合并链表

    include
  • 初级 - 如何搭建一个Java Web项目 - 记录

    目录 序言 一 使用 Spring Initializr 创建创建一个Java 项目基本框架的方法 1 新建项目时 安装依赖理解 Developer Tools 选项 Web 选项 其他选项具体详情请最下面的参考链接 这里就不一一列举了 只
  • splines

    当变量之间存在非线性关系时 线性回归就不再适用 这时可以转而使用其他非线性模型 但是 线性回归毕竟是统计建模的基础 通过本篇的介绍 可以看到即使是非线性关系有时也可以通过变换然后使用线性回归进行建模 1 多项式回归 多项式回归即是在模型中加
  • yml文件的全局注释和全局取消注释

    yml文件的全局注释和全局取消注释 最近在写微服务的配置文件的时候需要将所有的代码进行注释 手动一行行的添加 来注释实在过于麻烦 经过在网上百度后发现其实是有快捷键的 全局注释 对于 Mac 的话是 Command 对于 Linux 或 W
  • 面向对象(OOP)基础+进阶

    面向对象 OOP 基础 进阶 1 类 类是对现实生活中一类具有共同属性和行为的事物的抽象 类 是对事物 也就是对象的一种描述 可以将类理解为一张设计圈 根据设计圈 可以创建出具体存在的事物 类的组成 1 属性 该事物的各种特征 例如我的属性
  • Python学习经典书籍完整书单整理!!!

    一 Python 基础 01 Python编程 从入门到实践 第2版 本书是针对所有层次Python读者而作的Python入门书 让你可以快速学会Python编程 通过这本书 你将会学到各种Python库和工具 NumPy Pygal等 制
  • 初学gitlab上传代码实践

    初学Gitlabs上传代码实践 1 登录gitlab web页面 2 新建一个项目 可以默认系统的选项 点击创建 成功 3 回到window桌面上 右击选git bash here 前提你已经安装了git本地软件 这个作用是创建密钥 4 c
  • Spring IOC容器初始化主体流程

    文章目录 Spring IOC容器初始化主体流程 Spring IOC的容器体系 Bean生命周期关键时机点 SpringIOC容器初始化主流程 BeanFactory创建流程 获取BeanFactory子流程 BeanDefinition