spring的启动过程(一) :IOC容器的启动过程
一、web容器的加载
首先我们要先知道一个web项目的启动过程。
-
将Web项目部署到Tomcat中的方法之一,是部署没有封装到WAR文件中的Web项目。要使用这一方法部署未打包的webapp目录,只要把我们的项目(编译好的发布项目,非开发项目)放到Tomcat的webapps目录下就可以了。
-
一个常规的Spring应用,在web容器启动时,默认会先去加载/WEB-INF/web.xml,它配置了:servletContext上下文、监听器(Listener)、过滤器(Filter)、Servlet等。
二、spring的启动
spring的启动过程其实就是ioc的启动过程。
spring的ioc支持了controller层注入service,service注入dao。打通了各层之间的桥梁,省去了原来的new service(),new Dao()的方法。
1、如上面所言,spring启动优先加载了web.xml,我们看看里面配置的内容。
web.xml 加载顺序为: context-param < listener < filter < servlet
<!--该元素用来声明应用范围(整个WEB项目)内的上下文初始化参数。 --><context-param><param-name>contextConfigLocation</param-name><param-value>classpath*:applicationContext*.xml</param-value></context-param><!--监听器--><listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class></listener><!--servlet,mvc的前端控制器 --><servlet><servlet-name>mvc</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><load-on-startup>1</load-on-startup></servlet><!--截获请求,匹配的请求都交由上面配置的mvc这个servlet处理 --><servlet-mapping><servlet-name>mvc</servlet-name><url-pattern>/</url-pattern></servlet-mapping><!--过滤器 --><filter><filter-name>LoginValidateFilter</filter-name><filter-class>com.tianque.clue.web.LoginValidateFilter</filter-class></filter><!--截获请求,匹配的请求都经过上面配置的LoginValidateFilter过滤处理 --><filter-mapping><filter-name>LoginValidateFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping>
在启动Web项目时,容器会读web.xml配置文件中的两个节点context-param和listener。
知晓web.xml配置内容后,我们详细看一下。
2.ContextLoaderListener
在启动Web项目时,容器(比如Tomcat)会读web.xml配置文件中的两个节点
listener和context-param
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {public ContextLoaderListener() {}public ContextLoaderListener(WebApplicationContext context) {super(context);}//该方法contextInitialized是重点public void contextInitialized(ServletContextEvent event) {this.initWebApplicationContext(event.getServletContext());}public void contextDestroyed(ServletContextEvent event) {this.closeWebApplicationContext(event.getServletContext());ContextCleanupListener.cleanupAttributes(event.getServletContext());}
}
接着进入this.initWebApplicationContext方法,进入ContextLoader类中
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {throw new IllegalStateException("Cannot initialize context because there is already a root application context present - check whether you have multiple ContextLoader* definitions in your web.xml!");} else {//日志略.......long startTime = System.currentTimeMillis();try {if (this.context == null) {//创建 WebApplicationContextthis.context = this.createWebApplicationContext(servletContext);}if (this.context instanceof ConfigurableWebApplicationContext) {ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext)this.context;if (!cwac.isActive()) {if (cwac.getParent() == null) {ApplicationContext parent = this.loadParentContext(servletContext);cwac.setParent(parent);}//该方法是重点,赋值初始化this.configureAndRefreshWebApplicationContext(cwac, servletContext);}}//略........return this.context;} catch (Error | RuntimeException var8) {logger.error("Context initialization failed", var8);servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, var8);throw var8;}}}
点进该方法细看源码
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {String configLocationParam;if (ObjectUtils.identityToString(wac).equals(wac.getId())) {configLocationParam = sc.getInitParameter("contextId");if (configLocationParam != null) {wac.setId(configLocationParam);} else {wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(sc.getContextPath()));}}wac.setServletContext(sc);//眼熟吧 contextConfigLocation就是web.xml里<context-param>configLocationParam = sc.getInitParameter("contextConfigLocation");if (configLocationParam != null) {wac.setConfigLocation(configLocationParam);}ConfigurableEnvironment env = wac.getEnvironment();if (env instanceof ConfigurableWebEnvironment) {((ConfigurableWebEnvironment)env).initPropertySources(sc, (ServletConfig)null);}this.customizeContext(sc, wac);//重点方法,接来下会细看wac.refresh();}
点进去 wac.refresh();最后到了AbstractApplicationContext类中的refresh()
public void refresh() throws BeansException, IllegalStateException {Object var1 = this.startupShutdownMonitor;//加锁synchronized(this.startupShutdownMonitor) {//创建和准备了 Environment 对象this.prepareRefresh();//重点,该方法执行结束之后,xml中定义的bean就已经加载到IOC容器中了//然而Bean 并没有完成初始化ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();//完善 BeanFactory,设置 BeanFactory 的类加载器,添加 BeanPostProcessor,//手动注册几个特殊的 bean。this.prepareBeanFactory(beanFactory);try {//空方法this.postProcessBeanFactory(beanFactory);//执行BeanFactory后置处理器,可以用来补充或修改 BeanDefinitionthis.invokeBeanFactoryPostProcessors(beanFactory);//继续从 beanFactory 中找出 bean 后处理器,添加至 beanPostProcessors 集合中this.registerBeanPostProcessors(beanFactory);//国际化this.initMessageSource();//this.initApplicationEventMulticaster();//空实现this.onRefresh();//注册监听器this.registerListeners();//这一步会将 beanFactory 的成员补充完毕,并初始化所有非延迟单例 bean//Spring怎么解决循环依赖问题就在该方法里面的getBean再里面的// doCreateBean()方法//再往里的AbstractAutowireCapableBeanFactory类的doCreateBean会判断//是否有aop加强,在里面获得bean加强后的代理对象(aop源码)//Spring为了解决单例的循环依赖问题,使用了三级缓存,当个问题写在文章最后this.finishBeanFactoryInitialization(beanFactory);//这一步会为 ApplicationContext 添加 lifecycleProcessor 成员,//用来控制容器内需要生命周期管理的 beanthis.finishRefresh();} catch (BeansException var9) {if (this.logger.isWarnEnabled()) {this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);}this.destroyBeans();this.cancelRefresh(var9);throw var9;} finally {this.resetCommonCaches();}}}
点进去 this.obtainFreshBeanFactory();
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {//创建beanFactorythis.refreshBeanFactory();return this.getBeanFactory();}
进入 AbstractRefreshableApplicationContext类的refreshBeanFactory();
protected final void refreshBeanFactory() throws BeansException {if (this.hasBeanFactory()) {this.destroyBeans();this.closeBeanFactory();}try {DefaultListableBeanFactory beanFactory = this.createBeanFactory();beanFactory.setSerializationId(this.getId());this.customizeBeanFactory(beanFactory);//重点 加载bean定义this.loadBeanDefinitions(beanFactory);Object var2 = this.beanFactoryMonitor;synchronized(this.beanFactoryMonitor) {this.beanFactory = beanFactory;}} catch (IOException var5) {throw new ApplicationContextException("I/O error parsing bean definition source for " + this.getDisplayName(), var5);}}
点进去到XmlWebApplicationContext类的loadBeanDefinitions方法
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {//创建XmlBeanDefinitionReader对象,用于解析xml文件中定义的bean,// 将xml文件转化为Resource流对象XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);beanDefinitionReader.setEnvironment(this.getEnvironment());beanDefinitionReader.setResourceLoader(this);beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));this.initBeanDefinitionReader(beanDefinitionReader);//重点方法this.loadBeanDefinitions(beanDefinitionReader);}
点击进去细看
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {//configLocations 获取到的就是applicationContext.xml文件String[] configLocations = this.getConfigLocations();if (configLocations != null) {String[] var3 = configLocations;int var4 = configLocations.length;//遍历每个配置文件,将配置文件中的标签解析成beanfor(int var5 = 0; var5 < var4; ++var5) {String configLocation = var3[var5];//重点方法reader.loadBeanDefinitions(configLocation);}}}
public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {return this.loadBeanDefinitions(location, (Set)null);}public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {ResourceLoader resourceLoader = this.getResourceLoader();if (resourceLoader == null) {throw new BeanDefinitionStoreException("Cannot load bean definitions from location [" + location + "]: no ResourceLoader available");} else {int count;if (resourceLoader instanceof ResourcePatternResolver) {try {//拿到resources 即配置文件转化的Resource[] resources = ((ResourcePatternResolver)resourceLoader).getResources(location);//继续进入方法count = this.loadBeanDefinitions(resources);if (actualResources != null) {Collections.addAll(actualResources, resources);}if (this.logger.isTraceEnabled()) {this.logger.trace("Loaded " + count + " bean definitions from location pattern [" + location + "]");}return count;} catch (IOException var6) {throw new BeanDefinitionStoreException("Could not resolve bean definition resource pattern [" + location + "]", var6);}} else {............}}}
拿到resource[xml文件],进入this.loadBeanDefinitions(resources)方法,一路往下走,最终又会进入XmlBeanDefinitionReader类的loadBeanDefinitions(EncodedResource encodedResource)方法
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {Assert.notNull(encodedResource, "EncodedResource must not be null");...略......if (!((Set)currentResources).add(encodedResource)) {throw new BeanDefinitionStoreException("Detected cyclic loading of " + encodedResource + " - check your import definitions!");} else {int var5;try {InputStream inputStream = encodedResource.getResource().getInputStream();try {InputSource inputSource = new InputSource(inputStream);if (encodedResource.getEncoding() != null) {inputSource.setEncoding(encodedResource.getEncoding());}//重点方法,进入细看var5 = this.doLoadBeanDefinitions(inputSource, encodedResource.getResource());} finally {inputStream.close();}} catch (IOException var15) {.........} finally {..........}return var5;}}
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {try {//xml配置文件解析成document对象Document doc = this.doLoadDocument(inputSource, resource);//注册bean,重点int count = this.registerBeanDefinitions(doc, resource);if (this.logger.isDebugEnabled()) {this.logger.debug("Loaded " + count + " bean definitions from " + resource);}return count;} catch (BeanDefinitionStoreException var5) {throw var5;} //各种try catch异常代码略}
doLoadBeanDefinitions()方法中主要包含2个步骤,第一步根据inputSource和resource获取到一个Document对象,我们知道xml文档可以解析成一个document树,其中最外层标签就是root元素,子标签就是一个个的叶子node,具体的解析成Document对象的过程不用过于纠结,Spring提供了详细实现, 第二步就是讲document对象注册到Spring容器里,而resouce参数用来选择XmlReaderContext
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {BeanDefinitionDocumentReader documentReader = this.createBeanDefinitionDocumentReader();int countBefore = this.getRegistry().getBeanDefinitionCount();//重点,点进去细看documentReader.registerBeanDefinitions(doc, this.createReaderContext(resource));return this.getRegistry().getBeanDefinitionCount() - countBefore;}
一步步点击
protected void doRegisterBeanDefinitions(Element root) {BeanDefinitionParserDelegate parent = this.delegate;this.delegate = this.createDelegate(this.getReaderContext(), root, parent);if (this.delegate.isDefaultNamespace(root)) {String profileSpec = root.getAttribute("profile");if (StringUtils.hasText(profileSpec)) {String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, ",; ");if (!this.getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {if (this.logger.isDebugEnabled()) {this.logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching: " + this.getReaderContext().getResource());}return;}}}this.preProcessXml(root);//重点方法,点进去细看this.parseBeanDefinitions(root, this.delegate);this.postProcessXml(root);this.delegate = parent;}
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)) {//重点方法,解析标签元素this.parseDefaultElement(ele, delegate);} else {delegate.parseCustomElement(ele);}}}} else {delegate.parseCustomElement(root);}}
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {if (delegate.nodeNameEquals(ele, "import")) {this.importBeanDefinitionResource(ele);} else if (delegate.nodeNameEquals(ele, "alias")) {this.processAliasRegistration(ele);} else if (delegate.nodeNameEquals(ele, "bean")) {//重点看bean标签this.processBeanDefinition(ele, delegate);} else if (delegate.nodeNameEquals(ele, "beans")) {this.doRegisterBeanDefinitions(ele);}}
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {//不细看了,把bean标签的id,name解析返回BeanDefinitionHolderBeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);if (bdHolder != null) {//有需要就装饰bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);try {//重点,点进去细看BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, this.getReaderContext().getRegistry());} catch (BeanDefinitionStoreException var5) {this.getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, var5);}this.getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));}}
public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException {String beanName = definitionHolder.getBeanName();//重点,点进去细看registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());String[] aliases = definitionHolder.getAliases();if (aliases != null) {String[] var4 = aliases;int var5 = aliases.length;for(int var6 = 0; var6 < var5; ++var6) {String alias = var4[var6];registry.registerAlias(beanName, alias);}}}
最终就是往Map<String, BeanDefinition> beanDefinitionMap中放beanDefinition
this.beanDefinitionMap.put(beanName, beanDefinition);
至此this.obtainFreshBeanFactory()方法方法解析完毕。
该方法总结:
-
这一步获取(或创建) BeanFactory,它也是作为 ApplicationContext 的一个成员变量
-
BeanFactory 的作用是负责 bean 的创建、依赖注入和初始化,bean 的各项特征由 BeanDefinition 定义
-
BeanDefinition 作为 bean 的设计蓝图,规定了 bean 的特征,如单例多例、依赖关系、初始销毁方法等
-
BeanDefinition 的来源有多种多样,可以是通过 xml 获得、配置类获得、组件扫描获得,也可以是编程添加
-
所有的 BeanDefinition 会存入 BeanFactory 中的 beanDefinitionMap 集合
核心方法讲解完了,其他方法感兴趣可自行了解,不多赘述了。最后写个小问题。
spring管理的bean在默认情况下是会在服务器启动的时候初始化的。
bean设置了scope为prototype(原型)之后,会每次使用时生产一个
bean设置了lazy-init=”true”后,启动服务器不会马上实例化,而是在用到的时候被实例化。
问题:Spring怎么解决循环依赖问题?
循环依赖其实就是循环引用,也就是两个或则两个以上的bean互相持有对方,最终形成闭环。比如A依赖于B,B依赖于C,C又依赖于A。
doCreateBean 方法有三个核心流程。实例化,填充属性,初始化。
循环依赖主要发生在第一、第二步。也就是构造器循环依赖和field循环依赖。
那么我们要解决循环引用也应该从初始化过程着手,对于单例来说,在Spring容器整个生命周期内,有且只有一个对象,所以很容易想到这个对象应该存在Cache中,Spring为了解决单例的循环依赖问题,使用了三级缓存。
我们看一下doGetBean方法中的getSingleton方法。
Object sharedInstance = this.getSingleton(beanName);
protected Object getSingleton(String beanName, boolean allowEarlyReference) {Object singletonObject = this.singletonObjects.get(beanName);//判断当前单例bean是否正在创建中,也就是没有初始化完成//比如A的构造器依赖了B对象所以得先去创建B对象// 或则在A的populateBean过程中依赖了B对象,得先去创建B对象,//这时的A就是处于创建中的状态。if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {Map var4 = this.singletonObjects;synchronized(this.singletonObjects) {singletonObject = this.earlySingletonObjects.get(beanName);// 是否允许从singletonFactories中通过getObject拿到对象if (singletonObject == null && allowEarlyReference) {ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);if (singletonFactory != null) {singletonObject = singletonFactory.getObject();this.earlySingletonObjects.put(beanName, singletonObject);this.singletonFactories.remove(beanName);}}}}return singletonObject;}
该方法还依赖于三个map,这三个map就是三级缓存。
分析getSingleton()的整个过程,Spring首先从一级缓存singletonObjects中获取。如果获取不到,并且对象正在创建中,就再从二级缓存earlySingletonObjects中获取。
如果还是获取不到且允许singletonFactories通过getObject()获取,就从三级缓存singletonFactory.getObject()(三级缓存)获取,如果获取到了则从singletonFactories中移除,并放入earlySingletonObjects中。
singletonObject = singletonFactory.getObject();this.earlySingletonObjects.put(beanName, singletonObject);this.singletonFactories.remove(beanName);
其实也就是从三级缓存移动到了二级缓存。
从上面三级缓存的分析,我们可以知道,Spring解决循环依赖的诀窍就在于singletonFactories这个三级cache里就是解决循环依赖的关键,这段代码发生在createBeanInstance之后,也就是说单例对象此时已经被创建出来(调用了构造器)。这个对象已经被生产出来了,虽然还不完美(还没有进行初始化的第二步和第三步),但是已经能被人认出来了(根据对象引用能定位到堆中的对象),所以Spring此时将这个对象提前曝光出来让大家认识,让大家使用。
相关文章:

spring的启动过程(一) :IOC容器的启动过程
一、web容器的加载 首先我们要先知道一个web项目的启动过程。 将Web项目部署到Tomcat中的方法之一,是部署没有封装到WAR文件中的Web项目。要使用这一方法部署未打包的webapp目录,只要把我们的项目(编译好的发布项目,非开发项目&am…...

这次,我的CentOS又ping不通www.baidu.com了(gateway配置)
当我们保证了宿主机与虚拟机的ip地址在同一网段,并且我们使用虚拟机ping宿主机,与宿主机ping虚拟机都可以互相ping通的情况下虚拟机却ping不通外网了,由于涉及到了跨越网络访问,所以我们应该把问题聚焦在网关的配置上!…...

启智社区“我为开源狂”第六期活动小白教程之基础活跃榜
一、写在前面 春天来啦~启智社区第六期活动也来啦! 有奖金的哦~~ 基础活跃榜奖金根据用户活跃程度进行100-300元的激励。 挑战升级榜需要用户完成相应任务,达标者可获得300-1000元的激励。 邀请助力榜根据用户邀请情况进行积分累加,按实际达…...

华为OD机试 - 区块链文件转储系统(Python)【2023-Q1 新题】
华为OD机试300题大纲 参加华为od机试,一定要注意不要完全背诵代码,需要理解之后模仿写出,通过率才会高。 华为 OD 清单查看地址:blog.csdn.net/hihell/category_12199275.html 华为OD详细说明:https://dream.blog.csdn.net/article/details/128980730 区块链文件转储系…...

【字节面试】Fail-fast知识点相关知识点
字节面试,问到的一个小知识点,这里做一下总结,其实小编之前有一篇文章,已经对此有过涉及,不过这里知识专项针对于问题,把这个知识点拎出来说一下。 1.问题 什么是Fail-fast机制? Hashmap是否拥…...

git应用笔记(三)
在新增虚拟机linux的基础上,做git的下载与提交 1、初始化自己的用户名和邮箱。 git config --global user.name “输入你的用户名” git config --global user.email “输入你的邮箱” 2、将本地公钥及配置如图1复制粘贴到虚拟机当前用户.ssh\目录下 4929a0205f43…...

有序表的应用:设计一个增、删、查数据的时间复杂度均为O(logN)的结构
1、题目描述 设计一个结构包含如下三个方法: void add(int index, int num); //把num加入到index位置 int get(int index); //取出index位置的值(是自然序的index位置,非排序后) void remove(int index); //把index位置上的值删…...

离线环境拷贝迁移 conda envs 环境(蛮力方法,3行命令)
前言 最近要使用 GPU 服务器做实验,可惜的是,有网络连接的服务器显卡旧,算力不够;显卡较新的机器没有联网。于是有需求将旧机器上配置好的 conda 环境迁移至新机器。网上给的默认方法生成 yaml 文件迁移等 需要联网,只…...

【数据结构与算法】字符串1:反转字符串I 反转字符串II 反转字符串里的单词 剑指offer(替换空格、左旋转字符串)
今日任务 344.反转字符串541.反转字符串II剑指Offer 05.替换空格151.反转字符串里的单词剑指Offer58-II.左旋转字符串 1.Leetcode344.反转字符串 来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/reverse-string &#…...

深入浅出C++ ——容器适配器
文章目录一、容器适配器二、deque类简介1. deque的原理2. deque迭代器3. deque的优点和缺陷4. 为什么选择deque作为stack和queue的底层默认容器一、容器适配器 适配器的概念 适配器是STL六大核心组件之一,它是一种设计模式,该种模式是将一个类的接口转换…...

电脑常用知识与工作常用工具
什么是电脑快捷键? 所谓快捷键就是使用键盘上某一个或某几个键的组合完成一条功能命令,从而达到提高操作速度的目的。 键盘布局 主键盘区,数字辅助键盘区、F键功能键盘区、控制键区,对于多功能键盘还增添了快捷键区 一、常用快捷…...

JS的事件循环
文章目录写在前面1.浏览器的进程模型1.1 何为进程1.2 何为线程1.3 浏览器有哪些线程和进程2.渲染主线程是如何工作的任务队列的优先级面试题如何理解JS异步JS中的计时器能做到精确计时吗?为什么?写在前面 此处的文字为自己的理解 1.浏览器的进程模型 1.…...

【阿旭机器学习实战】【31】股票价格预测案例--线性回归
【阿旭机器学习实战】系列文章主要介绍机器学习的各种算法模型及其实战案例,欢迎点赞,关注共同学习交流。 注:本文模型结果不好,仅做学习参考使用,提供思路。了解数据处理思路,训练模型和预测数值的过程。 目录1. 读取数据K线图绘…...

浅谈毫米波技术与应用
浅谈毫米波之技术篇2020年10月GSMA发布的《5G毫米波技术白皮书》预计,在2022年北京冬奥会上,5G毫米波有望大放异彩,为观众、媒体转播者、赛事组织和参与者等提供优质的观赛体验、完备的服务保障,将可提供全景VR、新型信息交互、智…...

给安全平台编写插件模块的思路分享
一、背景 最近在GitHub看到一个新的开源安全工具,可以把工具都集成到一个平台里,觉得挺有意思,但是平台现有的工具不是太全,我想把自己的工具也集成进去,所以研究了一番 蜻蜓安全工作台是一个安全工具集成平台&#x…...

4123版驱动最新支持《霍格沃茨之遗》,英特尔锐炫显卡带你畅游魔法世界
2023年开年最火的3A大作,那一定是近期上架steam平台的《霍格沃茨之遗》,这款游戏在2020年9月份曝光,游戏根据《哈利波特》系列书籍内容改编,作为一款开放式的3A大作,《霍格沃兹之遗》目前在steam上的实时在线人数已经突…...

OSI模型和网络协议简介
文章目录一、OSI七层模型1.1什么是OSI七层模型?1.2这个网络模型究竟是干什么呢?二、TCP/IP协议三、常见协议四、物联网通信协议以及MQTT4.1 物联网七大通信协议4.2 MQTT特性一、OSI七层模型 1.1什么是OSI七层模型? 我们需要了解互联网的本质…...

传感器原理及应用期末复习汇总(附某高校期末真题试卷)
文章目录一、选择题二、填空题三、简答题四、计算题五、期末真题一、选择题 1.下列哪一项是金属式应变计的主要缺点(A) A、非线性明显 B、灵敏度低 C、准确度低 D、响应时间慢 2.属于传感器动态特性指标的是(D) A、重复性 B、线…...

【亲测2022年】网络工程师被问最多的面试笔试题
嗨罗~大家好久不见,主要是薄荷呢主业还是比较繁忙的啦,之前发了一个面试题大家都很喜欢,非常感谢各位大佬对薄荷的喜爱,嘻嘻然后呢~薄荷调研了身边的朋友和同事,发现我们之前去面试,写的面试题有很多共同的…...

Web前端:全栈开发人员的责任
多年来,关于全栈开发人员有很多说法,全栈开发人员是一位精通应用程序全栈开发过程的专业人士。这包括数据库、API、前端技术、后端开发语言和控制系统版本。你一定遇到过前端和后端开发人员。前端开发人员将构建接口,而后端开发人员将开发、更…...

C语言之通讯录的实现
通讯录实现所需头文件和源文件 Contact.h的功能 声明函数和创建结构体变量 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <string.h> #include <stdlib.h> #include <assert.h> #define MAX 1000 #define MAX_NAME 20 #define MAX…...

手把手教大家在 gRPC 中使用 JWT 完成身份校验
文章目录1. JWT 介绍1.1 无状态登录1.1.1 什么是有状态1.1.2 什么是无状态1.2 如何实现无状态1.3 JWT1.3.1 简介1.3.2 JWT数据格式1.3.3 JWT 交互流程1.3.4 JWT 存在的问题2. 实践2.1 项目创建2.2 grpc_api2.3 grpc_server2.4 grpc_client3. 小结上篇文章松哥和小伙伴们聊了在 …...

VSCode远程连接服务器
工作使用服务器的jupyter,直到有一天服务器挂了,然而,代码还没有来得及备份。o(╥﹏╥)o VScode远程连接服务器,使用服务器的资源,代码可以存在本地,可以解决上述困境。 1.官网下载VSCode.网址https://cod…...

【C++】-- 异常
目录 C语言传统的处理错误的方式 C异常概念 异常的使用 异常的抛出和捕获 自定义异常体系 异常的重新抛出 异常安全 异常规范(C期望) C标准库的异常体系 异常的优缺点 C异常的优点 C异常的缺点 总结 C语言传统的处理错误的方式 传统的错误…...

Java中的Stack与Queue
文章目录一、栈的概念及使用1.1 概念1.2 栈的使用1.3 栈的模拟实现二、队列的概念及使用2.1 概念2.2 队列的使用2.3 双端队列(Deque)三、相关OJ题3.1 用队列实现栈。3.2 用栈实现队列。总结一、栈的概念及使用 1.1 概念 栈:一种特殊的线性表,其只允许在…...

xilinx FPGA在线调试方法总结(vivado+ila+vio)
本文主要介绍xilinx FPGA开发过程中常用的调试方法,包括ILA、VIO和TCL命令等等,详细介绍了如何使用。一、FPGA调试基本原则根据实际的输出结果表现,来推测可能的原因,再在模块中加ILA信号,设置抓信号条件,逐…...

自动化测试——css元素定位
文章目录一、css定位场景二、css相对定位的优点三、css的调试方法1、表达式中含有字符串:表达式中的引号一定和外面字符串的引号相反四、css基础语法1、标签定位2、class定位特别注意:当class类型的属性值包含多个分割值,$(.s_tab s_tab_1z9n…...

ChatGPT可能马上取代你,这是它能做的十个工作
ChatGPT 的横空出世,在业界掀起了惊涛骇浪。专家表示,ChatGPT 和相关人工智能技术可能会威胁到一些工作岗位,尤其是白领工作。 自去年11月发布以来,新型聊天机器人模型 ChatGPT 已经被用于各种各样的工作:撰写求职信、编写儿童读物,甚至帮助学生在论文中作弊。谷歌公司发…...

ubuntu转储coredump
方法一: 输入以下命令即可,其中${USER}为自己电脑的用户名: ulimit -c unlimited echo "/home/${USER}/core.%p" > /proc/sys/kernel/core_pattern 方法二: Disable apport : sudo systemctl stop apport.servicesudo system…...

基于单片机的毕业设计推荐
** 2023基于单片机的毕业设计推荐: ** 1、基于51单片机的多功能门禁系统(低端、功能限制较大)。 2、基于单片机的多功能实时时钟。 3、基于单片机的音乐播放器。 4、基于STM32单片机的多功能门禁系统(高端、没有限制)…...