百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

JVM的艺术—类加载器篇(jvm的类加载机制)

nanshan 2024-11-10 10:11 11 浏览 0 评论

今天我们继续来深入的剖析类加载器的内容。上节课我们讲了类加载器的基本内容,拉勾IT课小编分解:

什么是定义类加载器和初始化类加载器?

· 定义类加载器:假设我们的某一个类是由ExtClassLoader加载的,那么ExtClassLoader称为该类的定义类加载器

· 初始化加载器:能够返回Class对象引用的都叫做该类的初始类加载器,比如类A是由我们的ExtClassLoader加载,那么

ExtClassLoader是该类的定义类加载器,也是该类的初始类加载器,而我们的AppClassLoader也能返回我们A类的引用

那么AppClassLoader也是该类的初始类加载器。

什么是类加载器的双亲委派模型?



上篇文章我们提到了类加载器的双亲委派模型,也可以称为双亲委托模型。今天这篇文章我们就来把这个概念给讲明白。

概念:用一种简单的方式去描述双亲委托的概念。可以分为两个部分去理解

1委托:

jvm加载类的时候是通过双亲委派的方式去加载,自下而上的去委托。

自定义类加载器需要加载类时,先委托应用类加载器去加载,然后应用类加载器又向扩展类加载器去委托,扩展类加载器在向启动类加载器去委托。

如果启动类加载器不能加载该类。那么就向下加载

2加载:

jvm加载类的时候是通过双亲委派的方式去加载委托,但是加载的时候是由上向下去加载的,当委托到最顶层启动类加载器的时候,无法在向上委托,那么

启动类加载器就开始尝试去加载这个类,启动类加载器加载不了就向下交给扩展类加载器去加载,扩展类加载器加载不了就继续向下委托交给应用类加载器

去加载,以此类推。

如果文字描述你还不清楚什么是双亲委托机制,那么我画了一幅图可以更清楚类加载的过程。如下:


通过上图,我们知道更能清楚的知道,双亲委托模型的工作机制,用一句简单的话说,就是需要加载一个类的时候,向上委托,向下加载。

注意:在双亲委派机制中,各个加载器按照父子关系形成树型结构,除了根加载器以外,每一个加载器有且只有一个父加载器。

接下来,我也从jdk底层源码的角度给大家画了一张类加载的主要过程,图如下:



以上就是类加载器加载一个类的重要过程步骤。希望各位小伙儿可以结合源码的方式,仔细再研究一下。其实还挺好理解的。

下面咱们再说说,java采用双亲委托的方式去加载类,这样做的好处是什么呢?

· 双亲委派模型的好处

总所周知:java.lang.object类是所有类的父类,所以我们程序在运行期间会把java.lang.object类加载到内存中,假如java.lang.object类

能够被我们自定义类加载器去加载的话,那么jvm中就会存在多份Object的Class对象,而且这些Class对象是不兼容的。

所以双亲委派模型可以保证java核心类库下的类型的安全。

借助双亲委派模型,我们java核心类库的类必须是由我们的启动类加载器加载的,这样可以确保我们核心类库只会在jvm中存在一份

这就不会给自定义类加载器去加载我们核心类库的类。

根据我们的演示案例,一个class可以由多个类加载器去加载,同时可以在jvm内存中存在多个不同版本的Class对象,这些对象是不兼容的。

并且是不能相互转换的。

什么是全盘委托加载?



解释:假如我们的Person类是由我们的系统类APP类加载器加载的,而person类所依赖的Dog类也会委托给App系统类进 行加载,这个委托过程也遵循双亲委派模型。代码如下

  • person类代码中创建Dog实例

public class Person {

  public Person(){
  
      new Dog();
  }

}

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

· 测试类

·         public class MainClass02 {
·          
·             public static void main(String[] args) throws Exception {
·                 //创建自定义类加载器的一个实例,并且通过构造器指定名称
·                 Test01ClassLoader myClassLoader = new Test01ClassLoader("loader1");
·                 myClassLoader.setPath("I:\test\");
·                 Class<?> classz = myClassLoader.loadClass("com.test.Person");
·                 System.out.println(classz.getClassLoader());
·                 System.out.println(Dog.class.getClassLoader());
·             }
·         }
·          
·          
·         运行结果:
·          
·         sun.misc.Launcher$AppClassLoader@18b4aac2
·         sun.misc.Launcher$AppClassLoader@18b4aac2
·          
·         Process finished with exit code 0

从上面的运行结果,我们可以看出,当我们用自定义类加载器去加载我们的Person的时候,根据双亲委托模型,我们的Person并没有被自定义类加载(Test01ClassLoader)加载,而是被AppClassloader加载成功,同时根据全盘委托规则,我们的Dog类也被AppClassLoader加载了。所以大家一定要记住这个至关重要的结论。为我们后面的学习打下坚实的基础。

下面我们在看一个例子。我们把类路径下的Person.class文件删除掉,然后再运行一下上面的main函数,看看结果。代码如下:


通过那行结果我们看出,Person类是由我们的自定义类加载器加载的。那为什么Dog类没有进行全盘委托的,这是因为双亲委托模型的缘故,我们的类路径下并没有Person类,故此AppClassLoader是无法加载我们的路径I:\test\下的com.test.Person.class文件的。所以Person类是由我们自定的类加载器加载的。再看Dog类,由于它的加载要遵循双亲委托模型,因为类路径下有Dog.class文件,所以AppClassLoader就可以加载Dog类。故此加载Dog类的ClassLoader是AppClassLoader。写到这里,大家对类加载已经有了一个非常深刻的理解。那么java为什么使用双亲委托模型的好处我相信已经不言而喻了。那么下面来说说双亲委托模型,有没有他的弊端呢,或者说有什么不好的地方嘛?我们可以打破这种双亲委托的方式去加载类嘛?下面我们来看一个例子。

类加载器的命名空间

说到双亲委托模型的弊端,那我就离不开命名空间的概念。

类加载器的命名空间 是由类加载器本身以及所有父加载器所加载出来的binary name(full class name)组成.

①:在同一个命名空间里,不允许出现二个完全一样的binary name。

②:在不同的命名空间种,可以出现二个相同的binary name。当时二者对应的Class对象是相互不能感知到的,也就是说Class对象的类型是不一样的。

解释:同一个Person.class文件 被我们的不同的类加载器去加载,那么我们的jvm内存中会生成二个对应的Person的Class对象,而且这二个对应的Class对象是相互不可见的(通过Class对象反射创建的实例对象相互是不能够兼容的不能相互转型**

③:子加载器的命名空间中的binary name对应的类中可以访问 父加载器命名空间中binary name对应的类,反之不行

下面准备了一张图,以便于大家的理解。



上面这张图就很好的解释了命名空间的概念。大家可以再好好的体会一下。

我们光画图,光用嘴说并不是一种很有力的证据,就如同我写在这篇博文的时候所提,我们在学习和掌握某个概念的时候,就必须要拿出有力的证据,来证明自己的猜想或者是观点,那我们就举一个例子。来验证一下我们上面的理论是否正确。代码如下:

这是Person类的代码。

package com.test;
 
public class Person {
 
    public Person() {
        new Dog();
        System.out.println("Dog的classLoader:-->"+ Dog.class.getClassLoader());
    }
 
    static{
        System.out.println("person类被初始化了");
    }
}

这是Dog类的代码。

package com.test;
 
public class Dog {
 
    public Dog(){
        System.out.println("Dog 的构造函数");
    }
}
 

具体的验证思路是这样的,首先我们把Person类的Class文件放到启动类加载器的加载目录下(C:Program FilesJavajdk1.8.0_144jreclasses 这是启动类加载器的加载目录)来达到Person类交给启动类加载器加载的目的。

然后呢,我们让Dog类去被AppClassLoader(系统类加载器去加载)。然后我们在Person类中去访问Dog类。看看能否访问成功。

测试环境:把我们的Person.class放置在C:Program FilesJavajdk1.8.0_131jreclasses这个目录下,那么我们的Person.class就会被我们的启动类加载器加载,而我们的Dog类是被AppClassLoader进行加载,我们的Person类 中引用我们的Dog类会抛出异常.

创建main方法进行测试:

package com.test;
 
import java.lang.reflect.Method;
 
/**
 * jvm 类加载器 第一章
 * @author 奇客时间-时光
 * 自定义类加载器——命名空间
 * 测试父加载所加载的类,不能访问子加载器所加载的类。
 */
public class MainClass02 {
 
    public static void main(String[] args) throws Exception {
 
        System.out.println("Person的类加载器:"+Person.class.getClassLoader());
 
        System.out.println("Dog的类加载器:"+Dog.class.getClassLoader());
 
        Class<?> clazz = Person.class;
        clazz.newInstance();
 
 
    }
}
 
运行结果:
    
"C:Program FilesJavajdk1.8.0_144binjava.exe" "-javaagent:C:Program FilesJetBrainsIntelliJ IDEA 2019.2libidea_rt.jar=59226:C:Program FilesJetBrainsIntelliJ IDEA 2019.2bin" -Dfile.encoding=UTF-8 -classpath "C:Program FilesJavajdk1.8.0_144jrelibcharsets.jar;C:Program FilesJavajdk1.8.0_144jrelibdeploy.jar;C:Program FilesJavajdk1.8.0_144jrelibextaccess-bridge-64.jar;C:Program FilesJavajdk1.8.0_144jrelibextcldrdata.jar;C:Program FilesJavajdk1.8.0_144jrelibextdnsns.jar;C:Program FilesJavajdk1.8.0_144jrelibextjaccess.jar;C:Program FilesJavajdk1.8.0_144jrelibextjfxrt.jar;C:Program FilesJavajdk1.8.0_144jrelibextlocaledata.jar;C:Program FilesJavajdk1.8.0_144jrelibextnashorn.jar;C:Program FilesJavajdk1.8.0_144jrelibextsunec.jar;C:Program FilesJavajdk1.8.0_144jrelibextsunjce_provider.jar;C:Program FilesJavajdk1.8.0_144jrelibextsunmscapi.jar;C:Program FilesJavajdk1.8.0_144jrelibextsunpkcs11.jar;C:Program FilesJavajdk1.8.0_144jrelibextzipfs.jar;C:Program FilesJavajdk1.8.0_144jrelibjavaws.jar;C:Program FilesJavajdk1.8.0_144jrelibjce.jar;C:Program FilesJavajdk1.8.0_144jrelibjfr.jar;C:Program FilesJavajdk1.8.0_144jrelibjfxswt.jar;C:Program FilesJavajdk1.8.0_144jrelibjsse.jar;C:Program FilesJavajdk1.8.0_144jrelibmanagement-agent.jar;C:Program FilesJavajdk1.8.0_144jrelibplugin.jar;C:Program FilesJavajdk1.8.0_144jrelibresources.jar;C:Program FilesJavajdk1.8.0_144jrelibrt.jar;I:jvmoutproductionjvm-classloader" com.test.MainClass02
Person的类加载器:null
Dog的类加载器:sun.misc.Launcher$AppClassLoader@18b4aac2
person类被初始化了
Exception in thread "main" java.lang.NoClassDefFoundError: com/test/Dog
         at com.test.Person.<init>(Person.java:7)
         at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
         at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
         at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
         at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
         at java.lang.Class.newInstance(Class.java:442)
         at com.test.MainClass02.main(MainClass02.java:20)
 
Process finished with exit code 1


总结:通过上面的代码我们就可以看出来,我们在Person中去new一个Dog的实例的时候,并没有创建成功,而是抛出了Exception in thread "main" java.lang.NoClassDefFoundError: com/test/Dog这样的异常,这也就证明了,我们上面所说的结论(父加载器所加载的类,不能访问子加载所加载的类。)

即启动类加载器所加载的类,不能访问系统类加载器所加载的类(AppClassLoader)。

那么肯定会有人问,我们的子加载器所加载的类,可以访问父加载器所加载的类嘛?我们不妨来证实一下,我们只需要改动一下MainClass02这个类的代码即可,让AppClassLoader去加载Dog类,让我们的自定义类加载器去加载我们的Person类。并在Person类中去访问Dog类。然后将之前C:Program FilesJavajdk1.8.0_131jreclasses目录下的Person中的Class文件删除掉,另外还有把我们类路径下的Person文件删除掉,并且在I:test目录下添加com.test.Person.class文件。代码如下:

package com.test;
 
import java.lang.reflect.Method;
 
/**
 * jvm 类加载器 第一章
 * @author 奇客时间-时光
 * 自定义类加载器
 * 测试子类加载器所加载的类,能否访问父加载器所加载的类。
 */
public class MainClass02 {
 
    public static void main(String[] args) throws Exception {
        //创建自定义类加载器的一个实例,并且通过构造器指定名称
        Test01ClassLoader myClassLoader = new Test01ClassLoader("loader1");
        myClassLoader.setPath("I:\test\");
        Class<?> classz = myClassLoader.loadClass("com.test.Person");
        System.out.println(classz.getClassLoader());
 
        System.out.println("Dog的类加载器:"+Dog.class.getClassLoader());
 
        classz.newInstance();
 
 
    }
}
 
运行结果:
"C:Program FilesJavajdk1.8.0_144binjava.exe" "-javaagent:C:Program FilesJetBrainsIntelliJ IDEA 2019.2libidea_rt.jar=60588:C:Program FilesJetBrainsIntelliJ IDEA 2019.2bin" -Dfile.encoding=UTF-8 -classpath "C:Program FilesJavajdk1.8.0_144jrelibcharsets.jar;C:Program FilesJavajdk1.8.0_144jrelibdeploy.jar;C:Program FilesJavajdk1.8.0_144jrelibextaccess-bridge-64.jar;C:Program FilesJavajdk1.8.0_144jrelibextcldrdata.jar;C:Program FilesJavajdk1.8.0_144jrelibextdnsns.jar;C:Program FilesJavajdk1.8.0_144jrelibextjaccess.jar;C:Program FilesJavajdk1.8.0_144jrelibextjfxrt.jar;C:Program FilesJavajdk1.8.0_144jrelibextlocaledata.jar;C:Program FilesJavajdk1.8.0_144jrelibextnashorn.jar;C:Program FilesJavajdk1.8.0_144jrelibextsunec.jar;C:Program FilesJavajdk1.8.0_144jrelibextsunjce_provider.jar;C:Program FilesJavajdk1.8.0_144jrelibextsunmscapi.jar;C:Program FilesJavajdk1.8.0_144jrelibextsunpkcs11.jar;C:Program FilesJavajdk1.8.0_144jrelibextzipfs.jar;C:Program FilesJavajdk1.8.0_144jrelibjavaws.jar;C:Program FilesJavajdk1.8.0_144jrelibjce.jar;C:Program FilesJavajdk1.8.0_144jrelibjfr.jar;C:Program FilesJavajdk1.8.0_144jrelibjfxswt.jar;C:Program FilesJavajdk1.8.0_144jrelibjsse.jar;C:Program FilesJavajdk1.8.0_144jrelibmanagement-agent.jar;C:Program FilesJavajdk1.8.0_144jrelibplugin.jar;C:Program FilesJavajdk1.8.0_144jrelibresources.jar;C:Program FilesJavajdk1.8.0_144jrelibrt.jar;I:jvmoutproductionjvm-classloader" com.test.MainClass02
自己的类加载器被加载了
com.test.Test01ClassLoader@677327b6
Dog的类加载器:sun.misc.Launcher$AppClassLoader@18b4aac2
Dog 的构造函数
 
Process finished with exit code 0

从上面的结果可以看出,Person是由我们的Test01ClassLoader自定义类加载器所加载的,那么它的父亲加载器是AppClassLoader,显然Dog类是由我们的AppClassLoader所加载的。故此代码正常运行,没有抛出异常,从而得出结论:

1:父加载器所加载的类,不能访问子加载器所加载的类。

2:子加载器所加载的类,可以访问父加载器所加载的类。

双亲委托模型的弊端

· 我们先看一段我们非常熟悉的数据库连接相关的代码片段。

·         Class.forName("com.mysql.jdbc.Driver");
·         Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/RUNOOB","root","123456");
·         Statement stmt = conn.createStatement();


案例分析

  • 在上述图中的第五步为什么会用线程上下文加载器进行加载呢?
  • 在双亲委托模型的机制下,类的加载是由下而上的。即下层的加载器会委托上层进行加载。有些接口是Java核心库(rt.jar)提供的例如上面的createStatement接口,而Java核心库是由启动类加载器进行加载的。而这些接口的具体实现是来自不同的厂商(Mysql)。而具体的实现都是通过依赖jar包放到我们项目中的classPath下的。Java的启动类加载器/根类加载器是不会加载这些其他来源的jar包。
  • 我们都知道classPath下的jar包是由我们系统类加载器/应用加载器进行加载,根据我们双亲委托的机制父类加载器是看不到子类(系统类加载器)所加载的具体实现。createStatement 这个接口是由根类加载器进行加载的 而具体的实现又加载不了。在双亲委托的机制下,createStatement这个接口就无具体的实现。
  • 我们Java的开发者就通过给当前线程池设置上下文加载器的机制,就可以由设置的上下文加载器来实现对于接口实现类的加载。换句话说父类加载器可以使用当前线程上下文加载器加载父类加载器加载不了的一些接口的实现。完美了解决了由于SPI模型(接口定义在核心库中,而实现由各自的厂商以jar的形式依赖到我们项目中)的接口调用。

下面我提供了一张SPI的流程图。不知道什么是SPI的小伙伴儿,可以看一下这张图:


从上面的例子,我们可以看出,双亲委托模型的弊端。然后我们的jdk给我们提供了一种通过修改线程上下文类加载的方式来打破这种双亲委托的规则。关于修改上下文类加载的话题,我们下个章节再具体的讲解。接下来呢,我们再看看,获取类加载器的几个方法。并且奉上翻译好的java doc文档。方便我们后续学习线程类加载器。

获取类加载器的几个方法

  • Class.getClassLoader()
/**
* Returns the class loader for the class(返回加载该类的类加载器). Some implementations may use
* null to represent the bootstrap class loader(有一些jvm的实现可能用null来表示我们的启动类加载器比如 hotspot).
* This method will return null in such implementations if this class was loaded by the bootstrap class loader.
* 若这个方法返回null的话,那么这个类是由我们的启动类加载器加载
*
* If this object represents a primitive type or void, null is returned.
(原始类型 比如int,long等等的类或者 void类型 那么他们的类加载器是null)
*
*
*/
public ClassLoader getClassLoader() {
ClassLoader cl = getClassLoader0();
if (cl == null)
return null;
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
ClassLoader.checkClassLoaderPermission(cl, Reflection.getCallerClass());
}
return cl;
}

· 1:返回代表加载该class的类加载器

· 2:有一些虚拟机(比如hotspot) 的启动类加载器是null来表示

· 3:原始类型 比如int ,long 或者是void类型 ,他们的类加载器是null

· ClassLoader.getSystemClassLoader()方法解读

/**
* Returns the system class loader for delegation(该方法返回系统类加载器). This is the default
* delegation parent for new ClassLoader instances(也是我们自己定义的类加载器的委托父类), and is
* typically the class loader used to start the application(通常系统类加载器是用来启动我们的应用的)
*
* This method is first invoked early in the runtime's startup
* sequence(程序在运行早起就会调用该方法), at which point it creates the system class loader and sets it
* as the context class loader of the invoking <tt>Thread</tt>.(在那个时间,调用线程创建我们的系统类加载器同时把系统类加载器设置到我们线程上下文中)
*
* <p> The default system class loader is an implementation-dependent
* instance of this class.(这句话没有很好的理解)
*
* <p> If the system property "<tt>java.system.class.loader</tt>" is defined
* when this method is first invoked then the value of that property is
* taken to be the name of a class that will be returned as the system
* class loader. The class is loaded using the default system class loader
* and must define a public constructor that takes a single parameter of
* type <tt>ClassLoader</tt> which is used as the delegation parent. An
* instance is then created using this constructor with the default system
* class loader as the parameter. The resulting class loader is defined
* to be the system class loader.
我们可以通过java.system.class.loader 系统属性来指定一个自定义的类加载的二进制名称作为新的系统类加载器,
在我们自定的加载中我们需要定义个带参数的构造函数,参数为classLoader,那么我们这个自定义的类加载器就会看做系统类加载器
 
*
* @return The system <tt>ClassLoader</tt> for delegation, or
* <tt>null</tt> if none
*
* @throws SecurityException
* If a security manager exists and its <tt>checkPermission</tt>
* method doesn't allow access to the system class loader.
*
* @throws IllegalStateException
* If invoked recursively during the construction of the class
* loader specified by the "<tt>java.system.class.loader</tt>"
* property.
*
* @throws Error
* If the system property "<tt>java.system.class.loader</tt>"
* is defined but the named class could not be loaded, the
* provider class does not define the required constructor, or an
* exception is thrown by that constructor when it is invoked. The
* underlying cause of the error can be retrieved via the
* {@link Throwable#getCause()} method.
*
* @revised 1.4
*/
@CallerSensitive
public static ClassLoader getSystemClassLoader() {
//初始化系统类加载器
initSystemClassLoader();
if (scl == null) {
return null;
}
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkClassLoaderPermission(scl, Reflection.getCallerClass());
}
return scl;
}
  • 1:该方法的作用是返回系统类加载器
  • 2:也是我们自定义加载器的直接父类
  • 3:系统类加载器是用来启动我们的应用的
  • 4:在系统早期,调用线程会创建出我们的系统类加载器,并且把我们的系统类加载器设置到当前线程的上下文中


相关推荐

F5负载均衡器如何通过irules实现应用的灵活转发?

F5是非常强大的商业负载均衡器。除了处理性能强劲,以及高稳定性之外,F5还可以通过irules编写强大灵活的转发规则,实现web业务的灵活应用。irules是基于TCL语法的,每个iRules必须包含...

映射域名到NAS

前面介绍已经将域名映射到家庭路由器上,现在只需要在路由器上设置一下端口转发即可。假设NAS在内网的IP是192.168.1.100,NAS管理端口2000.你的域名是www.xxx.com,配置外部端...

转发(Forward)和重定向(Redirect)的区别

转发是服务器行为,重定向是客户端行为。转发(Forward)通过RequestDispatcher对象的forward(HttpServletRequestrequest,HttpServletRe...

SpringBoot应用中使用拦截器实现路由转发

1、背景项目中有一个SpringBoot开发的微服务,经过业务多年的演进,代码已经累积到令人恐怖的规模,亟需重构,将之拆解成多个微服务。该微服务的接口庞大,调用关系非常复杂,且实施重构的人员大部分不是...

公司想搭建个网站,网站如何进行域名解析?

域名解析是将域名指向网站空间IP,让人们通过注册的域名可以方便地访问到网站的一种服务。IP地址是网络上标识站点的数字地址,为方便记忆,采用域名来代替IP地址标识站点地址。域名解析就是域名到IP地址的转...

域名和IP地址什么关系?如何通过域名解析IP?

一般情况下,访客通过域名和IP地址都能访问到网站,那么两者之间有什么关系吗?本文中科三方针对域名和IP地址的关系和区别,以及如何实现域名与IP的绑定做下介绍。域名与IP地址之间的关系IP地址是计算机的...

分享网站域名301重定向的知识

网站域名做301重定向操作时,一般需要由专业的技术来协助完成,如果用户自己在维护,可以按照相应的说明进行操作。好了,下面说说重点,域名301重定向的操作步骤。首先,根据HTTP协议,在客户端向服务器发...

NAS外网到底安全吗?一文看懂HTTP/HTTPS和SSL证书

本内容来源于@什么值得买APP,观点仅代表作者本人|作者:可爱的小cherry搭好了NAS,但是不懂做好网络加密,那么隐私泄露也会随时发生!大家好,这里是Cherry,喜爱折腾、玩数码,热衷于分享数...

ForwardEmail免费、开源、加密的邮件转发服务

ForwardEmail是一款免费、加密和开源的邮件转发服务,设置简单只需4步即可正常使用,通过测试来看也要比ImprovMX好得多,转发近乎秒到且未进入垃圾箱(仅以Mailbox.org发送、Out...

使用CloudFlare进行域名重定向

当网站变更域名的时候,经常会使用域名重定向的方式,将老域名指向到新域名,这通常叫做:URL转发(URLFORWARDING),善于使用URL转发,对SEO来说非常有用,因为用这种方式能明确告知搜索引...

要将端口5002和5003通过Nginx代理到一个域名上的操作笔记

要将端口5002和5003通过Nginx代理到域名www.4rvi.cn的不同路径下,请按照以下步骤配置Nginx:步骤说明创建或编辑Nginx配置文件通常配置文件位于/etc/nginx/sites...

SEO浅谈:网站域名重定向的三种方式

在大多数情况下,我们输入网站访问网站的时候,很难发现www.***.com和***.com的区别,因为一般的网站主,都会把这两个域名指向到同一网站。但是对于网站运营和优化来说,www.***.com和...

花生壳出现诊断域名与转发服务器ip不一致的解决办法

出现诊断域名与转发服务器ip不一致您可以:1、更改客户端所处主机的drs为223.5.5.5备用dns为119.29.29.29;2、在windows上进入命令提示符输入ipconfig/flush...

涨知识了!带你认识什么是域名

1、什么是域名从技术角度来看,域名是在Internet上解决IP地址对应的一种方法。一个完整的域名由两个或两个以上部分组成,各部分之间用英文的句号“.”来分隔。如“abc.com”。其中“com”称...

域名被跳转到其他网站是怎么回事

当你输入域名时被跳转到另一个网站,这可能是由几种原因造成的:一、域名可能配置了域名转发服务。无论何时有人访问域名,比如.com、.top等,都会自动重定向到另一个指定的URL,这通常是在域名注册商设...

取消回复欢迎 发表评论: