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

【JVM类加载】系统自带的类加载器如何加载如何自定义类加载器

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

系统自带的类加载器如何加载(重要)

  • 内建于JVM中的启动类加载器,会加载java.lang.classLoader以及其他平台的Java平台类,当JVM启动时,一块特殊的机器码会运行,他会加载扩展类加载器与系统类加载器,这块特殊的机器码叫做启动类加载器(Bootstrap)
  • 启动类加载器并不是java类(C++编写),而其他的加载器则都是Java类,启动类加载器是特定于平台的机器指令,它负责开启整个加载过程。
  • 所有类加载器(除了启动类加载器)都被实现为java类。不过,总归要有一个组件来加载第一个Java类加载器,从而让整个加载过程能够顺利进行下去,加载第一个纯java类加载器就是启动类的职责。
  • 启动类加载器还会负责加载供JRE正常运行所需要的基本组件,这包括java.util与java.lang包中的类等等

线程上下文类加载器的一般使用模式(获取->使用->还原)


    线程上下文类加载器的一般使用模式(获取->使用->还原)
    //获取
        ClassLoader classLoader = Thread.currentThread.getContextClassLoader();
    try{
        //设置要使用的类加载器
        //如cls就是启动类加载器就可以加载到实现类了 
        //springboot 加载MATE-INF/下spring.factories 就是采用的线程类加载器
        Thread.currentThread.setContextClassLoader(cls);
        //使用
        MyMethod();
    }finally{
        //如果不还原使用的就是设置的类加载器
        //还原
        Thread.currentThread.setContextClassLoader(classLoader);
    }

    

MyMethod里面则调用了Thread.currentThread().getContextClassLoader(),获取当前线程的上下文类加载器做某些事情。如果一个类由类加载器A加载,那么这个类的依赖类也是由相同的类加载器加载的(如果该依赖类之前没有被加载过的话)如: 启动类加载器扫描不到系统类加载器的内容 上下文类加载器就是为了解决这个问题

ContextClassLoader的作用就是为了破坏Java的类加载委托机制(如上)

当高层提供了统一的接口让低层去实现,同时又要在高层加载(或实例化)底层的类时,就必须要通过线程上下文类加载器来帮助高层的ClassLoader找到并加载该类


我们可以直接使用getClassLoader获取系统类加载器去加载对应的类为什么还要使用上下文类加载器?

  1. 方便,执行的任何代码都在线程中 我们可以随时取出来对应的上下文类加载器使用
  2. 解决高层加载底层类问题
  3. 有特定的情况,当前的线程类加载器 不一定是系统类加载器 此时家不能加载classpath下的.class文件

自定义系统类加载器


/*
    在运行期,一个java类是由该类的完全限定名(binary name,二进制名) 和用于加载该类的定义类加载器(defining loader)所共同决定的。
    如果同样名字(即相同的完全限定名)的类是由两个不同的加载器所加载,那么这些类就是不同的,即便.class文件的字节码完全一样,并且从
    相同的位置加载亦如此。
 */

/*
    在Oracle的Hotspot实现中,系统属性sun.boot.class.path如果修改错了,则运行会出差,提示如下错误信息:

    Error occurred during initialization of VM
    java/lang/NOClassDefFoundError: java/lang/Object
 */

public class MyTest23 {
    public static void main(String[] args) {
        System.out.println(System.getProperty("sun.boot.class.path"));//根类加载器
        System.out.println(System.getProperty("java.ext.dirs"));//扩展类加载器
        System.out.println(System.getProperty("java.class.path"));//应用类加载器  当在编译之后的.class文件会被放到classes下 所以会被该类加载器加载

        /*

            内建于JVM中的启动类加载器,会加载java.lang.classLoader以及其他平台的Java平台类,
            当JVM启动时,一块特殊的机器码会运行,他会加载扩展类加载器与系统类加载器,
            这快特殊的机器码焦作启动类加载器(Bootstrap).

            启动类加载器并不是java类,而其他的加载器则都是Java类,
            启动类加载器是特定于平台的机器指令,它负责开启整个加载过程。

            所有类加载器(除了启动类加载器)都被实现为java类。不过,总归要有一个组件来加载第一个Java类加载器,从而让整个加载过程能够顺利进行下去,加载第一个纯java类加载器就是启动类
            的职责。

            启动类加载器还会负责加载供JRE正常运行所需要的基本组件,这包括java.util与java.lang包中的类等等

         */

        System.out.println(ClassLoader.class.getClassLoader());//null 内建于JVM中的启动类加载器,会加载java.lang.classLoader以及其他平台的Java平台类,

        //扩展类加载器与系统类加载器也是由启动类加载器加载的
        System.out.println(Launcher.class.getClassLoader());//null 因为APPClassLoader和ExtClassLoader都是该类的静态内部类 二类加载器加载时其加载类的其他内容都是由该类加载器加载

        System.out.println(System.getProperty("java.system.class.loader"));//文件指定路径

        System.out.println(MyTest23.class.getClassLoader());

        System.out.println(Test16.class.getClassLoader());
        
		System.out.println(ClassLoader.getSystemClassLoader());//etSystemClassLoader静态方法 类名.获取系统类加载器
    }
}

在ClassLoader的doc文档中


  * <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”定义首次调用此方法时(如果java.system.class.loader指定的路径为null未定义时 会去使用AppClassLoader作为系统类加载器,当定义了系统属性时系统会让默认的AppClassLoader这个类加载器去加载即将成为系统类加载这个自定义类加载器),该属性的值(方法名)将作为系统类加载器的名字。类会使用默认的系统类加载器装入的必须定义一个公共构造函数,该构造函数只接受一个参数类型类加载器,用作委托父类(ClassLoader的一个成员变量每个子类都有)然后使用默认系统类加载器作为这个构造函数参数。此时自定义的类加载器将成为系统类装入器。

当我们直接run时


null
null
null 此时java.system.class.loader 并没有指定自定义路径下的类为系统加载类
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$AppClassLoader@18b4aac2

根据文档修改默认的系统类加载器时必须在自定义的类加载器中编写一个构造函数该构造函数只接受一个参数类型类装入器,用作委托父类。在Test16中加入对应的构造函数 重新编译

//新加一个构造方法
    public Test16(ClassLoader parent) {
        super(parent);
    }

在控制台输入 将java.system.class.loader 指定的类加载器设置为com.example.demo.com.jvm.Test16 并加载com.example.demo.com.jvm.MyTest23

java -Djava.system.class.loader=com.example.demo.com.jvm.Test16 com.example.demo.com.jvm.MyTest23
null
null
com.example.demo.com.jvm.Test16
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$AppClassLoader@18b4aac2 //此时AppClassLoader是自定义系统类加载器的父类 父类可以加载 就直接输出
com.example.demo.com.jvm.Test16@7852e922 //此时系统类加载器为我们自定义的类加载器

getSystemClassLoader()源码分析


问题1,为什么自带的类加载器加载指定位置?
问题1,为什么系统类加载器是扩展类加载器的子类?

doc文档


 Returns the system class loader for delegation.  This is the default
     * delegation parent for new <tt>ClassLoader</tt> instances, and is
     * typically the class loader used to start the application.
     *
     * <p> 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.
     *
  
  • 返回用于委托的系统类装入器。他是新的(自定义)类加载器的默认委托双亲(父类),并且是通常用于启动应用程序的类加载器。(main方法)
  • 此方法首先在程序运行时启动时很早被调用,此时创建并设置系统类加载器;并设置为:将调用该方法线程的上下文类加载器。
  • 默认的系统类加载器是依赖于实现的这个类的实例。如果系统属性java.system.class.loader”定义首次调用此方法时(如果java.system.class.loader指定的路径为null未定义时
  • 会去使用AppClassLoader作为系统类加载器,当定义了系统属性时系统会让默认的AppClassLoader这个类加载器去加载即将成为系统类加载这个自定义类加载器),该属性的值(方法名)将作为系统类加载器的名字。类会使用默认的系统类加载器装入的必须定义一个公共构造函数,该构造函数只接受一个参数类型类加载器,用作委托父类(ClassLoader的一个成员变量每个子类都有)然后使用默认系统类加载器作为这个构造函数参数。此时自定义的类加载器将成为系统类装入器。

ClassLoader类中


  //获取系统类加载器
  @CallerSensitive
    public static ClassLoader getSystemClassLoader() {
    	//初始化系统类加载器
        initSystemClassLoader();
        if (scl == null) {
            return null;
        }
        //获取安全管理器
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkClassLoaderPermission(scl, Reflection.getCallerClass());
        }
        return scl;
    }


   //初始化系统类加载器
   private static synchronized void initSystemClassLoader() {
   		//如果系统类加载器没有被设置
        if (!sclSet) {
        	//系统类加载器没有没有被设置又不为空 矛盾 就抛异常
            if (scl != null)
                throw new IllegalStateException("recursive invocation");
             //Launcher.getLauncher 获取该实例(解析在下)
            sun.misc.Launcher l = sun.misc.Launcher.getLauncher();
            if (l != null) {
            	//创建成功
                Throwable oops = null;
                //把Launcher 中的系统类加载器付给当前类的类加载器
                scl = l.getClassLoader();
                try {
                //获取类加载器对象(有可能是appClassLoader或者自定义类加载器)
                    scl = AccessController.doPrivileged(
                        new SystemClassLoaderAction(scl));//传入系统类加载器作为父加载器
                } catch (PrivilegedActionException pae) {
                    oops = pae.getCause();
                    if (oops instanceof InvocationTargetException) {
                        oops = oops.getCause();
                    }
                }
                if (oops != null) {
                    if (oops instanceof Error) {
                        throw (Error) oops;
                    } else {
                        // wrap the exception
                        throw new Error(oops);
                    }
                }
            }
            //此时系统类加载器设置完成
            sclSet = true;
        }
    }

参数


	//委托双亲 
   private final ClassLoader parent;
   // 系统类加载器
    // @GuardedBy("ClassLoader.class")
    private static ClassLoader scl;

    // 如果设置了系统类加载器的话 sclSet 为true
    // @GuardedBy("ClassLoader.class")
    private static boolean sclSet;

SystemClassLoaderAction类分析


class SystemClassLoaderAction
    implements PrivilegedExceptionAction<ClassLoader> {
    private ClassLoader parent;

    SystemClassLoaderAction(ClassLoader parent) {
        this.parent = parent;
    }

    public ClassLoader run() throws Exception {
    //获取java.system.class.loader 系统属性
        String cls = System.getProperty("java.system.class.loader");
        if (cls == null) {
        //如果改系统属性没被设置返回 appClassLoader
            return parent;
        }
		//如果自定义了系统类加载器
		//获取cls名的Class参数为(ClassLoader.class)的构造函数 
		//Class.forName 加载cls对应的二进制名 并使用父类(parent)初始化(true)
        Constructor<?> ctor = Class.forName(cls, true, parent)
            .getDeclaredConstructor(new Class<?>[] { ClassLoader.class });
        //将父类加载器即系统类加载器传给了自定义类加载器
        //所以当自定义类加载器在设置完时是由appClassLoader去加载的
        //获取自定义的ClassLoader类
        ClassLoader sys = (ClassLoader) ctor.newInstance(
        //为什么自定义类加载器必须要编写一个接收ClassLoader构造方法定义个父类参数的原因
            new Object[] { parent });
            
        Thread.currentThread().setContextClassLoader(sys);
        //返回自定义类加载器
        return sys;
    }
}

Launcher

getLauncher内容讲解

进入Launcher的无参构造方法
上下文类加载器

 public Launcher() {
 		//创建扩展类加载器
        Launcher.ExtClassLoader var1;//定义个扩展类加载器
        try {
            //创建扩展类加载器实例
            var1 = Launcher.ExtClassLoader.getExtClassLoader();
        } catch (IOException var10) {
            throw new InternalError("Could not create extension class loader", var10);
        }

        try {
        	//创建应用类加载器实例,并将扩展类加载器作为父类 并将其作为Launcher的一个成员变量
        	//为什么不把扩展类加载器也作为成员变量 没必要 因为 应用类加载器作为其子类可以获取到
            this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
        } catch (IOException var9) {
            throw new InternalError("Could not create application class loader", var9);
        }
		//将系统类加载器设置为当前的执行线程设置一个上下文类加载器(*重要*)
        Thread.currentThread().setContextClassLoader(this.loader);
        //下面安全管理器内容
        String var2 = System.getProperty("java.security.manager");
        if (var2 != null) {
            SecurityManager var3 = null;
            if (!"".equals(var2) && !"default".equals(var2)) {
                try {
                    var3 = (SecurityManager)this.loader.loadClass(var2).newInstance();
                } catch (IllegalAccessException var5) {
                    ;
                } catch (InstantiationException var6) {
                    ;
                } catch (ClassNotFoundException var7) {
                    ;
                } catch (ClassCastException var8) {
                    ;
                }
            } else {
                var3 = new SecurityManager();
            }

            if (var3 == null) {
                throw new InternalError("Could not create SecurityManager: " + var2);
            }

            System.setSecurityManager(var3);
        }

    }

getExtClassLoader() 获取已经加载的扩展类加载器

 static class ExtClassLoader extends URLClassLoader {
 //创建的扩展类加载器限制java.ext.dirs地区class文件
        public static Launcher.ExtClassLoader getExtClassLoader() throws IOException {
        	//获取指定文件数组  System.getProperty("java.ext.dirs");
            final File[] var0 = getExtDirs();

            try {
                return (Launcher.ExtClassLoader)AccessController.doPrivileged(
                //返回时判断是否有权限去执行该操作
                new PrivilegedExceptionAction<Launcher.ExtClassLoader>() {
                    public Launcher.ExtClassLoader run() throws IOException {
                        //获取路径长度
                        int var1 = var0.length;
						//遍历每一个路径获取对应路径内容
                        for(int var2 = 0; var2 < var1; ++var2) {
                            MetaIndex.registerDirectory(var0[var2]);
                        }
						//返回扩展类对象给调用端
                        return new Launcher.ExtClassLoader(var0);
                    }
                });
            } catch (PrivilegedActionException var2) {
                throw (IOException)var2.getException();
            }
        }



从java.class.path下加载指定内容
getAppClassLoader(final ClassLoader var0) 获取系统类加载器
为什么扩展类加载器是系统类加载器父类? 解答如下

  static class AppClassLoader extends URLClassLoader {
        final URLClassPath ucp = SharedSecrets.getJavaNetAccess().getURLClassPath(this);

        public static ClassLoader getAppClassLoader(final ClassLoader var0) throws IOException {
        	//读取指定路径下的文件
            final String var1 = System.getProperty("java.class.path");
            final File[] var2 = var1 == null ? new File[0] : Launcher.getClassPath(var1);
            return (ClassLoader)AccessController.doPrivileged(new PrivilegedAction<Launcher.AppClassLoader>() {
                public Launcher.AppClassLoader run() {
                    URL[] var1x = var1 == null ? new URL[0] : Launcher.pathToURLs(var2);
                   // 生成系统类加载器 传对应的数据和扩展类加载器
                    return new Launcher.AppClassLoader(var1x, var0);
                }
            });
        }

        AppClassLoader(URL[] var1, ClassLoader var2) {
        	//调用父类完成双亲委托 将扩展类加载器作为父类
            super(var1, var2, Launcher.factory);
            this.ucp.initLookupCache(this);
        }
  static class AppClassLoader extends URLClassLoader {
        final URLClassPath ucp = SharedSecrets.getJavaNetAccess().getURLClassPath(this);

        public static ClassLoader getAppClassLoader(final ClassLoader var0) throws IOException {
        	//读取指定路径下的文件
            final String var1 = System.getProperty("java.class.path");
            final File[] var2 = var1 == null ? new File[0] : Launcher.getClassPath(var1);
            return (ClassLoader)AccessController.doPrivileged(new PrivilegedAction<Launcher.AppClassLoader>() {
                public Launcher.AppClassLoader run() {
                    URL[] var1x = var1 == null ? new URL[0] : Launcher.pathToURLs(var2);
                   // 生成系统类加载器 传对应的数据和扩展类加载器
                    return new Launcher.AppClassLoader(var1x, var0);
                }
            });
        }

        AppClassLoader(URL[] var1, ClassLoader var2) {
        	//调用父类完成双亲委托 将扩展类加载器作为父类
            super(var1, var2, Launcher.factory);
            this.ucp.initLookupCache(this);
        }

分析Class.forName


public static Class<?> forName(String name, boolean initialize,
ClassLoader loader)

  • 返回给定名字(String name)的类或接口的Class对象。用给定的( ClassLoader loader)的类加载器去加载这个方法试图定位、加载和链接类或接口。指定的类加载器用于加载类或接口。如果参数loader为null,类通过bootstrap类加载器加载。这个类类只有在initialize参数是true,尚未被初始化的情况下才会被初始化
  • 如果{String name}表示原始类型或void,则尝试将被用来在其未命名的包中寻找一个用户定义的类 name是{String name}。因此,这个方法不能用于获取表示原生类型或void的任何 Class对象。
  • 如果{String name}表示一个数组类,则组件类型(jvm)为加载了数组类,但没有初始化。例如,在一个实例方法中的表达式: Class.forName (" Foo ")

Class<?> caller = Reflection.getCallerClass();获取调用者的类加载器


//该会将调用者的类加载器去加载className的类
 public static Class<?> forName(String className)
                throws ClassNotFoundException {
        //获取调用者的Class对象 单参默认使用调用者的类加载器
        Class<?> caller = Reflection.getCallerClass();
        //返回Class 使用C++实现
        return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
    }

相当于:Class.forName(“foo”,true, this.getClass.getClassLoader()) 这个方法是使用 自定义的类加载器

无指定加载器使用当前加载这个类的加载器去加载这个类,如果指定了类加载器就会使用指定的类加载器去加载

注意此方法抛出与加载、链接或初始化相关的错误按照的第12.2、12.3和12.4节的规定进行初始化Java语言规范。注意,这个方法不会检查被请求的类是否存在调用者可以访问*。

String name:指定类完整的限定名

boolean initialize:是否初始化

ClassLoader loader:用于去加载这个类的类加载器

源代码

  public static Class<?> forName(String name, boolean initialize,
                                   ClassLoader loader)
        throws ClassNotFoundException
    {
        Class<?> caller = null;
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            // Reflective call to get caller class is only needed if a security manager
            // is present.  Avoid the overhead of making this call otherwise.
            //获取调用forname方法的类的Class对象
            caller = Reflection.getCallerClass();
            if (sun.misc.VM.isSystemDomainLoader(loader)) {
            	//获取调用forname方法的类的Class对象的类加载器
                ClassLoader ccl = ClassLoader.getClassLoader(caller);
                if (!sun.misc.VM.isSystemDomainLoader(ccl)) {
                	//安全检查
                    sm.checkPermission(
                        SecurityConstants.GET_CLASSLOADER_PERMISSION);
                }
            }
        }
        //c++代码
        return forName0(name, initialize, loader, caller);
    }

相关推荐

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,这通常是在域名注册商设...

取消回复欢迎 发表评论: