首页 > 网名大全 正文
“qq代理叫什么名字“qq的前身叫什么名字…

时间:2023-05-15 02:04:50 阅读: 评论: 作者:佚名

什么是代理模式

我们现在有这样一个场景。有一个简单的手机类,只能打电话

public class Phone {   public void call() {    Sy("打电话");   } }

现在我们要改需求了,我们想要手机在打电话的时候可以开启录音

public class Phone {   public void call() {     Sy("开启了录音...");    Sy("打电话");   } }

但是追求优雅的程序员是不会这么写的,修改源代码不就破坏了面向对象的开闭原则了么。那么我们创建一个子类去继承Phone

public class RecordPhone extends Phone {   public void call() {     Sy("开启了录音...");    Sy("打电话");   } }

这样我们也不用修改源代码了,如果需要可录音的电话,直接使用RecordPhone就可以了。受到上面的启发,我们继续改进

抽象出一个Phone接口

public interface IPhone {     void call(); }

实现这个接口

public class Phone implements IPhone {   public void call() {    Sy("打电话");   } }

我们再创建一个代理类也实现IPhone接口

public class PhoneProxy implements IPhone {     private IPhone phone;     public PhoneProxy(){          = new Phone();     }     @Override     public void call() {         Sy("开启了录音...");         ();     } }

我们直接调用这个代理类

public class Test {     public static void main(String[] args) {         PhoneProxy proxy = new PhoneProxy();         ();     } }

结果

开启了录音... 打电话

上面就是使用了代理模式,我们抽象出接口让程序更具备扩展性。

但有个问题如果手机的游戏方法也需要增加录音功能,我们需要在代理类中重写游戏方法增加录音功能,这个还好办,毕竟是同一个类。如果不是同一个类呢,如果微信类,QQ类也需要增加录音功能,那岂不是还要写微信代理类,QQ代理类么。这样也太麻烦了。

因此我们需要一个动态的代理类,这个代理类并不是一开始就创建的,而是在调用的时候创建。

Java中的动态代理有动态代理和动态代理,这两种动态代理在Spring的代理模式中有用到。

Spring中这两种动态代理的区别为:

(1)当目标对象实现了接口,默认使用JDK动态代理,也可以强制使用CGLIB动态代理。

(2)当目标对象没有实现接口,必须使用CGLIB动态代理。

JDK动态代理

代码

public class JdkProxy  implements InvocationHandler {     //需要代理的目标对象     private Object target;     @Override     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {         Sy("JDK动态代理,监听开始...");         Object invoke = me(target, args);         Sy("JDK动态代理,监听结束...");         return invoke;     }     public Object getJdkProxy(Object targetObject) {          = targetObject;         //实例化         return Proxy.newProxyInstance().getClassLoader(),().getInterfaces(),this);     } }

测试

@org.junit.Test public void testJdkProxy() {     JdkProxy jdkProxy = new JdkProxy();     UserService userService = (UserService) jdkProxy.getJdkProxy(new UserServiceImpl());     u("lvshen","123456"); } JDK动态代理,监听开始... 调用addUser()... 参数为:name[lvshen],password[123456] JDK动态代理,监听结束...

核心代码在这里

UserService userService = (UserService) jdkProxy.getJdkProxy(new UserServiceImpl());

这里的userService实际上不是原本的userService,而是一个代理的userService。我们debug调试

如上图userService是代理方式生成的,userService1是自己new出来的。可以看到(1)和(2)的区别。当调用u("xxx"),实际上是进入了JdkProxy的invoke方法。

Object invoke = me(target, args);

就是执行的userService本身的addUser()方法,我们在me(target, args)前后进行方法增强。

jdkProxy.getJdkProxy()可以塞入其他的类,从而获得对应类的代理类。这样就避免了静态代理的弊端:每个类都要写死对应的代理类。

jdkProxy.getJdkProxy()方法返回代码

().getClassLoader(),().getInterfaces(),this);

参数中有().getInterfaces(),返回的代理对象需要获取到目标对象的接口,所以说JDK动态代理目标对象需要有接口才能生成代理对象。

继续跟代码newProxyInstance()

@CallerSensitive public static Object newProxyInstance(ClassLoader loader,                                           Class<?>[] interfaces,                                           InvocationHandler h){    ...     Class<?> cl = getProxyClass0(loader, intfs);   //通过接口获取代理类    ...                                            }

我们来看看getProxyClass0()方法,代理类是从缓存中获取的

private static Class<?> getProxyClass0(ClassLoader loader,                                            Class<?>... interfaces) {         if  > 65535) {             throw new IllegalArgumentException("interface limit exceeded");         }         // If the proxy class defined by the given loader implementing         // the given interfaces exists, this will simply return the cached copy;         // otherwise, it will create the proxy class via the ProxyClassFactory         return (loader, interfaces);      //缓存     } proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

在缓存的get()方法中有段代码

 Object subKey = Objec(key, parameter));

()实际是调用的ProxyClassFactory的apply方法。ProxyClassFactory是Proxy的内部类。apply方法就是生成代理类的方法。

生成代理类的时序图如下

CGLIB动态代理

代码

public class CglibProxy implements MethodInterceptor {     private Object target;     @Override     public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {         Sy("CGLIB动态代理,监听开始...");         Object invoke = me(target, objects);         Sy("CGLIB动态代理,监听结束...");         return invoke;     }     public Object getCglibProxy(Object target) {          = target;         Enhancer enhancer = new Enhancer();         //指定父类         en());         en(this);         Object result = en();         return result;     } }

测试

@org.junit.Test     public void testCglibProxy() {         CglibProxy cglibProxy = new CglibProxy();         UserService service = (UserService) cglibProxy.getCglibProxy(new UserServiceImpl());         ("zhouzhou","654321");     } CGLIB动态代理,监听开始... 调用addUser()... 参数为:name[zhouzhou],password[654321] CGLIB动态代理,监听结束..

同样我们来看看这两种不同创建方式(通过代理创建,自己创建)。图上图,Enhancer类似JDK动态代理的Proxy。

CGLIB动态代理需要实现MethodInterceptor接口。增强的方法就是写在intercept()中,这个方法有4个参数。

1)Object o表示增强的对象,即实现这个接口类的一个对象;

2)Method method表示要被拦截的方法;

3)Object[] objects表示要被拦截方法的参数;

4)MethodProxy methodProxy表示要触发父类的方法对象;

最后,我们发现生成代理类的方法在En()中。

protected Object nextInstance(Object instance) {    EnhancerFactoryData data = (EnhancerFactoryData) instance;    if (classOnly) {       return da;    }    Class[] argumentTypes = ;    Object[] arguments = ;    if (argumentTypes == null) {       argumentTypes = Con;       arguments = null;    }    return da(argumentTypes, arguments, callbacks); }

调用过程如下

生成代理类的时序图如下

最后总结,如果目标对象存在接口,可以通过JDK和CGLIB生成代理对象;如果目标对象没有接口,则只能通过CGLIB生成代理对象。

JDK生成的代理对象与目标对象平级;CGLIB生成的代理对象继承目标对象,并且使用CGLIB生成代理对象时,目标类不能是final修饰的。

关于性能,网上有相关的结论,在JDK1.8以前:

1、CGLIB所创建的动态代理对象在实际运行时候的性能要比JDK动态代理高不少,有研究表明,大概要高10倍;

2、但是CGLIB在创建对象的时候所花费的时间却比JDK动态代理要多很多,有研究表明,大概有8倍的差距;

3、因此,对于singleton的代理对象或者具有实例池的代理,因为无需频繁的创建代理对象,所以比较适合采用CGLIB动态代理,反正,则比较适用JDK动态代理。

然而,JDK1.8以后,两者都优化的很不错,不要再纠结使用哪种性能更好了。

  • 评论列表

发表评论: