代理的基本概念
几个英文单词:
proxy [ˈprɒksi] n. 代理服务器;表明权;代理人,代替物;委托书;
invoke [ɪnˈvəʊk] vt. 乞灵,祈求;提出或授引…以支持或证实;召鬼;借助;
invocation [ˌɪnvəˈkeɪʃn] n. 祈祷;乞求;乞灵;乞求神助;
subject [ˈsʌbdʒɪkt] n. 主题,话题;学科,科目;[哲] 主观;
adj. 须服从…的;(在君主等)统治下的;
v. 提供,提出;使…隶属;
什么是代理
咱们你们都知道微商代理,简单地说就是代替厂家卖商品,厂家"委托"代理为其销售商品。关于微商代理,首先咱们从他们那里买东西时一般不知道背后的厂家到底是谁(不要钻牛角尖!),也就是说,"委托者"对咱们来讲是不可见的;其次,微商代理主要以朋友圈的人为目标客户,这就至关于为厂家作了一次对客户群体的"过滤"。
咱们把微商代理和厂家进一步抽象,前者可抽象为"代理类",后者可抽象为"委托类(被代理类)"。
经过使用代理,一般有两个优势:
- 能够隐藏委托类的实现
- 能够实现客户与委托类间的解耦,在不修改委托类代码的状况下可以作一些额外的处理。
代理模式是一种结构型设计模式,其目的就是
为其余对象提供一个代理以控制对某个对象的访问。代理类负责为委托类
预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。
其实方法直接调用就能够完成功能,为何还要加个代理呢?
缘由是
采用代理模式能够有效的将具体的实现与调用方进行解耦,经过面向接口进行编程彻底将具体的实现隐藏在内部。
更通俗的说,代理解决的问题是:当两个类须要通讯时,引入第三方代理类,将两个类的关系解耦,让咱们只了解代理类便可,并且代理的出现还可让咱们完成与另外一个类之间的关系的统一管理。可是一个前提,
代理类和委托类要实现相同的接口,由于代理真正调用的仍是委托类的方法。
按照代理的建立时期,能够分为两种:
- 静态代理:若代理类在程序运行前就已经存在,那么这种代理方式被成为静态代理 。静态代理一般是由程序员在Java代码中定义的, 且代理类和委托类会实现同一接口或是派生自相同的父类。
- 动态代理:在程序运行时运用反射机制动态建立而成。
静态代理
静态代理的通常实现过程:
- 首先建立一个接口
- 而后建立具体实现类来实现这个接口,具体实现类中须要将接口中定义的方法的业务逻辑功能实现
- 再建立一个代理类一样实现这个接口,代理类中接口的方法只要调用具体类中的对应方法便可
这样,咱们在须要使用接口中的某个方法时,直接调用代理类的方法便可,而具体的实现隐藏在了底层。
/**第一步:定义一个接口*/
interface Iuser {
void eat(String s);
}
/**第二步:建立具体实现类,即被代理类或委托类*/
class UserImpl implements Iuser {
@Override
public void eat(String s) {
System.out.println("我要吃" + s);
}
}
/**第三步:建立代理类*/
class UserProxy implements Iuser {
private Iuser user;//代理类一般只实现一个接口,由于代理类中持有的一般只是接口的引用,而不是某个委托类的直接引用
public UserProxy() {
this.user = new UserImpl();
}
public UserProxy(Iuser user) {
this.user = user;
}
@Override
public void eat(String s) {
System.out.println("静态代理前置内容");
user.eat(s);//利用的就是多态的特性,也是面向接口编程的一种典型的体现
System.out.println("静态代理后置内容");
}
}
/**静态代理访问演示*/
public class Test {
public static void main(String[] args) {
UserProxy proxy = new UserProxy();//或 UserProxy proxy = new UserProxy(new UserImpl())
proxy.eat("苹果");
}
}
代理类和被代理类
都须要实现某个功能接口,
代理类
里面持有被
代理类
的引用,
代理类
能够根据须要添加不一样的操做。
静态代理类的
优势:
客户端不须要知道实现类是什么、怎么作的,而只需知道代理类便可(不要钻牛角尖)。
静态代理类的缺点:
- 代理类和委托类实现了相同的接口,代理类经过委托类实现了相同的方法,可是由于代理类中接口的方法每每是没什么逻辑的,它一般只是调用了委托类的同名方法而已,因此这就出现了大量重复、冗余的代码。
- 若是接口中增长或修改了某个方法,除了全部委托类须要修改代码外,全部代理类也须要修改代码,增长了代码维护的复杂度。
- 代理对象只服务于一种类型的对象,即静态代理类只能为特定的接口服务,如想要为多个接口服务则须要创建多个代理类,这在大型系统中大大增长了复杂度。
动态代理
静态代理是在编译时就将接口、实现类、代理类一古脑儿所有手动完成
,但若是咱们须要不少的代理,每个都这么手动的去建立实属浪费时间,并且会有大量的重复代码,此时咱们就能够采用动态代理。
动态代理能够在程序运行期间根据须要动态的建立代理类及其实例,来完成具体的功能,主要用的是JAVA的反射机制。
也就是说,这
种状况下,代理类并非在Java代码中定义的,而是在运行时根据咱们在Java代码中的"指示"动态生成的。
/**第一步:定义一个接口*/
interface Iuser {
void eat(String s);
}
/**再定义一个接口*/
interface Irun {
String run(int length);
}
/**第二步:建立具体实现类,即被代理类或委托类*/
class UserImpl implements Iuser, Irun {
@Override
public void eat(String s) {
System.out.println("我要吃" + s);
}
@Override
public String run(int length) {
System.out.println("我跑了 " + length + " 米");
return "跑步很欢乐";
}
}
/**第三步:定义代理实例的【调用处理器】,这是一个位于代理类与委托类之间的【中介类】,它须要实现【InvocationHandler】接口*/
class UserHandler implements InvocationHandler {
private Object object;//调用处理器持有委托类的引用,但并不限定委托类必须是某一接口或某一类
public UserHandler(Object object) {//这里是用Object接收的,因此能够传递任何类型的对象
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object returnObj = method.invoke(object, args); //【核心点】经过反射执行某个类的某方法
System.out.println("【方法】" + method.getName() + "【参数】" + Arrays.toString(args) + "【返回值】" + returnObj);
return returnObj;
}
}
/**第四步:在使用时动态建立动态代理类*/
public class Test {
public static void main(String[] args) {
test1();
System.out.println("----------------------2---------------------");
test2();
System.out.println("----------------------3---------------------");
test3();
}
private static void test1() {//彻底基于接口Iuser的用法
Iuser user = new UserImpl();
System.out.println("【委托类】" + user.getClass().getName());//UserImpl
System.out.println("【委托类实现的接口】" + Arrays.toString(user.getClass().getInterfaces()));//[interface Iuser, interface Irun]
InvocationHandler handler = new UserHandler(user);
Iuser proxy = (Iuser) Proxy.newProxyInstance(Iuser.class.getClassLoader(), new Class[] { Iuser.class }, handler);//能够直接强转为Iuser
System.out.println("【代理类实现的接口】" + Arrays.toString(proxy.getClass().getInterfaces()));//[interface Iuser]。由于你只指定了Iuser接口
proxy.eat("苹果");
}
private static void test2() {//精简形式
Iuser proxy = (Iuser) Proxy.newProxyInstance(Iuser.class.getClassLoader(), new Class[] { Iuser.class }, new UserHandler(new UserImpl()));
System.out.println("【代理类实现的接口】" + Arrays.toString(proxy.getClass().getInterfaces()));//[interface Iuser, interface Irun]
proxy.eat("香蕉你个巴拉");
}
private static void test3() {//彻底基于委托类UserImpl,之因此采用这种方式,是由于委托类实现了多个接口,且咱们须要调用不一样接口中的方法
Object proxy = Proxy.newProxyInstance(UserImpl.class.getClassLoader(), UserImpl.class.getInterfaces(), new UserHandler(new UserImpl()));
((Iuser) proxy).eat("你妹");//能够强转为Iuser
String returnObj = ((Irun) proxy).run(99);//也能够强转为Irun
System.out.println(returnObj);
}
}
动态代理的实现其实与静态代理相似,都须要建立代理类,可是不一样之处也很明显,建立方式不一样!
不一样之处体如今
静态代理咱们知根知底,咱们知道要对哪一个接口、哪一个实现类来建立代理类,因此咱们在编译前就直接实现与实现类相同的接口,直接在实现的方法中调用实现类中的相应同名方法便可
;而动态代理不一样,
咱们不知道它何时建立,也不知道要建立针对哪一个接口、实现类的代理类
(由于它是在运行时因需实时建立的)。
动态代理用到的两个类
proxy instance:代理实例,即代理类的实例,指得是经过 Proxy.newProxyInstance 产生的对象
the invocation handler of a proxy instance:代理实例的调用处理器,指的是实现InvocationHandler接口的那个中介类
接口 InvocationHandler 调用处理器
java.lang.reflect.InvocationHandler
InvocationHandler is the interface implemented by the invocation handler of a proxy instance.
InvocationHandler是代理实例的调用处理器实现的接口。
PS:InvocationHandler的实现类并非代理类,而只是位于代理类与委托类之间的中介类。
Each proxy instance has an associated invocation handler. When a method is invoked on a proxy instance, the method invocation is encoded and dispatched to the invoke method of its invocation handler.
每一个
代理实例都具备一个
关联的调用处理器。对代理实例
调用方法时,将对
方法调用进行编码,并将其指派到,它的调用处理器的 invoke 方法。
换一种语气描述就是:每一个代理类的实例都关联到了一个实现InvocationHandler接口的handler,当咱们经过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用。
惟一的一个方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable; //在代理实例上处理方法调用并返回结果。
在代理实例上处理方法调用并返回结果。在与方法关联的代理实例上调用方法时,将在调用处理程序上调用此方法。
参数:
- proxy - 在其上调用方法的代理实例
- method - 对应于在代理实例上调用的接口方法的 Method 实例。 Method 对象的声明类将是在其中声明方法的接口,该接口能够是代理类赖以继承方法的代理接口的超接口。
- args - 包含传入代理实例上方法调用的参数值的对象数组,若是接口方法不使用参数,则为 null。基本类型的参数被包装在适当基本包装器类的实例中。
返回:
- 从代理实例的方法调用返回的值。
- 若是接口方法的声明返回类型是基本类型,则此方法返回的值必定是相应基本包装对象类的实例;不然,它必定是可分配到声明返回类型的类型。
- 若是此方法返回的值为 null 而且接口方法的返回类型是基本类型,则代理实例上的方法调用将抛出 NullPointerException。不然,若是此方法返回的值与上述接口方法的声明返回类型不兼容,则代理实例上的方法调用将抛出 ClassCastException。
Proxy 动态代理类
public class java.lang.reflect.Proxy extends Object implements java.io.Serializable
Proxy 提供用于建立动态代理类和实例的静态方法,
它仍是由这些方法建立的全部动态代理类的超类。
基本简介
建立某一接口 Foo 的代理:
InvocationHandler handler = new MyInvocationHandler(new FooImpl());//FooImpl是实现 Foo 接口的某一委托类
Foo f = (Foo) Proxy.getProxyClass(Foo.class.getClassLoader(), new Class[] { Foo.class })
.getConstructor(new Class[] { InvocationHandler.class })
.newInstance(new Object[] { handler });
或使用如下更简单的方法:
Foo proxy = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),//定义了由哪一个ClassLoader对象来对生成的代理对象进行加载
new Class[] { Foo.class },//表示的是我将要给我须要代理的对象提供一组什么接口,以后我这个代理对象就会实现了这组接口
handler);//表示的是当我这个动态代理对象在调用方法的时候,会关联到哪个InvocationHandler对象上
动态代理类(如下简称为代理类)是一个实如今建立类时
在运行时指定的接口列表的类,该类具备下面描述的行为。
代理接口 是代理类实现的一个接口。
代理实例 是代理类的一个实例。 每一个代理实例都有一个关联的
调用处理程序 对象,它能够实现接口InvocationHandler。经过其中一个代理接口的代理实例上的方法调用将被指派到实例的调用处理程序的 Invoke 方法,并传递代理实例、识别调用方法的 java.lang.reflect.Method 对象以及包含参数的 Object 类型的数组。调用处理程序以适当的方式处理编码的方法调用,而且它返回的结果将做为代理实例上方法调用的结果返回。
代理类具备的属性(properties)
A proxy class has the following properties:
- 代理类是公共的、最终的,而不是抽象的。Proxy classes are public, final, and not abstract.
//这句话我以为是有问题的,以下:
System.out.println("【代理类的修饰符】" + Modifier.toString(proxy.getClass().getModifiers()));//final,并非public的
- 未指定代理类的非限定名称 The unqualified name of a proxy class is unspecified。可是,以字符串 "$Proxy" 开头的类名空间应该为代理类保留。
System.out.println("【代理类】" + proxy.getClass().getName());//【$Proxy0】
- 代理类的超类为(A proxy class extends) java.lang.reflect.Proxy。
System.out.println("【代理类的超类】" + proxy.getClass().getSuperclass());//【class java.lang.reflect.Proxy】
- 代理类会按同一顺序准确地实现其建立时指定的接口。
- 若是代理类实现了非公共接口,那么它将在与该接口相同的包中定义。If a proxy class implements a non-public interface, then it will be defined in the same package as that interface. 不然,代理类的包也是未指定的 unspecified。注意,包密封 package sealing 将不阻止代理类在运行时在特定包中的成功定义,也不会阻止相同类加载器和带有特定签名的包所定义的类。
- 因为代理类将实现全部在其建立时指定的接口,因此对其 Class 对象调用 getInterfaces 将返回一个包含相同接口列表的数组(按其建立时指定的顺序),对其 Class 对象调用 getMethods 将返回一个包括这些接口中全部方法的 Method 对象的数组,而且调用 getMethod 将会在代理接口中找到指望的 as would be expected 一些方法。
System.out.println("【代理类实现的接口】" + Arrays.toString(proxy.getClass().getInterfaces()));//[interface Iuser, interface Irun]
- 若是 Proxy.isProxyClass 方法传递代理类(由 Proxy.getProxyClass 返回的类,或由 Proxy.newProxyInstance 返回的对象的类),则该方法返回 true,不然返回 false。
System.out.println("【是不是代理类】" +Proxy.isProxyClass(proxy.getClass()));//true
- 代理类的 java.security.ProtectionDomain 与由引导类加载器(如 java.lang.Object)加载的系统类相同,缘由是代理类的代码由受信任的系统代码生成。此保护域一般被授予 java.security.AllPermission。
- 每一个代理类都有一个能够带一个参数(接口 InvocationHandler 的实现)的公共构造方法,用于设置代理实例的调用处理程序。并不是必须使用反射 API 才能访问公共构造方法,经过调用 Proxy.newInstance 方法(将调用 Proxy.getProxyClass 的操做和调用带有调用处理程序的构造方法结合在一块儿)也能够建立代理实例。
代理实例具备的属性
- 提供代理实例 proxy 和一个由其代理类 Foo 实现的接口,表达式 proxy instanceof Foo 将返回 true,而且 (Foo) proxy 的强制转换操做将会成功(而不抛出 ClassCastException):
//指定代理类实现的接口为:new Class[] { Iuser.class, Irun.class }
((Iuser) proxy).eat("苹果");//能够强转为Iuser
((Irun) proxy).run(99);//也能够强转为Irun
- 每一个代理实例都有一个关联的调用处理程序,它会被传递到其构造方法中。静态 Proxy.getInvocationHandler 方法将返回与做为其参数传递的代理实例相关的调用处理程序。
Iuser proxy = (Iuser) Proxy.newProxyInstance(Iuser.class.getClassLoader(), new Class[] { Iuser.class }, handler);
System.out.println(Proxy.getInvocationHandler(proxy) == handler);//true
- 代理实例上的接口方法调用将按照该方法的文档描述进行编码,并被指派到调用处理程序的 Invoke 方法。
- 在代理实例上的 java.lang.Object 中声明的 hashCode、equals 或 toString 方法的调用将按照与编码和指派接口方法调用相同的方式进行编码,并被指派到调用处理程序的 invoke 方法,如上所述。传递到 invoke 的 Method 对象的声明类是 java.lang.Object。代理类不重写从 java.lang.Object 继承的代理实例的其余公共方法,因此这些方法的调用行为与其对 java.lang.Object 实例的操做同样。
在多代理接口中重复的方法
Methods Duplicated in Multiple Proxy Interfaces
当代理类的
两个或多个接口包含一个
具备相同名称和参数签名的方法时,代理类的接口顺序变得很是重要。在代理实例上调用重复方法时,传递到调用处理程序的 Method 对象没有必要成为其声明类能够从接口(经过该接口调用代理方法)的引用类型指派的对象。此限制存在的缘由是,
生成的代理类中的相应方法实现没法肯定它经过哪个接口调用。所以,在代理实例上调用重复方法时,第一个接口中的方法的 Method 对象包含接口的代理类列表中的方法(直接或经过超级接口继承),该对象会传递到调用处理程序的 invoke 方法,不管该方法调用经过哪种引用类型发生。
若是代理接口包含某一方法,它的名称和参数签名与 java.lang.Object 的 hashCode、equals 或 toString 方法相同,那么在代理实例上调用这样的方法时,传递到调用处理程序的 Method 对象将使 java.lang.Object 成为其声明类。换句话说,java.lang.Object 公共的非最终方法理论上在全部代理接口以前,以便肯定哪个 Method 对象传递到调用处理程序。
还要注意,当重复方法被指派到调用处理程序时,invoke 方法只能够抛出通过检查的异常类型,该异常类型可使用全部 代理接口(能够经过它调用)中方法的 throws 子句指派一种异常类型。若是 invoke 方法抛出一个通过检查的异常,该异常没有指派给任何由一个代理接口(能够经过它调用)中的方法声明的异常类型,那么该代理实例上的调用将抛出一个未经检查的 UndeclaredThrowableException。此限制表示并不是全部的由传递到 invoke 方法的 Method 对象上调用 getExceptionTypes 返回的异常类型均可以由 invoke 方法成功抛出。
API
字段
- protected InvocationHandler h 此代理实例的调用处理程序。
构造方法
- protected Proxy(InvocationHandler h) 使用其调用处理程序的指定值从子类(一般为动态代理类)构建新的 Proxy 实例。
公共方法
- static InvocationHandler getInvocationHandler(Object proxy) 返回指定代理实例的调用处理程序。
- static boolean isProxyClass(Class<?> cl) 当且仅当指定的类经过 getProxyClass 方法或 newProxyInstance 方法动态生成为代理类时,返回 true。
- static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces) 返回代理类的 java.lang.Class 对象,并向其提供类加载器和接口数组。
- static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 返回一个指定接口的代理类实例,该接口能够将方法调用指派到指定的调用处理程序。
- 返回值:一个带有代理类的指定调用处理程序的代理实例,它由指定的类加载器定义,并实现指定的接口
- 参数:
- loader - 定义代理类的类加载器。定义了由哪一个ClassLoader对象来对生成的代理对象进行加载
- interfaces - 代理类要实现的接口列表。表示的是我将要给我须要代理的对象提供一组什么接口,以后我这个代理对象就会实现了这组接口
- h - 指派方法调用的调用处理程序。表示的是当我这个动态代理对象在调用方法的时候,会关联到哪个InvocationHandler对象上
关于 newProxyInstance 方法
为何咱们能够将newProxyInstance方法返回的Object类型的对象转化为Iuser或Irun类型的对象?
缘由就在newProxyInstance这个方法的第二个参数上,咱们给这个代理对象提供了一组什么接口,那么我这个代理对象就会实现这组接口,这个时候咱们固然能够将这个代理对象强制类型转化为这个接口的对象。
另外,经过 Proxy.newProxyInstance 建立的代理对象是在jvm运行时动态生成的一个对象,它实际的类型并非咱们的 InvocationHandler 类型,也不是咱们定义的那组接口的类型,而是在运行时动态生成的一个对象,而且命名方式都是这样的形式,以$开头,Proxy为中,最后一个数字表示对象的标号。
咱们来看看这两句:
((Iuser) proxy).eat("苹果");
((Irun) proxy).run(99);
这里是经过代理对象来调用实现的那种接口中的方法,这个时候程序就会跳转到由这个代理对象关联到的 handler 中的 invoke 方法去执行,而咱们的这个 handler 对象又接受了一个 UserImpl 类型的参数,表示我要代理的就是这个真实对象,因此此时就会调用 handler 中的 invoke 方法去执行。
CGLIB代码生成库简介
Byte Code Generation Library is high level API to generate and transform JAVA byte code. It is used by AOP, testing, data access frameworks to generate dynamic proxy objects and intercept field access.
字节码生成库是用于生成和转换JAVA字节码的高级API。它被AOP,测试,数据访问框架用于生成动态代理对象并拦截字段访问。
JDK从1.3版本起就提供了一个动态代理,它使用起来很是简单,可是有个明显的缺点:
须要目标对象实现一个或多个接口。
一般来讲,你可使用JDK动态代理方法来建立代理,
对于没有接口的状况或者性能因素
,CGLIB是一个很好的选择。
使用CGLIB即便代理类没有实现任何接口也能够实现动态代理功能。
CGLIB简单易用,且它的运行速度要远远快于JDK的Proxy动态代理。
本质上来讲,CGLIB经过产生子类覆盖
非final
方法来进行代理,CGLIB不能代理一个final类或者final方法。
CGLIB是一个强大的、高性能的代码生成库。它被普遍使用在
基于代理的AOP框架(例如Spring AOP和dynaop)提供方法拦截。Hibernate做为最流行的ORM工具也一样使用CGLIB库来代理单端关联(集合懒加载除外,它使用另一种机制)。EasyMock和jMock做为流行的Java测试库,它们提供Mock对象的方式来支持测试,都使用了CGLIB来对没有接口的类进行代理。
在实现内部,CGLIB库使用了
ASM这一个轻量但高性能的字节码操做框架来转化字节码,产生新类。除了CGLIB,像Groovy和BeanShell这样的脚本语言一样使用ASM来生成Java字节码。ASM使用了一个相似于SAX分析器的机制来达到高性能。咱们不建议直接使用ASM,由于这样须要对JVM很是了解,包括类文件格式和指令集。
CGLIB API
CGLIB库组织以下所示:
- net.sf.cglib.core:底层字节码操做类;大部分与ASP相关。
- net.sf.cglib.transform:编译期、运行期的class文件转换类。
- net.sf.cglib.proxy:代理建立类、方法拦截类。
- net.sf.cglib.reflect:更快的反射类、C#风格的代理类。
- net.sf.cglib.util:集合排序工具类
- net.sf.cglib.beans:JavaBean相关的工具类
对于建立动态代理,大部分状况下你只须要使用proxy包的一部分API便可。
一个案例
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
interface LoginService {
public boolean checkUser();
}
class LoginServiceImpl implements LoginService {
@Override
public boolean checkUser() {
System.out.println("LoginServiceImpl checkUser");
return false;
}
}
interface UserService {
public String getUserName();
}
class UserServiceImpl implements UserService {
@Override
public String getUserName() {
System.out.println("UserServiceImpl getUserName");
return null;
}
}
class CglibProxy implements MethodInterceptor {
@Override
public Object intercept(Object proxy, Method method, Object[] params, MethodProxy methodProxy) throws Throwable {
System.out.println("*********代理方法执行前************");
Object retObj = methodProxy.invokeSuper(proxy, params);
System.out.println("*********代理方法执行后************");
return retObj;
}
//返回目标对象的代理对象
public Object newProxy(Object target) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(this);
enhancer.setClassLoader(target.getClass().getClassLoader());
return enhancer.create();
}
}
public class Test {
public static void main(String[] args) {
//建立目标对象
LoginService loninService = new LoginServiceImpl();
UserService userService = new UserServiceImpl();
CglibProxy proxy = new CglibProxy();
//建立代理对象
LoginService loninService$Proxy = (LoginService) proxy.newProxy(loninService);
UserService userService$Proxy = (UserService) proxy.newProxy(userService);
loninService$Proxy.checkUser();
userService$Proxy.getUserName();
}
}
2017-9-6