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、前端技术、后端开发语言和控制系统版本。你一定遇到过前端和后端开发人员。前端开发人员将构建接口,而后端开发人员将开发、更…...

SpringBoot-17-MyBatis动态SQL标签之常用标签
文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...

Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例
使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件,常用于在两个集合之间进行数据转移,如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model:绑定右侧列表的值&…...

《通信之道——从微积分到 5G》读书总结
第1章 绪 论 1.1 这是一本什么样的书 通信技术,说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号(调制) 把信息从信号中抽取出来&am…...
【论文笔记】若干矿井粉尘检测算法概述
总的来说,传统机器学习、传统机器学习与深度学习的结合、LSTM等算法所需要的数据集来源于矿井传感器测量的粉尘浓度,通过建立回归模型来预测未来矿井的粉尘浓度。传统机器学习算法性能易受数据中极端值的影响。YOLO等计算机视觉算法所需要的数据集来源于…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个生活电费的缴纳和查询小程序
一、项目初始化与配置 1. 创建项目 ohpm init harmony/utility-payment-app 2. 配置权限 // module.json5 {"requestPermissions": [{"name": "ohos.permission.INTERNET"},{"name": "ohos.permission.GET_NETWORK_INFO"…...
【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)
升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点,但无自动故障转移能力,Master宕机后需人工切换,期间消息可能无法读取。Slave仅存储数据,无法主动升级为Master响应请求ÿ…...
实现弹窗随键盘上移居中
实现弹窗随键盘上移的核心思路 在Android中,可以通过监听键盘的显示和隐藏事件,动态调整弹窗的位置。关键点在于获取键盘高度,并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...

pikachu靶场通关笔记22-1 SQL注入05-1-insert注入(报错法)
目录 一、SQL注入 二、insert注入 三、报错型注入 四、updatexml函数 五、源码审计 六、insert渗透实战 1、渗透准备 2、获取数据库名database 3、获取表名table 4、获取列名column 5、获取字段 本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关࿰…...

Linux --进程控制
本文从以下五个方面来初步认识进程控制: 目录 进程创建 进程终止 进程等待 进程替换 模拟实现一个微型shell 进程创建 在Linux系统中我们可以在一个进程使用系统调用fork()来创建子进程,创建出来的进程就是子进程,原来的进程为父进程。…...
基于Java Swing的电子通讯录设计与实现:附系统托盘功能代码详解
JAVASQL电子通讯录带系统托盘 一、系统概述 本电子通讯录系统采用Java Swing开发桌面应用,结合SQLite数据库实现联系人管理功能,并集成系统托盘功能提升用户体验。系统支持联系人的增删改查、分组管理、搜索过滤等功能,同时可以最小化到系统…...