SpringBoot 源码分析初始化应用上下文(1)-createApplicationContext

news2024/11/25 0:39:12

前言:springBoot的版本是  2.2.4.RELEASE

一、入口

/**
	 * Run the Spring application, creating and refreshing a new
	 * {@link ApplicationContext}.
	 * @param args the application arguments (usually passed from a Java main method)
	 * @return a running {@link ApplicationContext}
	 */
	public ConfigurableApplicationContext run(String... args) {
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		configureHeadlessProperty();
		SpringApplicationRunListeners listeners = getRunListeners(args);
		listeners.starting();
		try {
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
			ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
			configureIgnoreBeanInfo(environment);
			Banner printedBanner = printBanner(environment);
            //主要看这个方法,初始化应用上下文
			context = createApplicationContext();
			exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);
			prepareContext(context, environment, listeners, applicationArguments, printedBanner);
			refreshContext(context);
			afterRefresh(context, applicationArguments);
			stopWatch.stop();
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
			}
			listeners.started(context);
			callRunners(context, applicationArguments);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, listeners);
			throw new IllegalStateException(ex);
		}

		try {
			listeners.running(context);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, null);
			throw new IllegalStateException(ex);
		}
		return context;
	}

问题切入:为什么叫做上下文对象呢?上下文对象,就是当前应用环境下的一个集合

初始化(创建)上下文对象主要看上面注释那行,即:

context = createApplicationContext();

接着看下 createApplicationContext() 这个方法的实现

截图:

代码:

/**
	 * Strategy method used to create the {@link ApplicationContext}. By default this
	 * method will respect any explicitly set application context or application context
	 * class before falling back to a suitable default.
	 * @return the application context (not yet refreshed)
	 * @see #setApplicationContextClass(Class)
	 */
	protected ConfigurableApplicationContext createApplicationContext() {
		Class<?> contextClass = this.applicationContextClass;
		if (contextClass == null) {
			try {
				switch (this.webApplicationType) {
				case SERVLET:
                    // 创建上下文对象 AnnotationConfigServletWebServerApplicationContext 进行实例化的过程中,此时 ioc 容器也会被创建
					contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
					break;
				case REACTIVE:
					contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
					break;
				default:
					contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
				}
			}
			catch (ClassNotFoundException ex) {
				throw new IllegalStateException(
						"Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex);
			}
		}
        //创建 AnnotationConfigServletWebServerApplicationContext 对象的无参构造
		return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
	}

 接着看下AnnotationConfigServletWebServerApplicationContext 这个对象的继承关系

 从继承关系上面看继承关系比较深,同时可以看到 GenericApplicationContext(通用上下文对象),上面实例化AnnotationConfigServletWebServerApplicationContext的时候就是从该对象的无参构造方法开始的,我们就从AnnotationConfigServletWebServerApplicationContext对象的无参构造方法从代码上面看一下代码逻辑,注意:子类实例化的时候先实例化父类的构造方法

截图如下:

 1、AnnotationConfigServletWebServerApplicationContext 的无参构造【注解配置Servlet Web服务器应用程序上下文】

2、ServletWebServerApplicationContext 的无参构造【Servlet Web服务器应用程序上下文】

==> 这一步啥事没做

 3、GenericWebApplicationContext 的无参构造【通用Web应用程序上下文】

==> 这个方法实例化的时候继承了父类

 4、GenericApplicationContext 的无参构造【通用应用程序上下文】

 ==> 这里直接new一个 DefaultListableBeanFactory 对象,这个对象就是IOC容器最开始的样子,

这里其实是已经创建了 bean 工厂, DefaultListableBeanFactory 这个类里面有一个beanDefinitionMap,就是用来存放bean对象的。

二、AnnotationConfigServletWebServerApplicationContext 无参构造的两个重要方法

截图:

代码:

/**
	 * Create a new {@link AnnotationConfigServletWebServerApplicationContext} that needs
	 * to be populated through {@link #register} calls and then manually
	 * {@linkplain #refresh refreshed}.
	 */
	public AnnotationConfigServletWebServerApplicationContext() {
        //创建一个读取注解的Bean定义读取器
        //什么是 Bean 定义?BeanDefinition 完成了 spring 内部 Bean 的 BeanDefinition 的注册(主要是后置处理器)
		this.reader = new AnnotatedBeanDefinitionReader(this);
        //创建 BeanDefinition 扫描器,可以用来扫描包或者类,继而转换为 bd,
        //spring 默认的扫描器其实不是这个 scanner 对象,而是在后面自己又重新 new 了一个 ClassPathBeanDefinitionScanner 
        //spring 在执行工程后置器 ConfigurationClassPostProcessor 时,去扫描包时会 new
        //这里 scanner 仅仅是为了 程序员可以手动调用 AnnotationConfigApplicationContext 对象的 scanner方法
		this.scanner = new ClassPathBeanDefinitionScanner(this);
	}

2.1 AnnotatedBeanDefinitionReader 构造方法

在看这个方法之前,我们先打个断点,然后看下this里面的 beanDefinitionMap 里面是否有有值,即IOC容器中是否有bean对象,可以看到,到这一步还是没有值的,即还没有bean实例存在IOC容器中。接着我们继续跑下一步

到这一步的时候可以看到  beanDefinitionMap 已经有bean存进去了,接下来我们重点看下

AnnotatedBeanDefinitionReader 构造方法 都做了哪些事情。

 AnnotatedBeanDefinitionReader 构造方法

截图:

接着看其有参构造方法

截图:

代码:

/**
	 * Create a new {@code AnnotatedBeanDefinitionReader} for the given registry,
	 * using the given {@link Environment}.
	 * @param registry the {@code BeanFactory} to load bean definitions into,
	 * in the form of a {@code BeanDefinitionRegistry}
	 * @param environment the {@code Environment} to use when evaluating bean definition
	 * profiles.
	 * @since 3.1
	 */
	public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
		Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
		Assert.notNull(environment, "Environment must not be null");
		this.registry = registry;
		this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
        //根据名字顾名思义就是注册注解配置的处理器
        //也就是这个方法里面会注册一些用于处理注解的处理器
		AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
	}

 接着看 registerAnnotationConfigProcessors 这个方法

继续看 registerAnnotationConfigProcessors 方法

截图:

代码:

	/**
	 * Register all relevant annotation post processors in the given registry.
	 * @param registry the registry to operate on
	 * @param source the configuration source element (already extracted)
	 * that this registration was triggered from. May be {@code null}.
	 * @return a Set of BeanDefinitionHolders, containing all bean definitions
	 * that have actually been registered by this call
	 */
	public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
			BeanDefinitionRegistry registry, @Nullable Object source) {
        //unwrap 打开...的包装,这里即获取默认bean工厂对象
		DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);
		if (beanFactory != null) {
			if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) {
			//主要解析 @Order 和 @Priority	beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
			}
			if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) {
            //提供处理延迟加载的功能
				beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
			}
		}

		Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8);
        //spring 默认 BeanDefinition 的注册
		if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
			RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
			def.setSource(source);
			beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
		}
        //注册 AutowiredAnnotationBeanPostProcessor
		if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
			RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
			def.setSource(source);
			beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
		}
         //注册 CommonAnnotationBeanPostProcessor
		// Check for JSR-250 support, and if present add the CommonAnnotationBeanPostProcessor.
		if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {
			RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
			def.setSource(source);
			beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
		}

		// Check for JPA support, and if present add the PersistenceAnnotationBeanPostProcessor.
		if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) {
			RootBeanDefinition def = new RootBeanDefinition();
			try {
				def.setBeanClass(ClassUtils.forName(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME,
						AnnotationConfigUtils.class.getClassLoader()));
			}
			catch (ClassNotFoundException ex) {
				throw new IllegalStateException(
						"Cannot load optional framework class: " + PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, ex);
			}
			def.setSource(source);
			beanDefs.add(registerPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME));
		}

		if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) {
			RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);
			def.setSource(source);
			beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME));
		}

		if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) {
			RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);
			def.setSource(source);
			beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME));
		}

		return beanDefs;
	}

 ==> 这里其实就是6个默认的spring内部bean的注册

(1) ConfigurationClassPostProcessor 配置类后置处理器

工厂后置处理器,这个后置处理器非常重要,基本上类上面的注解都在这里面判断并解析,spring的包扫描也在里面完成

 (2)  AutowiredAnnotationBeanPostProcessor 自动导入注解Bean后处置理器

主要是@Autowired,是一个bean的后置处理器,在bean的属性注入的时候会用到

(3)  CommonAnnotationBeanPostProcessor 通用注释Bean后置处理器

处理一些公共注解的,它是一个bean的后置处理器,可以处理@PostConstruct和@PreDestroy还有@Resource等

(4) PersistenceAnnotationBeanPostProcessor 持久性注释Bean后置处理器

对jpa的处理,所以需要引入spring-orm的包,没有引入的话则spring不会注册这个类

(5) internalEventListenerProcessor 内部事件监听器处理器

对 @EventListener 注解的处理,spring实现事件监听的方式有很多种,其中一种就是在方法上添加 @EventListener 注解

(6)  internalEventListenerFactory 内部事件监听器工厂

跟事件监听有关

接着我们看其中一个方法,挑第一个来跟一下逻辑代码

截图:

 接着看 registerPostProcessor 这个方法

截图:

 代码:

private static BeanDefinitionHolder registerPostProcessor(
			BeanDefinitionRegistry registry, RootBeanDefinition definition, String beanName) {
		definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
        //registry 就是 AnnotationApplicationContext
        //这里是调用父类 GenericApplicationContext 中的 registerBeanDefinition 方法
        //调用 beanFactory 将 spring 默认的 BeanDefinition 注册进去
		registry.registerBeanDefinition(beanName, definition);
        //一开始就初始化了 DefaultListableBeanFactory
		return new BeanDefinitionHolder(definition, beanName);
	}

接着看  registerBeanDefinition 方法

截图:

代码:

/**
	 * Register a new bean definition with this registry.//在此注册表中注册一个新的bean定义,
	 * Must support RootBeanDefinition and ChildBeanDefinition.//必须支持 RootBeanDefinition 和 ChildBeanDefinition
	 * @param beanName the name of the bean instance to register //beanName 要注册的bean实例的名称
	 * @param beanDefinition definition of the bean instance to register //beanDefinition  要注册的 bean 实例的定义
	 * @throws BeanDefinitionStoreException if the BeanDefinition is invalid
	 * @throws BeanDefinitionOverrideException if there is already a BeanDefinition
	 * for the specified bean name and we are not allowed to override it //指定bean的名称,我们不允许覆盖它
	 * @see GenericBeanDefinition
	 * @see RootBeanDefinition
	 * @see ChildBeanDefinition
	 */
	void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
			throws BeanDefinitionStoreException;

这是一个接口,继续看其具体实现类 

这里看默认的工厂实现类 DefaultListableBeanFactory

截图:

代码:

//---------------------------------------------------------------------
	// Implementation of BeanDefinitionRegistry interface
	//---------------------------------------------------------------------

	@Override
	public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
			throws BeanDefinitionStoreException {

		Assert.hasText(beanName, "Bean name must not be empty");
		Assert.notNull(beanDefinition, "BeanDefinition must not be null");

		if (beanDefinition instanceof AbstractBeanDefinition) {
			try {
				((AbstractBeanDefinition) beanDefinition).validate();
			}
			catch (BeanDefinitionValidationException ex) {
				throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
						"Validation of bean definition failed", ex);
			}
		}
        //在注册 bd 的时候判断该名字有没有被注册
		BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
        //该名字已经被注册
        //spring 默认支持覆盖 bd,但是 spring 会输入一些日志
        //1、两个 bd 相同的情况下
        //2、两个 bd 不同的情况 role 不同
        //3、两个 bd 不相同但是 role 相同
		if (existingDefinition != null) {
			if (!isAllowBeanDefinitionOverriding()) {
				throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
			}
            //优先级
			else if (existingDefinition.getRole() < beanDefinition.getRole()) {
				// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
				if (logger.isInfoEnabled()) {
					logger.info("Overriding user-defined bean definition for bean '" + beanName +
							"' with a framework-generated bean definition: replacing [" +
							existingDefinition + "] with [" + beanDefinition + "]");
				}
			}
			else if (!beanDefinition.equals(existingDefinition)) {
				if (logger.isDebugEnabled()) {
					logger.debug("Overriding bean definition for bean '" + beanName +
							"' with a different definition: replacing [" + existingDefinition +
							"] with [" + beanDefinition + "]");
				}
			}
			else {
				if (logger.isTraceEnabled()) {
					logger.trace("Overriding bean definition for bean '" + beanName +
							"' with an equivalent definition: replacing [" + existingDefinition +
							"] with [" + beanDefinition + "]");
				}
			}
			this.beanDefinitionMap.put(beanName, beanDefinition);
		}
		else {
            //判断我们的 spring 容器是否开启实例化 bean了
            //如果为null,set 为空,没有开始就创建 bean 不会进入 if
			if (hasBeanCreationStarted()) {
				// Cannot modify startup-time collection elements anymore (for stable iteration)
				synchronized (this.beanDefinitionMap) {
                    //一个 map 一个 list ,list里面方法的 map 的 key,也就是 beanName
					this.beanDefinitionMap.put(beanName, beanDefinition);
					List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
					updatedDefinitions.addAll(this.beanDefinitionNames);
					updatedDefinitions.add(beanName);
					this.beanDefinitionNames = updatedDefinitions;
                    //如果注册了 beanDefinition 的名字和手工注册的 bd 集合当中某个相同则删除手动注册的 beanName
					removeManualSingletonName(beanName);
				}
			}
			else {
				// Still in startup registration phase
				this.beanDefinitionMap.put(beanName, beanDefinition);
				this.beanDefinitionNames.add(beanName);
				removeManualSingletonName(beanName);
			}
			this.frozenBeanDefinitionNames = null;
		}
        //判断注册的 bd 以及 beanName 是否存在
		if (existingDefinition != null || containsSingleton(beanName)) {
            //清除 allBeanNameByType
            //把单例池当中的 bean 也 remove
			resetBeanDefinition(beanName);
		}
	}

 this.beanDefinitionMap.put(beanName, beanDefinition);

 从这里可以看到bean已经存在到 IOC容器中,到此 AnnotatedBeanDefinitionReader 分析结束

接着看 ClassPathBeanDefinitionScanner 的构造方法

2.2  ClassPathBeanDefinitionScanner 构造方法

这个方法主要是创建BeanDefinition扫描器,可以用来扫描包或者类,继而转换为bd,spring默认的扫描器其实不是这个scanner对象, 而是在后面自己又重新new了一个ClassPathBeanDefinitionScanner, spring在执行工程后置处理器ConfigurationClassPostProcessor时,去扫描包时会new一个ClassPathBeanDefinitionScanner
这里的scanner仅仅是为了程序员可以手动调用AnnotationConfigApplicationContext对象的scan方法


所以这里不展开看了。

 

 

 3、最后回到我们最开始的地方, 可以看到 spring 初始化上下文已经有了,打个断点可以看到IOC容器已经实例化了一些对象,就是上面的6中默认bean,

截图:

参考博客:

1、https://blog.csdn.net/zzzzzyyyz/article/details/116999121

2、spring加载流程之AnnotatedBeanDefinitionReader_浴缸灬的博客-CSDN博客

3、SpringBoot源码深度剖析——@SpringBootApplication注解和new SpringApplication().run()方法深度解密_生活,没那么矫情的博客-CSDN博客

 

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/628003.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2023最新版Java 面试突击手册开源(涵盖 p5-p8 技术栈)

前言: 本文收集整理了各大厂常见面试题N道&#xff0c;你想要的这里都有内容涵盖&#xff1a;Java、MyBatis、ZooKeeper、Dubbo、Elasticsearch、Memcached、Redis、MySQL、Spring、Spring Boot、Spring Cloud、RabbitMQ、Kafka、Linux 等技术栈&#xff0c;希望大家都能找到适…

【免配置】Qt的mingw使用编译opencv库

【免配置】Qt的mingw_32/64使用编译opencv库 网上在qt中使用mingw编译器配置opencv的时候&#xff0c;通常需要使用cmake编译工具&#xff0c;进行预先编译&#xff0c;步骤比较繁琐&#xff0c;这里推荐一个捷径&#xff0c;直接使用前人编译好的opencv库即可&#xff0c;避免…

软件测试金融项目,在测试的时候一定要避开的一些雷区

软件测试金融项目需要格外谨慎和专注&#xff0c;因为这些项目通常涉及大量的交易、用户隐私和其他敏感信息。以下是一些软件测试金融项目时需要关注的方面&#xff1a; 1. 数据保护 在测试金融项目时&#xff0c;必须确保用户数据和投资信息得到保护。测试人员必须确保测试环…

Flink CDC 实时mysql到mysql

CDC 的全称是 Change Data Capture &#xff0c;在广义的概念上&#xff0c;只要是能捕获数据变更的技术&#xff0c;我们都可以称之为 CDC 。目前通常描述的 CDC 技术主要面向数据库的变更&#xff0c;是一种用于捕获数据库中数据变更的技术。 mysqlcdc需要mysql开启binlog&a…

TCP报文段结构

TCP报文段结构 源端口号和目的端口号&#xff1a;含义从名字就能看出来。 序号和确认号&#xff1a;这二个字段被 TCP 发送方和接收方用来实现可靠数据传输服务&#xff0c;每个字段都是32比特。 接收窗口&#xff1a;该字段用于流量控制&#xff0c;大小为16比特。 首部长度…

VTK学习之vtkProp

vtkProp。渲染场景中数据的可视表达&#xff08;Visible Depictions&#xff09;是由vtkProp的子类负责。 也就是说&#xff0c;数据想要进行可视化显示&#xff0c;需要一个转换过程&#xff0c;这个过程就是转换为vtkProp 这样才能进行渲染展示出来。 而vtkProp子类是vtkA…

一阶电路和二阶电路的时域分析(1)——“电路分析”

小雅兰期末加油冲冲冲&#xff01;&#xff01;&#xff01; 动态电路的方程及其初始条件 动态电路&#xff0c;物理学名词&#xff0c;是指含有储能元件L、C的电路&#xff0c;动态电路方程的阶数通常等于电路中动态元件的个数。 动态电路是指含有储能元件的电路。当动态电路状…

openpnp - 底部相机矫正(subject not found)的原因总结

文章目录 openpnp - 底部相机矫正(subject not found)的原因总结概述问题的由来相机的选择相机焦距的选择相机初始安装距离位置的选择相机安装支柱接触面过大会影响相机模组PCB的安装相机支柱的绝缘问题安装相机模组时的平整度问题相机轴垂直度的问题相机成像时间矫正时的Z轴位…

全球顶尖科学家陈松蹊院士出任百分点数据科学研究院名誉院长

近日&#xff0c;百分点科技正式宣布聘请北京大学数学科学学院、光华管理学院教授&#xff0c;中国科学院院士陈松蹊担任百分点数据科学研究院名誉院长。公司将以此深化布局数据科学领域&#xff0c;助推数字中国建设。 全球顶尖科学家 陈松蹊院士主要从事超高维大数据统计分析…

卷S人的Java岗!全靠这份1000页的面试手册,拿了28K的offer

大家好&#xff0c;最近有不少朋友给鄙人留言&#xff0c;说今年面试实在是太卷了&#xff0c;不知道从何下手&#xff01; 不论是跳槽涨薪&#xff0c;还是学习提升&#xff01;先给自己定一个小目标&#xff0c;然后再朝着目标去努力就完事儿了&#xff01;为了帮大家节约时…

JVM内存结构及程序执行的内存分析过程

一. JVM内存结构 1. JVM的内存结构大概分为 堆&#xff08;Heap&#xff09; 线程共享。所有的对象实例以及数组都要在堆上分配。回收器主要管理的对象。 方法区&#xff08;Method Area&#xff09; 线程共享。存储类信息、常量、静态变量、即时编译器编译后的代码。 方…

机器学习-12 卷积神经网络简介

卷积神经网络 引言深度学习发展历程深度应用领域深度学习vs传统机器学习深度神经网络vs浅层神经网络深度学习概述 卷积神经网络CNNBP神经网络CNN概述卷积神经网络大致结构卷积神经网络大致过程 局部连接权值共享非线性映射ReLU&#xff08;Rectified Linear Units&#xff09;池…

盘点一下架构师主流的画图工具(附地址)

盘点一下架构师主流的画图工具&#xff08;附地址&#xff09; 转发我个人微信公众号的内容&#xff0c;后续优先公众号。 一、文章来源 写这篇文章的目的是做个关于常用画图工具的总结。 起源是在架构组时为了降低沟通成本和提高作战效率&#xff0c;我们频繁用图交流&…

13. 精灵动画Sprite和SpriteSequence的基本使用

1. 说明&#xff1a; 在unity二维游戏开发中&#xff0c;有一种精灵类的玩家角色&#xff0c;通过一系列动作的静态图片可以合成该精灵的某一个动作。在QML当中也有一个控件可以实现这种精灵类动画的制作&#xff0c;主要使用到三个控件&#xff1a;Sprite和SpriteSequence和A…

一文解释python中的实例方法,类方法和静态方法作用和区别是啥?该如何使用

我们都知道 &#xff0c;python类中有三种常见的方法 &#xff0c;分别是实例方法 &#xff0c;类方法和静态方法 。那么这几个方法到底有什么作用 &#xff1f; 它们之间有什么区别 &#xff1f;该如何使用 &#xff1f; 带着这些问题 &#xff0c;下面我们就来了解下这三种方…

windows平台python脚本执行环境搭建笔记

1.python脚本环境下载 这里是原始发布源&#xff1a; https://www.python.org/downloads/release/python-3114/https://www.python.org/downloads/release/python-3114/安装时记得添加进系统path&#xff0c;这样你可以随时调用python环境。 2.扩展模块的安装 step1.找到py…

35款优秀的 SpringBoot/SpringCloud 开源项目,开发脚手架,总有一款适合你...

简介 SpringBoot 是一个非常流行的 Java 框架&#xff0c;它可以帮助开发者快速构建应用程序。他不仅继承了 Spring 框架原有的优秀特性&#xff0c;而且还通过简化配置来进一步简化了 Spring 应用的整个搭建和开发过程。 最近&#xff0c;小编蹲点各大开源网站、社区等&#x…

iOS App的打包和上架流程

转载&#xff1a;iOS App的打包和上架流程 - 掘金 1. 创建账号 苹果开发者账号几种开发者账号类型 个人开发者账号 费用&#xff1a;99 美元/年&#xff08;688.00元&#xff09;协作人数&#xff1a;仅限开发者自己不需要填写公司的邓百氏编码&#xff08; D-U-N-S Number…

源码分析spring容器启动销毁资源

文章目录 一、InitializingBean二、SmartInitializingSingleton三、PostConstruct四、DisposableBean五、PreDestroy六、BeanPostProcessor七、ApplicationContextAware八、Bean初始化销毁过程 spring项目启动时&#xff0c;在bean的生命周期内&#xff0c;可以添加一些前置、后…

ATE测试工程师的前景怎么样?能转DFT工程师吗?

最近后台不少同学私信想要咨询ATE这个岗位&#xff0c;想了解这个岗位的薪资&#xff0c;前景&#xff0c;以及相关的技能&#xff0c;下面就来一起了解一下~ 什么是ATE&#xff1f; ATE是&#xff08;Automatic Test Equipment&#xff09;的缩写&#xff0c; 于半导体产业意…