编码踩坑——运行时报错java.lang.NoSuchMethodError / 同名类加载问题 / 双亲委派【建议收藏】
本篇介绍一个实际遇到的排查异常的case,涉及的知识点包括:类加载机制、jar包中的类加载顺序、JVM双亲委派模型、破坏双亲委派模型及自定义类加载器的代码示例;
问题背景
业务版本,旧功能升级,原先引用的一个二方包中的dubbo接口入参新增了属性,本次需要用到这个新属性;因此在pom中升级了该二方包的version;
在本地环境测试功能通过;
到test环境时,编译启动都正常,当运行时执行到该模块代码时报错java.lang.NoSuchMethodError;
问题排查
1. 初步推测是使用的snapshot二方包在部署test环境前被替换,原先的新增加的属性所在的包被旧版本代码替换,导致NoSuchMethodError;
通过查看仓库中包上传的记录,发现包并没有被替换;
这种情况比较极端,如果是编译前被替换,理论上应该编译不通过才对;
2. 推测可能是工程中存在"全限定类名完全一致"的多个类
Ctrl+N搜索类名,发现确实如此!
历史原因
这两个类实际上是有关系的;
由于历史原因,对JarA所在的工程做了微服务拆分,将C类所在的dubbo接口拆到了JarB所在的新的微服务工程;
为了减小切换微服务对业务方的影响,JarB所在的微服务工程打包时,C类所在的dubbo接口及相关类的全限定类名与原JarA所在的工程保持一致;
同时,原JarA所在的工程的dubbo服务,内部转发调用到新服务;
这样,就保证了新服务部署后,zk中新老服务名相同,业务方无感知;
问题分析
项目中存在2个全限定类名相同的类,这里记为C类;
工程中的C类分别来自Maven依赖中的2个二方包,分别记为JarA和JarB;
JarB中的C类的定义与JarA中的C类的定义基本一致,本次JarB新包中C类新增了一个属性M;
在编译过程中,编译器根据Maven的pom文件引入依赖的顺序,先加载了JarB,从而使用JarB的C类,因此代码中执行新属性M的setter方法,编译是通过的;
根据JVM的双亲委派模型,默认情况下相同全限定类名的类只会加载一次,因此JVM加载C类时只会从JarA或JarB选一个;
运行时,JVM加载类C,根据【操作系统】的选择,本次加载了JarA的C类的class文件,而JarA的C类没有新属性属性M,因此执行M的setter方法,报运行时异常提示找不到setter方法;
同名的两个C类来自不同的二方Jar包,他们是平级的,根据JVM的类加载机制——双亲委派模型,相同全限定类名的类默认只会加载一次(除非手动破坏双亲委派模型);
Jar包中的类是使用AppClassLoader加载的,而类加载器中有一个命名空间的概念,同一个类加载器下,相同包名和类名的class只会被加载一次,如果已经加载过了,直接使用加载过的;
总的来说,编译器根据pom中的引包顺序选择了我们预期的JarB的C类,而运行时JVM仅加载了JarA中的旧的C类;因此导致——编译通过,运行时提示NoSuchMethodError;
小结
1.本次运行时java.lang.NoSuchMethodError产生的原因?
项目二方包中存在多个全限定类名相同的类,运行时加载错了类;
2.既然选错了类,为什么没有编译错误?
JVM类加载是一种懒加载模式,运行时在指定目录随机选择.class文件加载;
本地的编译器,改变编译器优先选择的Jar顺序从而选择哪个类(这个顺序在本地IDE中是可以手动调整的);
例如这里的例子中,由于是maven依赖,因此主需要把JarA的依赖放在JarB前面即可修改编译器选择的类加载顺序,修改后则此处直接编译不通过,提示新属性的setter方法不存在,如下:
3.如果依赖中有多个全限定类名相同的类,那JVM会加载哪一个类呢?
比较靠谱的说法是,操作系统本身,控制了Jar包的默认加载顺序;也就是说,对于我们来说是不明确不确定的!
而Jar包的加载顺序,是跟classpath这个参数有关,当使用idea启动springboot的服务时,可以看到classpath参数的;包路径越靠前,越先被加载;
换句话说,如果靠前的Jar包里的类被加载了,后面Jar包里有同名同路径的类,就会被忽略掉,不会被加载;
4.如何解决这类问题?
先说结论:因为操作系统控制加载顺序,运行时加载的类可能跟编译时选择的类不一致,因此这种情况原则上需要避免而不是解决!
理论上不应该出现两个全限定类名,如果有一般是因为2个二方包同时引用了某个依赖,此时做手动排除即可;
对于本次情况,JarA的最新包已经全部去除了这个"重复的类C",因此只需要更新JarA的二方包version即可,就不会有多个类了;
此外,JDK提供了一些骚操作来专门破坏双亲委派模型,可以让全限定类名相同的类被"加载多次";
5.如何实现全限定类名相同的类被"加载多次"?
这里使用最简单的方式,将自定义的类加载器的parent置位null,跳过应用程序类加载器,这样2个这样的自定义类加载器就可以分别加载这2个类;示例如下:
IDE中Jar引用顺序决定哪个类被加载:
分别加载这2个类的代码示例:
/*** @author Akira* @description* @date 2023/2/10*/
public class SameClassTestLocal {public static void main(String[] args) {ClassLoader classloader = Thread.currentThread().getContextClassLoader();try {// 获取所有SameClassTest的.class路径Enumeration<URL> urls = classloader.getResources("pkg/my/SameClassTest.class");while (urls.hasMoreElements()) {// url有2个:jar:file:/E:/jar/jarB.jar!/pkg/my/SameClassTest.class和jar:file:/E:/jar/jarA.jar!/pkg/my/SameClassTest.classURL url = (URL) urls.nextElement();String fullPath = url.getPath();System.out.println("fullPath: " + fullPath);// 截取fullPath 获取jar文件的路径以及文件名,用来读取.class文件String[] strs = fullPath.split("!");String jarFilePath = strs[0].replace("file:/", "");String classFullName = strs[1].substring(1).replace(".class", "").replace("/", ".");System.out.println("jarFilePath: " + jarFilePath);System.out.println("classFullName: " + classFullName);// 关键步骤:用兄弟类加载器分别加载 父加载器置位nullFile file = new File(jarFilePath);URLClassLoader loader = new URLClassLoader(new URL[]{file.toURI().toURL()}, null);try {// 加载类 .class转Class对象Class<?> clazz = loader.loadClass(classFullName);// 反射创建实体Object obj = clazz.getDeclaredConstructor().newInstance();// 获取全部属性final Field[] fields = clazz.getDeclaredFields();if (fields.length > 0) {// 这里通过反射获取Fields 判断是否有新属性"reqNo"final boolean containsReqNo = Stream.of(fields).map(Field::getName).collect(Collectors.toSet()).contains("reqNo");if (containsReqNo) {// 如果有则执行setter方法Method method = clazz.getMethod("setReqNo", String.class);method.invoke(obj, "seqStr");System.out.println("当前类的包路径:" + jarFilePath + ";当前类具备reqNo属性 " + "json:" + JSON.toJSONString(obj));} else {System.out.println("当前类的包路径:" + jarFilePath + ";当前类具备不具备属性-跳过");}}} catch (Exception e) {e.printStackTrace();}System.out.println("-----当前类加载完成-----");}} catch (IOException e) {e.printStackTrace();}}}
输出:
fullPath: file:/E:/jar/jarB.jar!/pkg/my/SameClassTest.class
jarFilePath: E:/jar/jarB.jar
classFullName: pkg.my.SameClassTest
当前类的包路径:E:/jar/jarB.jar;当前类具备reqNo属性 json:{"reqNo":"seqStr"}
-----当前类加载完成-----fullPath: file:/E:/jar/jarA.jar!/pkg/my/SameClassTest.class
jarFilePath: E:/jar/jarA.jar
classFullName: pkg.my.SameClassTest
当前类的包路径:E:/jar/jarA.jar;当前类具备不具备属性-跳过
-----当前类加载完成-----
补充知识:类加载器
类加载器的作用
类加载器,顾名思义就是一个可以将Java字节码加载为java.lang.Class实例的工具;这个过程包括,读取字节数组、验证、解析、初始化等;
类加载器的特点
懒加载/动态加载:JVM并不是在启动时就把所有的.class文件都加载一遍,而是在程序运行的过程中,用到某个类时动态按需加载;这个动态加载的特点为热部署、热加载做了有力支持;
依赖加载:跟Spring的Bean的依赖注入过程有点像,当一个类加载器加载一个类时,这个类所依赖的、引用的其他所有类,都由这个类加载器加载;除非在程序中显式地指定另外一个类加载器加载;
哪几种类加载器
启动类加载器(Bootstrap ClassLoader):负责加载<JAVA_HOME>\lib目录中的,或者被-Xbootclasspath参数所指定的路径,并且是虚拟机识别的类库加载到虚拟机内存中;无法被Java程序直接引用;自定义类加载器时,如果想设置Bootstrap ClassLoader为其父加载器,可直接设置parent=null;
扩展类加载器(Extension ClassLoader):负责加载<JAVA_HOME>\lib\ext目录中的,或者被java.ext.dirs系统变量所指定路径中的所有类库;其父类加载器为启动类加载器;
应用程序类加载器(Application ClassLoader):负责加载用户类路径(ClassPath)上所指定的类库;被启动类加载器加载的,但它的父加载器是扩展类加载器;在一个应用程序中,系统类加载器一般是默认类加载器;
自定义类加载器(User ClassLoader):用户自己定义的类加载器;一般情况下我们不会自定义类加载器,除非特殊情况破坏双亲委派模型,需要实现java.lang.ClassLoader接口;
一个类的唯一性
一个类的唯一性由加载它的类加载器和这个类的本身决定,类唯一标识包括2部分:(1)类的全限定名(2)类加载器的实例ID;
比较两个类是否相等(包括Class对象的equals()、isAssignableFrom()、isInstance()以及instanceof关键字等),只有在这两个类是由同一个类加载器加载的前提下才有意义;否则,即使这两个类来源于同一个Class文件,被同一个虚拟机加载,只要加载它们的类加载器不同,这两个类就必定不相等;
补充知识:JVM双亲委派
什么是双亲委派
实现双亲委派机制,首先检查这个类是不是已经被加载过了,如果加载过了直接返回,否则委派给父加载器加载,这是一个递归调用,一层一层向上委派,最顶层的类加载器(启动类加载器)无法加载该类时,再一层一层向下委派给子类加载器加载;
JVM的类加载器是分层次的,它们有父子关系,而这个关系不是继承维护,而是组合,每个类加载器都持有一个parent字段;
除了启动类加载器外,其他所有类加载器都需要继承抽象类ClassLoader;
ClassLoader类的3个关键方法
defineClass方法:调用native方法把Java类的字节码解析成一个Class对象;
findClass:找到.class文件并把.class文件读到内存得到字节码数组,然后调用defineClass方法得到Class对象;
loadClass:实现双亲委派机制;当一个类加载器收到“去加载一个类”的请求时,会先把这个请求“委派”给其父类类加载器;这样无论哪个层的类加载器,加载请求最终都会委派给顶层的启动类加载器,启动类加载器在其目录下尝试加载该类;父加载器找不到该类时,子加载器才会自己尝试加载这个类;
为什么使用双亲委派模型?
双亲委派保证类加载器"自下而上的委派,自上而下的加载",保证每一个类在各个类加载器中都是同一个类,换句话说,就是保证一个类只会加载一次;
一个非常明显的目的就是保证java官方的类库<JAVA_HOME>\lib和扩展类库<JAVA_HOME>\lib\ext的加载安全性,不会被开发者覆盖;
如果开发者通过自定义类尝试覆盖JDK中的类并加载,JVM一定会优先加载JDK中的类而不再加载用户自己尝试覆盖而定义的类;例如类java.lang.Object,它存放在rt.jar之中,无论哪个类加载器要加载这个类,最终都是委派给启动类加载器加载,因此Object类在程序的各种类加载器环境中都是同一个类;
此外,根据ClassLoader类的源码(java.lang.ClassLoader#preDefineClass),java禁止用户用自定义的类加载器加载java.开头的官方类,也就是说只有启动类加载器BootstrapClassLoader才能加载java.开头的官方类;
类似的,如果开发者自己开发开源框架,也可以自定义类加载器,利用双亲委派模型,保护自己框架需要加载的类不被应用程序覆盖;
破坏双亲委派?
双亲委派模型并不是一个具有强制性约束的模型,而是Java设计者推荐给开发者们的类加载器实现方式;当开发者有特殊需求时,这个委派和加载顺序完全是可以被破坏的:
如想要自己显示的加载某个指定类;
或者由于一些框架的特殊性,如Tomcat需要加载不同工程路径的类,Tomcat中可以部署多个web项目,为了保证每个web项目互相独立,所以不能都由AppClassLoader加载,所以自定义了类加载器WebappClassLoader;
以及本篇提到的加载全限定类名相同的多个类;
破幻双亲委派模型的方式:
上面介绍了,实现双亲委派的核心就在ClassLoader#loadClass;如果想不遵循双亲委派的类加载顺序,可以自定义类加载器,重写loadClass,不再先委派父亲类加载器而是选择优先自己加载;
另一种简单粗暴的方式就是直接将父加载器parent指定位null,这样做主要就是跳过了默认的应用程序类加载器(Application ClassLoader),自己来加载某个指定类;
参考:
Java双亲委派模型:为什么要双亲委派?如何打破它?破在哪里?
如何加载两个jar包中含有相同包名和类名的类
JVM jar包加载顺序
相关文章:
编码踩坑——运行时报错java.lang.NoSuchMethodError / 同名类加载问题 / 双亲委派【建议收藏】
本篇介绍一个实际遇到的排查异常的case,涉及的知识点包括:类加载机制、jar包中的类加载顺序、JVM双亲委派模型、破坏双亲委派模型及自定义类加载器的代码示例;问题背景业务版本,旧功能升级,原先引用的一个二方包中的du…...
软件测试选Python还是Java?
目录 前言 1、先从一门语言开始 2、两个语言的区别 3、两个语言的测试栈技术 4、如何选择两种语言? 总结 前言 对于工作多年的从业者来说,同时掌握java和Python两门语言再好不过,可以大大增加找工作时的选择范围。但是对于转行的人或者…...
“2023数据安全智能化中国行”活动,开幕即高能
工信部等16部门近日发布的《关于促进数据安全产业发展的指导意见》提出,到2025年,数据安全产业基础能力和综合实力明显增强,数据安全产业规模超过1500亿元,年复合增长率超过30%。到2035年,数据安全产业进入繁荣成熟期。…...
机器人操作规划——Deep Visual Foresight for Planning Robot Motion(2017 ICRA)
1 简介 model-based RL方法,预测Action对图像的变化,以push任务进行研究。 采用完全自监督的学习方式,不需要相机标定、3D模型、深度图像和物理仿真。 2 数据集 采用几百个物体、10个7dof机械臂采集了包括5万个push attempts的数据集。 每…...
go 连接redis集群
最近用redis shake做redis数据迁移,由于redis提供的客户端没有用于查看集群的工具,且我部署的redis集群是基于k8s来构建的,没有使用ingress做转发,所以只能在k8s内部访问集群,于是我先用gogin框架编写了访问redis集群的…...
LeetCode 146. LRU 缓存
原题链接 难度:middle\color{orange}{middle}middle 题目描述 请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。 实现 LRUCacheLRUCacheLRUCache 类: LRUCache(intcapacity)LRUCache(int capacity)LRUCache(intcapacity) 以 正整数 …...
【mac】在m2 mbp上通过Parallels Desktop安装ubuntu22.04
文章目录前言一、参考文章二、版本信息三、方法1:通过ubuntu官网提供的iso安装3.1 配置服务器3.2 安装图形界面四、方法2:通过Parallels Desktop提供的安装包五、 小工具5.1 调整应用栏图标大小5.2 ubuntu获取mac的剪切板5.3 调整terminal字体大小5.4 安装samba5.5 ubuntu连接m…...
C++类和对象,初见类
坚持看完,结尾有思维导图总结 这里写目录标题C语言和 C 的区别类的定义类的初认识类的内容访问限定符类的作用域类的实例化类中的 this 指针总结C语言和 C 的区别 C 的祖师爷除了在 C语言的基础上化简了一些复杂操作 更为重要的是,两个语言实现的过程是…...
Redis常用数据结构及应用场景
1.总体结构 Redis中的数据,总体上是键值对,不同数据类型指的是键值对中值的类型。 2.string类型 Redis中最基本的类型,它是key对应的一个单一值。二进制安全,不必担心由于编码等问题导致二进制数据变化。所以redis的string可以…...
C++虚继承内存布局
C菱形继承内存布局 编译器:Visual Studio 2019 关于如何查看内存布局 B class B { public:B(): _ib(10), _cb(B){cout << "B()" << endl;}B(int ib, char cb): _ib(ib), _cb(cb){cout << "B(int,char)" << endl;}vi…...
IO模型--从BIO、NIO、AIO到内核select、poll、epoll剖析
IO基本概述 IO的分类 IO以不同的维度划分,可以被分为多种类型;从工作层面划分成磁盘IO(本地IO)和网络IO; 也从工作模式上划分:BIO、NIO、AIO;从工作性质上分为阻塞式IO与非阻塞式IO;…...
Zebec完成BNB Chain以及Near链上协议部署,多链化进程加速
从去年开始,Zebec 就开始以多链的形式来拓展自身的流支付生态,一方面向更多的区块链系统拓展自身流支付协议,即从Solana上向EVM链上对协议与通证等进行迁移与拓展。目前基本完成了在BNB Chain以及Near上的合约部署,且能够在这些EV…...
wpscan常见的使用方法
目录 简单介绍 暴力破解 信息收集 指定用户爆破 命令集合 简单介绍 Wordpress是一个以PHP和MySQL为平台的免费自由开源的博客软件和内容管理系统。 WPScan是Kali Linux默认自带的一款漏洞扫描工具,它采用Ruby编写,能够扫描WordPress网站中的多种安…...
Tree 底层源码实现(二叉树、递归、迭代)
树(Tree)是一种非线性数据结构,由一组节点和它们之间的边组成。在树中,每个节点都有零个或多个子节点,除了根节点外,每个节点都有且仅有一个父节点。树可以被用于许多应用程序,如文件系统、XML文…...
家政服务小程序实战教程13-接入客服
小程序在微信里使用,以其无需安装随用随走为特点。但是有个问题是,如果提供商品或者服务的,用户如果有问题往往希望平台的运营方给出专业的解答。为了满足这类需求,就需要我们提供客服接入的功能,用户可以点击客服图标…...
大白话高并发(三)
背景 高并发得第三篇,讲一讲压测吧,因为我的目的是模拟100万人同时来秒杀。 是不是真的要找100万个人 没必要 ,你就算100万人掐着表在同一毫秒内把请求请求某一台机器,服务器也不可能在同一时间处理那么多请求,因为…...
vue全家桶(四)前端工程化
vue全家桶(四)前端工程化1.模块化的相关规范1.1模块化概述1.2模块化的分类A.浏览器端的模块化B.服务器端的模块化C.ES6模块化1.2.1 Node.js中通过bable体验ES6模块化1.2.2 ES6模块化的基本语法1.2.2.1 默认导出与默认导入1.2.2.2 按需导出与按需导入1.2.…...
超螺旋滑模控制(STA)
超螺旋滑模控制(Super Twisting Algorithm, STA) 超螺旋滑模控制又称超扭滑模控制,可以说是二阶系统中最好用的滑模控制方法。 系统模型 对于二阶系统可以建立具有标准柯西形式的微分方程组 {x˙1x2x˙2fg⋅u\begin{cases} \dot x_1 x_2 \\ \dot x_2 f g \cdo…...
NX二次开发编译时dll自动数字签名及拷贝
前言 在UG5.0开始,所有基于UG二次开发的DLL都要“签名”后才能被客户端上正版的NX调用。 一、基于C# 开发签名 1、添加资源文件 (1)项目类库上右键–>属性–>资源–>添加资源右边小三角–>添加现有文件–>切换到UG安装目录下…...
教你如何搭建人事OA-薪资管理系统,demo可分享
1、简介1.1、案例简介本文将介绍,如何搭建人事OA-薪资管理。1.2、应用场景根据设置薪资基础及考勤和绩效的数据计算得到各个员工工资详情。2、设置方法2.1、表单搭建1)新建表单【工资表】,字段设置如下;名称类型名称类型人员资料分…...
ChIP-seq 分析:Mapped 数据可视化(4)
1. Mapped reads 现在我们有了 BAM 文件的索引,我们可以使用 idxstatsBam() 函数检索和绘制映射读取的数量。 mappedReads <- idxstatsBam("SR_Myc_Mel_rep1.bam")TotalMapped <- sum(mappedReads[, "mapped"])ggplot(mappedReads, aes(x…...
Jenkins 基于Kubernetes 弹性构建池
流程:创建Jenkins Agent;获取Jenkins Agent的参数;渲染yaml模板;调用K8s API在固定的NS中创建一个Pod;运行Jenkins pipeline到agent;创建Agentimport hudson.model.Node.Mode import hudson.slaves.* impor…...
经典算法题---链表奇偶重排(好题)双指针系列
我听别人说这世界上有一种鸟是没有脚的,它只能够一直的飞呀飞呀,飞累了就在风里面睡觉,这种鸟一辈子只能下地一次,那一次就是它死亡的时候。——《阿甘正传》这一文章讲解链表的奇偶排序问题,这是一道不难但是挺好的链…...
数据仓库实战
目录1、最佳实战1.1 表的分类1.2 ETL策略1.3 任务调度2、项目实战2.1 项目概述2.2 数据描述2.3 架构设计2.4 环境搭建2.5 项目开发1、最佳实战 1.1 表的分类 维度建模中表的类型:事实表和维度表 事实表又可以分为:事务事实表、周期快照事实表、累积快照…...
GPT系列:GPT, GPT-2, GPT-3精简总结 (模型结构+训练范式+实验)
😄 花一个小时快速跟着 人生导师-李沐 过了一遍GPT, GPT-2, GPT-3。下面精简地总结了GPT系列的模型结构训练范式实验。 文章目录1、GPT1.1、模型结构:1.2、范式:预训练 finetune1.3、实验部分:2、GPT-22.1、模型结构2.2、范式:预…...
ASE12N65SE-ASEMI高压MOS管ASE12N65SE
编辑-Z ASE12N65SE在ITO-220AB封装里的静态漏极源导通电阻(RDS(ON))为0.68Ω,是一款N沟道高压MOS管。ASE12N65SE的最大脉冲正向电流ISM为48A,零栅极电压漏极电流(IDSS)为10uA,其工作时耐温度范围为-55~150摄氏度。ASE…...
centos8防火墙命令配置(开放端口)
查看防火墙状态:(root用户)firewall-cmd –state启动防火墙:(root用户)systemctl start firewalld.service查看防火墙开放端口:(root用户) firewall-cmd --list-ports …...
Instagram营销教程_编程入门自学教程_菜鸟教程-免费教程分享
教程简介 Instagram营销初学者教程 - 从简单和简单的步骤学习Instagram营销从基本到高级概念,包括概述,业务战略,安装和注册,发布和参与,活动审查,微调内容,营销工具和应用程序,集成…...
HTTP Code含义
HTTP Code描述详细100继续100(继续)状态代码表示一个已收到请求,尚未被拒绝服务器。服务器打算在请求已完全收到并已采取行动。当请求包含 Expect 标头字段时100-continue expectation,100响应表示服务器希望接收请求有效负载主体…...
Elasticsearch:Security API 介绍
在我之前的文章 “Elasticsearch:运用 API 创建 roles 及 users” ,我展示了如何使用 Security API 来创建用户及角色来控制访问 Elasticsearch 中的索引。在今天的文章中,我将展示一个使用 Security API 来创建一个用户及角色来访问一个索引…...
贵州建设监理协会网站/公司网站建设步骤
MySql noinstall-5.1.xx-win32 配置(原创)1、解压mysql-noinstall-5.1.xx-win32.zip 到你喜欢的目录,例如:d:\php\mysql2、在根目录d:\php\mysql中有五个配置信息文件:my-small.ini (内存 < 64M)my-medium.ini (内存 128M )my-large.ini (内存 512M)m…...
wordpress最新版爆破/新网站seo
19.12 添加自定义监控项目需求:监控某台web的80端口连接数,并出图两步:1)zabbix监控中心创建监控项目;2)针对该监控项目以图形展现1 需要到客户端定义脚本[rootcentos7-02 sbin]#vim /usr/local/sbin/estab…...
深圳网站开发工资/seo如何提高排名
每天一道大厂SQL题【Day03】订单量统计 大家好,我是Maynor。相信大家和我一样,都有一个大厂梦,作为一名资深大数据选手,深知SQL重要性,接下来我准备用100天时间,基于大数据岗面试中的经典SQL题,…...
西安当地做网站的公司/网络营销的概念及特征
毕业大半年了,现在还清晰的记得当时毕业论文不会用SPSS的痛苦,每天挣扎把度娘、知乎、知网、优酷、某宝等各大网站都逛了个遍,依然没有找到用SPSS完整的分析一份问卷的流程,几乎都是零零散散的一些知识,又或是几十个视…...
酒类网站如何做/推广网站源码
定期更新新媒体知识,在学习之中,有问题可以留言,大家交流,谢谢!新媒体平台视觉呈现 头像:表明账号主体,加深印象 名称:账号的名字,最好能说明账号内容领域或主题&#x…...
网站开发形象设计要求/怎样联系百度客服
window.history 我们可以通过window.history的两个方法来控制浏览器的路由改变,但不会让浏览器刷新页面。 pushState 会追加浏览器的路由历史,但不会刷新页面,可以用这种方式来实现前端路由的控制。 history.pushState(state, title[, ur…...