Java动态代理

一、概述

1. 什么是代理

我们大家都知道微商代理,简单地说就是代替厂家卖商品,厂家“委托”代理为其销售商品。关于微商代理,首先我们从他们那里买东西时通常不知道背后的厂家究竟是谁,也就是说,“委托者”对我们来说是不可见的;其次,微商代理主要以朋友圈的人为目标客户,这就相当于为厂家做了一次对客户群体的“过滤”。我们把微商代理和厂家进一步抽象,前者可抽象为代理类,后者可抽象为委托类(被代理类)。通过使用代理,通常有两个优点,并且能够分别与我们提到的微商代理的两个特点对应起来:

优点一:可以隐藏委托类的实现;

优点二:可以实现客户与委托类间的解耦,在不修改委托类代码的情况下能够做一些额外的处理。

2. 静态代理

若代理类在程序运行前就已经存在,那么这种代理方式被成为 静态代理 ,这种情况下的代理类通常都是我们在Java代码中定义的。 通常情况下, 静态代理中的代理类和委托类会实现同一接口或是派生自相同的父类。 下面我们用Vendor类代表生产厂家,BusinessAgent类代表微商代理,来介绍下静态代理的简单实现,委托类和代理类都实现了Sell接口,Sell接口的定义如下:

/*** 委托类和代理类都实现了Sell接口*/
public interface Sell { void sell(); void ad();
}
复制代码

Vendor类的定义如下:

/*** 生产厂家*/
public class Vendor implements Sell { public void sell() { System.out.println("In sell method"); } public void ad() { System,out.println("ad method");}
}
复制代码

代理类BusinessAgent的定义如下:

/*** 代理类*/
public class BusinessAgent implements Sell {private Sell vendor;public BusinessAgent(Sell vendor){this.vendor = vendor;}public void sell() { vendor.sell();} public void ad() {vendor.ad();}
}
复制代码

从BusinessAgent类的定义我们可以了解到,静态代理可以通过聚合来实现,让代理类持有一个委托类的引用即可。

下面我们考虑一下这个需求:给Vendor类增加一个过滤功能,只卖货给大学生。通过静态代理,我们无需修改Vendor类的代码就可以实现,只需在BusinessAgent类中的sell方法中添加一个判断即可如下所示:

/*** 代理类*/
public class BusinessAgent(){ implements Sell {private Sell vendor;public BusinessAgent(Sell vendor){this.vendor = vendor;}public void sell() {if (isCollegeStudent()) {vendor.sell();}} public void ad() {vendor.ad();}
}
复制代码

这对应着我们上面提到的使用代理的第二个优点:可以实现客户与委托类间的解耦,在不修改委托类代码的情况下能够做一些额外的处理。静态代理的局限在于运行前必须编写好代理类,下面我们重点来介绍下运行时生成代理类的动态代理方式。

二、动态代理

1. 什么是动态代理

代理类在程序运行时创建的代理方式被成为 动态代理。 也就是说,这种情况下,代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的。相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类的函数。 这么说比较抽象,下面我们结合一个实例来介绍一下动态代理的这个优势是怎么体现的。

现在,假设我们要实现这样一个需求:在执行委托类中的方法之前输出“before”,在执行完毕后输出“after”。我们还是以上面例子中的Vendor类作为委托类,BusinessAgent类作为代理类来进行介绍。首先我们来使用静态代理来实现这一需求,相关代码如下:

public class BusinessAgent implements Sell {private Vendor mVendor; public BusinessAgent(Vendor vendor) {this.mVendor = vendor; } public void sell() {System.out.println("before"); mVendor.sell(); System.out.println("after"); } public void ad() {System.out.println("before"); mVendor.ad(); System.out.println("after"); }
}
复制代码

从以上代码中我们可以了解到,通过静态代理实现我们的需求需要我们在每个方法中都添加相应的逻辑,这里只存在两个方法所以工作量还不算大,假如Sell接口中包含上百个方法呢?这时候使用静态代理就会编写许多冗余代码。通过使用动态代理,我们可以做一个“统一指示”,从而对所有代理类的方法进行统一处理,而不用逐一修改每个方法。下面我们来具体介绍下如何使用动态代理方式实现我们的需求。

2. 使用动态代理

2.1 InvocationHandler接口

在使用动态代理时,我们需要定义一个位于代理类与委托类之间的中介类,这个中介类被要求实现InvocationHandler接口,这个接口的定义如下:

/*** 调用处理程序*/
public interface InvocationHandler { Object invoke(Object proxy, Method method, Object[] args);
}
复制代码

从InvocationHandler这个名称我们就可以知道,实现了这个接口的中介类用做“调用处理器”。当我们调用代理类对象的方法时,这个“调用”会转送到invoke方法中,代理类对象作为proxy参数传入,参数method标识了我们具体调用的是代理类的哪个方法,args为这个方法的参数。这样一来,我们对代理类中的所有方法的调用都会变为对invoke的调用,这样我们可以在invoke方法中添加统一的处理逻辑(也可以根据method参数对不同的代理类方法做不同的处理)。因此我们只需在中介类的invoke方法实现中输出“before”,然后调用委托类的invoke方法,再输出“after”。下面我们来一步一步具体实现它。

2.2 委托类的定义

动态代理方式下,要求委托类必须实现某个接口,这里我们实现的是Sell接口。委托类Vendor类的定义如下:

public class Vendor implements Sell { public void sell() { System.out.println("In sell method"); }public void ad() {System,out.println("ad method");}
}
复制代码

2.3中介类

上面我们提到过,中介类必须实现InvocationHandler接口,作为调用处理器”拦截“对代理类方法的调用。中介类的定义如下:

public class DynamicProxy implements InvocationHandler { //obj为委托类对象; private Object obj; public DynamicProxy(Object obj) {this.obj = obj;} @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("before"); Object result = method.invoke(obj, args); System.out.println("after"); return result; }
}
复制代码

从以上代码中我们可以看到,中介类持有一个委托类对象引用,在invoke方法中调用了委托类对象的相应方法,看到这里是不是觉得似曾相识?

通过聚合方式持有委托类对象引用,把外部对invoke的调用最终都转为对委托类对象的调用。这不就是我们上面介绍的静态代理的一种实现方式吗?

实际上,中介类与委托类构成了静态代理关系,在这个关系中,中介类是代理类,委托类就是委托类;

代理类与中介类也构成一个静态代理关系,在这个关系中,中介类是委托类,代理类是代理类。

也就是说,动态代理关系由两组静态代理关系组成,这就是动态代理的原理。下面我们来介绍一下如何”指示“以动态生成代理类。

2.4动态生成代理类

动态生成代理类的相关代码如下:

public class Main { public static void main(String[] args) {//创建中介类实例 DynamicProxy inter = new DynamicProxy(new Vendor()); //加上这句将会产生一个$Proxy0.class文件,这个文件即为动态生成的代理类文件System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true"); //获取代理类实例sell Sell sell = (Sell)(Proxy.newProxyInstance(Sell.class.getClassLoader(), new Class[] {Sell.class}, inter)); //通过代理类对象调用代理类方法,实际上会转到invoke方法调用 sell.sell(); sell.ad(); }
}
复制代码

在以上代码中,我们调用Proxy类的newProxyInstance方法来获取一个代理类实例。这个代理类实现了我们指定的接口并且会把方法调用分发到指定的调用处理器。这个方法的声明如下:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
复制代码

方法的三个参数含义分别如下:

loader:定义了代理类的ClassLoder; interfaces:代理类实现的接口列表 h:调用处理器,也就是我们上面定义的实现了InvocationHandler接口的类实例

我们运行一下,看看我们的动态代理是否能正常工作。我这里运行后的输出为

说明我们的动态代理确实奏效了。

上面我们已经简单提到过动态代理的原理,这里再简单的总结下:首先通过newProxyInstance方法获取代理类实例,而后我们便可以通过这个代理类实例调用代理类的方法,对代理类的方法的调用实际上都会调用中介类(调用处理器)的invoke方法,在invoke方法中我们调用委托类的相应方法,并且可以添加自己的处理逻辑。

三、代理模式

这个应该是设计模式中最简单的一个了,类图

代理模式最大的特点就是代理类和实际业务类实现同一个接口(或继承同一父类),代理对象持有一个实际对象的引用,外部调用时操作的是代理对象,而在代理对象的内部实现中又会去调用实际对象的操作

Java动态代理其实内部也是通过Java反射机制来实现的,即已知的一个对象,然后在运行时动态调用其方法,这样在调用前后作一些相应的处理,这样说的比较笼统,举个简单的例子

比如我们在应用中有这样一个需求,在对某个类的一个方法的调用前和调用后都要做一下日志操作,

一个普通的接口

public interface AppService {  public boolean createApp(String name);
}
复制代码

该接口的默认实现类

public class AppServiceImpl implements AppService {  public boolean createApp(String name) {  System.out.println("App["+name+"] has been created.");  return true;  }
}
复制代码

日志处理器(实质充当了中介类)

/*** 注意需实现Handler接口  */
public class LoggerInterceptor implements InvocationHandler {private Object target;//目标对象的引用,这里设计成Object类型,更具通用性  public LoggerInterceptor(Object target){  this.target = target;  }public Object invoke(Object proxy, Method method, Object[] arg)  throws Throwable {  System.out.println("Entered "+target.getClass().getName()+"-"+method.getName()+",with arguments{"+arg[0]+"}");  Object result = method.invoke(target, arg);//调用目标对象的方法  System.out.println("Before return:"+result);  return result;  }
}
复制代码

外部调用

public class Main {  public static void main(String[] args) {  AppService target = new AppServiceImpl();//生成目标对象  //接下来创建代理对象  AppService proxy = (AppService) Proxy.newProxyInstance(  target.getClass().getClassLoader(),  target.getClass().getInterfaces(), new LoggerInterceptor(target));  proxy.createApp("Kevin Test");  }
}  

一、概述

1. 什么是代理

我们大家都知道微商代理,简单地说就是代替厂家卖商品,厂家“委托”代理为其销售商品。关于微商代理,首先我们从他们那里买东西时通常不知道背后的厂家究竟是谁,也就是说,“委托者”对我们来说是不可见的;其次,微商代理主要以朋友圈的人为目标客户,这就相当于为厂家做了一次对客户群体的“过滤”。我们把微商代理和厂家进一步抽象,前者可抽象为代理类,后者可抽象为委托类(被代理类)。通过使用代理,通常有两个优点,并且能够分别与我们提到的微商代理的两个特点对应起来:

优点一:可以隐藏委托类的实现;

优点二:可以实现客户与委托类间的解耦,在不修改委托类代码的情况下能够做一些额外的处理。

2. 静态代理

若代理类在程序运行前就已经存在,那么这种代理方式被成为 静态代理 ,这种情况下的代理类通常都是我们在Java代码中定义的。 通常情况下, 静态代理中的代理类和委托类会实现同一接口或是派生自相同的父类。 下面我们用Vendor类代表生产厂家,BusinessAgent类代表微商代理,来介绍下静态代理的简单实现,委托类和代理类都实现了Sell接口,Sell接口的定义如下:

/*** 委托类和代理类都实现了Sell接口*/
public interface Sell { void sell(); void ad();
}
复制代码

Vendor类的定义如下:

/*** 生产厂家*/
public class Vendor implements Sell { public void sell() { System.out.println("In sell method"); } public void ad() { System,out.println("ad method");}
}
复制代码

代理类BusinessAgent的定义如下:

/*** 代理类*/
public class BusinessAgent implements Sell {private Sell vendor;public BusinessAgent(Sell vendor){this.vendor = vendor;}public void sell() { vendor.sell();} public void ad() {vendor.ad();}
}
复制代码

从BusinessAgent类的定义我们可以了解到,静态代理可以通过聚合来实现,让代理类持有一个委托类的引用即可。

下面我们考虑一下这个需求:给Vendor类增加一个过滤功能,只卖货给大学生。通过静态代理,我们无需修改Vendor类的代码就可以实现,只需在BusinessAgent类中的sell方法中添加一个判断即可如下所示:

/*** 代理类*/
public class BusinessAgent(){ implements Sell {private Sell vendor;public BusinessAgent(Sell vendor){this.vendor = vendor;}public void sell() {if (isCollegeStudent()) {vendor.sell();}} public void ad() {vendor.ad();}
}
复制代码

这对应着我们上面提到的使用代理的第二个优点:可以实现客户与委托类间的解耦,在不修改委托类代码的情况下能够做一些额外的处理。静态代理的局限在于运行前必须编写好代理类,下面我们重点来介绍下运行时生成代理类的动态代理方式。

二、动态代理

1. 什么是动态代理

代理类在程序运行时创建的代理方式被成为 动态代理。 也就是说,这种情况下,代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的。相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类的函数。 这么说比较抽象,下面我们结合一个实例来介绍一下动态代理的这个优势是怎么体现的。

现在,假设我们要实现这样一个需求:在执行委托类中的方法之前输出“before”,在执行完毕后输出“after”。我们还是以上面例子中的Vendor类作为委托类,BusinessAgent类作为代理类来进行介绍。首先我们来使用静态代理来实现这一需求,相关代码如下:

public class BusinessAgent implements Sell {private Vendor mVendor; public BusinessAgent(Vendor vendor) {this.mVendor = vendor; } public void sell() {System.out.println("before"); mVendor.sell(); System.out.println("after"); } public void ad() {System.out.println("before"); mVendor.ad(); System.out.println("after"); }
}
复制代码

从以上代码中我们可以了解到,通过静态代理实现我们的需求需要我们在每个方法中都添加相应的逻辑,这里只存在两个方法所以工作量还不算大,假如Sell接口中包含上百个方法呢?这时候使用静态代理就会编写许多冗余代码。通过使用动态代理,我们可以做一个“统一指示”,从而对所有代理类的方法进行统一处理,而不用逐一修改每个方法。下面我们来具体介绍下如何使用动态代理方式实现我们的需求。

2. 使用动态代理

2.1 InvocationHandler接口

在使用动态代理时,我们需要定义一个位于代理类与委托类之间的中介类,这个中介类被要求实现InvocationHandler接口,这个接口的定义如下:

/*** 调用处理程序*/
public interface InvocationHandler { Object invoke(Object proxy, Method method, Object[] args);
}
复制代码

从InvocationHandler这个名称我们就可以知道,实现了这个接口的中介类用做“调用处理器”。当我们调用代理类对象的方法时,这个“调用”会转送到invoke方法中,代理类对象作为proxy参数传入,参数method标识了我们具体调用的是代理类的哪个方法,args为这个方法的参数。这样一来,我们对代理类中的所有方法的调用都会变为对invoke的调用,这样我们可以在invoke方法中添加统一的处理逻辑(也可以根据method参数对不同的代理类方法做不同的处理)。因此我们只需在中介类的invoke方法实现中输出“before”,然后调用委托类的invoke方法,再输出“after”。下面我们来一步一步具体实现它。

2.2 委托类的定义

动态代理方式下,要求委托类必须实现某个接口,这里我们实现的是Sell接口。委托类Vendor类的定义如下:

public class Vendor implements Sell { public void sell() { System.out.println("In sell method"); }public void ad() {System,out.println("ad method");}
}
复制代码

2.3中介类

上面我们提到过,中介类必须实现InvocationHandler接口,作为调用处理器”拦截“对代理类方法的调用。中介类的定义如下:

public class DynamicProxy implements InvocationHandler { //obj为委托类对象; private Object obj; public DynamicProxy(Object obj) {this.obj = obj;} @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("before"); Object result = method.invoke(obj, args); System.out.println("after"); return result; }
}
复制代码

从以上代码中我们可以看到,中介类持有一个委托类对象引用,在invoke方法中调用了委托类对象的相应方法,看到这里是不是觉得似曾相识?

通过聚合方式持有委托类对象引用,把外部对invoke的调用最终都转为对委托类对象的调用。这不就是我们上面介绍的静态代理的一种实现方式吗?

实际上,中介类与委托类构成了静态代理关系,在这个关系中,中介类是代理类,委托类就是委托类;

代理类与中介类也构成一个静态代理关系,在这个关系中,中介类是委托类,代理类是代理类。

也就是说,动态代理关系由两组静态代理关系组成,这就是动态代理的原理。下面我们来介绍一下如何”指示“以动态生成代理类。

2.4动态生成代理类

动态生成代理类的相关代码如下:

public class Main { public static void main(String[] args) {//创建中介类实例 DynamicProxy inter = new DynamicProxy(new Vendor()); //加上这句将会产生一个$Proxy0.class文件,这个文件即为动态生成的代理类文件System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true"); //获取代理类实例sell Sell sell = (Sell)(Proxy.newProxyInstance(Sell.class.getClassLoader(), new Class[] {Sell.class}, inter)); //通过代理类对象调用代理类方法,实际上会转到invoke方法调用 sell.sell(); sell.ad(); }
}
复制代码

在以上代码中,我们调用Proxy类的newProxyInstance方法来获取一个代理类实例。这个代理类实现了我们指定的接口并且会把方法调用分发到指定的调用处理器。这个方法的声明如下:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
复制代码

方法的三个参数含义分别如下:

loader:定义了代理类的ClassLoder; interfaces:代理类实现的接口列表 h:调用处理器,也就是我们上面定义的实现了InvocationHandler接口的类实例

我们运行一下,看看我们的动态代理是否能正常工作。我这里运行后的输出为

说明我们的动态代理确实奏效了。

上面我们已经简单提到过动态代理的原理,这里再简单的总结下:首先通过newProxyInstance方法获取代理类实例,而后我们便可以通过这个代理类实例调用代理类的方法,对代理类的方法的调用实际上都会调用中介类(调用处理器)的invoke方法,在invoke方法中我们调用委托类的相应方法,并且可以添加自己的处理逻辑。

三、代理模式

这个应该是设计模式中最简单的一个了,类图

代理模式最大的特点就是代理类和实际业务类实现同一个接口(或继承同一父类),代理对象持有一个实际对象的引用,外部调用时操作的是代理对象,而在代理对象的内部实现中又会去调用实际对象的操作

Java动态代理其实内部也是通过Java反射机制来实现的,即已知的一个对象,然后在运行时动态调用其方法,这样在调用前后作一些相应的处理,这样说的比较笼统,举个简单的例子

比如我们在应用中有这样一个需求,在对某个类的一个方法的调用前和调用后都要做一下日志操作,

一个普通的接口

public interface AppService {  public boolean createApp(String name);
}
复制代码

该接口的默认实现类

public class AppServiceImpl implements AppService {  public boolean createApp(String name) {  System.out.println("App["+name+"] has been created.");  return true;  }
}
复制代码

日志处理器(实质充当了中介类)

/*** 注意需实现Handler接口  */
public class LoggerInterceptor implements InvocationHandler {private Object target;//目标对象的引用,这里设计成Object类型,更具通用性  public LoggerInterceptor(Object target){  this.target = target;  }public Object invoke(Object proxy, Method method, Object[] arg)  throws Throwable {  System.out.println("Entered "+target.getClass().getName()+"-"+method.getName()+",with arguments{"+arg[0]+"}");  Object result = method.invoke(target, arg);//调用目标对象的方法  System.out.println("Before return:"+result);  return result;  }
}
复制代码

外部调用

public class Main {  public static void main(String[] args) {  AppService target = new AppServiceImpl();//生成目标对象  //接下来创建代理对象  AppService proxy = (AppService) Proxy.newProxyInstance(  target.getClass().getClassLoader(),  target.getClass().getInterfaces(), new LoggerInterceptor(target));  proxy.createApp("Kevin Test");  }
}  

什么是Java动态代理,如何实现一个动态代理例子相关推荐

  1. 用Java和Jquery实现了一个砸金蛋例子

    之前在网上找到了一个Jquery+PHP实现的砸金蛋的例子,正好公司也要我写一个类似的砸金蛋的功能,于是在网上找了找参考,用Jsp程序也弄了一个出来,首先是Html的效果,我打算每次在页面上只显示一个 ...

  2. 【Java 28岁了】一个有趣的例子,再推荐一些经典好书(文末惊喜福利)

    文章目录 1 写在前面 2 C语言与Java语言的互相调用 2.1 C语言调用Java语言 2.2 Java语言调用C语言 3 友情推荐 4 更多分享 1 写在前面 众所周知,C语言和Java语言是两 ...

  3. c语言2维动态数组,如何创建一个动态2维数组?

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 #include #include #include void **darray_new(int row,int col,int size_t){ voi ...

  4. Java JDK代理、CGLIB、AspectJ代理分析比较

    前言 什么是代理,在Design patterns In java这个本书中是这样描述的,简单的说就是为某个对象提供一个代理,以控制对这个对象的访问.在不修改源代码的基础上做方法增强,代理是一种设计模 ...

  5. Java使用Proxy和CGLib实现动态代理

    在java中对一个方法进行改造有几种方法可以实现,分别是最常用的继承(extends).动态代理和装饰者模式.继承是最简单也是用的最多的方法,但是还是有一些场景不适用,这个时候就可能会用到动态代理.使 ...

  6. java动态代理_Java中的动态代理一

    方法newProxyInstance介绍 这篇开始学Java中的动态代理部分,刚开始,我们不去了解什么是动态代理相关的概念,除了动态代理,还有静态代理.我们学习动态代理直接从学习一个方法开始,这个方法 ...

  7. Java中三种代理方式—— 静态代理与两种动态代理的实现机制

    个人博客请访问 http://www.x0100.top 1.代理模式 代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现 ...

  8. java Spring的AOP理解和动态代理

    Spring的AOP OOP面向对象,允许开发者定义纵向的关系,但并不适用于定义横向的关系,会导致大量代码的重复,而不利于各个模块的重用. AOP,一般称为面向切面,作为面向对象的一种补充,用于将那些 ...

  9. java类加载机制、反射、动态代理详解

    类的加载.连接和初始化(系统可能在第一次使用某个类时加载该类, 也可能采用预加载机制来加载某个类)动态代理实现 1.JVM和类 当调用 java 命令运行某个 Java 程序时, 该命令将会启动一个 ...

最新文章

  1. Ubuntu安装Anaconda
  2. 在校园里捉到一只小鸟,怎么办?
  3. NK.bin和NK.nb0学习
  4. 简述sqlite数据库的特点_SQLite
  5. 【MySQL】按平均成绩从高到低显示所有学生的所有课程的成绩以及平均成绩
  6. Java多线程--使用future进行异步编程
  7. Linux的实际操作:用户管理(查看用户和组的配置文件/etc/passwd /etc/group /etc/shadow)
  8. 当前监管面比较关注的企业征信模型
  9. 抓到的一次suse11 xen用libvirt连接的错误
  10. Android 音频均衡器,可通过拖动调节音频EQ
  11. LeaFlet学习之聚合效果
  12. Acrobat Pro DC 教程,如何将文件合并为 PDF?
  13. 第 11 章 日志管理 - 090 - ELK 完整部署和使用
  14. USB 协议分析(含基本协议和 USB 请求和设备枚举)
  15. oeasy教您玩转vim - 43 - # 替换模式
  16. Dbeaver Phoenix 各种报错
  17. ok6410 移植linux-3.9.7过程及问题总结
  18. spring-boot、jeecg-boot中解除shiro限制了上传视频文件,以及设置上传文件大小的限制
  19. 宏的录制以及在Visual Basic中显示代码
  20. java中构造方法的理解,super()与构造方法,无参,有参构造方法,this()与构造方法。

热门文章

  1. 新高考 计算机专业怎么选科目,@所有高中生!新高考怎么选科?计算机类专业该怎么选科?...
  2. storm风暴英雄 tempo_《风暴英雄》官方网站
  3. qml如何使用opengl渲染视频流?
  4. PFSI 开源啦,欢迎您来贡献您的代码
  5. 用OBS推流直播,设置与调教入坑
  6. nacos server默认的登录账号密码
  7. php早午晚问候语_php 输出上午好,下午好等问候语的方法教程
  8. 立方人物:孙永旺律师|理智与温度
  9. word设置替换并加着重号_word着重号怎么打-word加着重号的方法 - 河东软件园
  10. Ubuntu卸载火狐firefox浏览器