代理设计模式(JDK与CGLIB)
来源:互联网 发布:程序员要学什么 编辑:程序博客网 时间:2024/06/17 08:40
- 静态代理
- 动态代理
- 1JDK动态代理
- 2CGLIB动态代理
代理模式的定义:
- 给某个对象提供一个代理对象,并由代理对象控制对于原对象的访问。
- 客户不直接操控原对象,而是通过代理对象间接地操控原对象。
代理模式UML图:
其中:
- RealSubject 委托对象,Proxy 是代理对象。
- Subject 是委托对象和代理对象都共同实现的接口。
- Request() 是委托对象和代理对象共同拥有的方法。
我们为什么要用代理模式呢?换句话说,代理模式的好处是什么?
- 好处一:我们可以隐藏委托类的实现;
- 好处二:可以使客户端与委托类实现解耦。即在不修改委托类的情况下实现额外的处理。
代理的实现分为:
- 静态代理:代理类是在编译时就实现好的。也就是说 Java 编译完成后代理类是一个实际的 class 文件。
- 动态代理:代理类是在运行时生成的。也就是说 Java 编译完之后并没有实际的 class 文件,而是在运行时通过Java的反射机制,动态生成类的字节码,并加载到JVM中。
静态代理
看懂了上面的UML图,其实静态代理不难实现。直接先给出代码:
- 公共接口:
public interface Subject{ void request();}
- 委托类:
public class RealSubject implements Subject{ public void request(){ System.out.println("realSubject request."); }}
- 代理类:
public class Proxy implements Subject { private Subject subject; public Proxy(Subject subject) { this.subject = subject; } public void request() { System.out.println("BeforeProcess") subject.request(); System.out.println("AfterProcess"); }}
- 客户端:
public class Client{ public static void main(String args[]){ RealSubject realSubject = new RealSubject(); Proxy p = new Proxy(realSubject); p.request(); }}
- 输出:
BeforeProcessrealSubject requestAfterProcess
可以看出,静态代理通过聚合来实现,只要让代理类持有一个委托类的引用即可。
静态代理的特点:
- 优点:业务类只需要关注业务逻辑本身,保证了业务类的重用性。当然了,这是所以代理模式的共有优点。
- 缺点:
- 代理对象的一个接口只服务于一种类型的对象,如果要代理的方法很多,势必要为每一种方法都进行代理,静态代理在程序规模稍大时就无法胜任了。
- 如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
动态代理
(1)JDK动态代理:
Java实现JDK动态代理的大致步骤如下:
首先定义一个委托类和公共接口。
然后自定义一个调用处理器类(即实现 InvocationHandler 接口的类),这个类的目的是指定运行时将生成的代理类需要完成的具体任务(包括”before process”和”after process”),代理类调用任何方法都会经过这个调用处理器类。
最后生成代理对象(当然也会生成代理类),需要为他指定(1)委托对象、(2)实现的一系列接口、(3)调用处理器类的实例。因此可以看出一个代理对象对应一个委托对象,对应一个调用处理器实例。
先用一个简单的例子实现Java实现JDK动态代理的整个过程:
- 公共接口:
public interface Subject{ void request();}
- 委托类:
public class RealSubject implements Subject{ public void request(){ System.out.println("realSubject request"); }}
- 调用处理器类:
public class ProxyHandler implements InvocationHandler{ private Subject subject; public ProxyHandler(Subject subject){ this.subject = subject; } @Override public Object invoke(Object subject, Method method, Object[] args) throws Throwable { System.out.println("before process"); Object result = method.invoke(subject, args); System.out.println("after process"); return result; }}
- 客户端:
public class Client{ public static void main(String[] args) { //1.创建委托对象 RealSubject realSubject = new RealSubject(); //2.创建调用处理器对象 ProxyHandler handler = new ProxyHandler(realSubject); //3.动态生成代理对象 Subject proxySubject = (Subject)Proxy.newProxyInstance(RealSubject.class.getClassLoader(), RealSubject.class.getInterfaces(), handler); //4.通过代理对象调用方法 proxySubject.request(); }}
- 输出:
before processrealSubject requestafter process
Jdk 的 java.lang.reflect 包下的 Proxy 类,正是构造代理类的入口。他内部的newProxyInstance 就是创建代理对象的方法,源码如下:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { //如果h为空将抛出异常 Objects.requireNonNull(h); //拷贝被代理类实现的一些接口,用于后面权限方面的一些检查 final Class<?>[] intfs = interfaces.clone(); final SecurityManager sm = System.getSecurityManager(); if (sm != null) { //这里对某些安全权限进行检查,确保我们有权限对预期的被代理类进行代理 checkProxyAccess(Reflection.getCallerClass(), loader, intfs); } //根据类加载器和接口创建代理类! Class<?> cl = getProxyClass0(loader, intfs); /* 使用指定的调用处理程序获取代理类的构造函数对象 */ try { if (sm != null) { checkNewProxyPermission(Reflection.getCallerClass(), cl); } //获得代理类的带参数的构造器 final Constructor<?> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; // 假如代理类的构造函数是非共有的,就使用反射来set accessible if (!Modifier.isPublic(cl.getModifiers())) { AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run() { cons.setAccessible(true); return null; } }); } //根据代理类的构造函数来生成代理类的对象并返回 return cons.newInstance(new Object[]{h}); } catch (IllegalAccessException|InstantiationException e) { throw new InternalError(e.toString(), e); } catch (InvocationTargetException e) { Throwable t = e.getCause(); if (t instanceof RuntimeException) { throw (RuntimeException) t; } else { throw new InternalError(t.toString(), t); } } catch (NoSuchMethodException e) { throw new InternalError(e.toString(), e); } }
上述代码主要做了一下三件事:
- 1,根据类加载器和接口创建代理类;
- 2,获得代理类的带参数的构造函数;
- 3,根据代理类的构造函数来生成代理类的对象(调用处理器实例为参数传入),并返回。
InvocationHandler 接口中有方法:
invoke(Object proxy, Method method, Object[] args)
这个函数是在代理对象调用任何一个方法时都会调用的,方法不同会导致第二个参数method不同,第一个参数是代理对象(表示哪个代理对象调用了method方法),第二个参数是 Method 对象(表示哪个方法被调用了),第三个参数是指定调用方法的参数。
总结一下使用JDK动态生成的代理类的特点:
- 继承 Proxy 类,并实现了在Proxy.newProxyInstance()中提供的接口数组。
- 代理类是public final的。
- 命名方式为
$ProxyN
,其中N会慢慢增加,一开始是$Proxy1
,接下来是$Proxy2
。。。 - 有一个参数为 InvocationHandler 的构造函数。这个从 Proxy.newProxyInstance() 函数内部的clazz.getConstructor(new Class[] { InvocationHandler.class }) 可以看出。
- 动态代理类相对于静态代理类,当代理类的实现是有很多共性的(重复代码),动态代理的好处在于避免了这些重复代码,只需要关注操作。
- Java 实现动态代理的缺点:因为 Java 的单继承特性(每个代理类都继承了 Proxy 类),只能针对接口创建代理类,不能针对类创建代理类。
(2)CGLIB动态代理:
上文提到,JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能使用JDK的动态代理了。cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。
下面我们来实现简单的CGLIB动态代理。
首先,我们需要使用maven引入cglib的依赖:
<dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.1</version><dependency>
下面开始实现cglib动态代理:
- 委托类:
public class RealSubject { public void request() { System.out.println("request."); }}
注意,该委托类没有实现接口,所以不能、或者说无法使用JDK动态代理。
- 实现MethodInterceptor接口的方法拦截器:
public class MyMethodInterceptor implements MethodInterceptor { public Object intercept(Object objcet, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("before..."); Object obj = methodProxy.invokeSuper(object, objects); System.out.println("after..."); return obj; }}
- 客户端:
public class Client { public static void main(String[] args) { // 利用Enhancer类生成代理类 Enhancer enhancer = new Enhancer(); // 继承被代理类 enhancer.setSuperClass(RealSubject.class); // 设置回调 enhancer.setCallBack(new MyMethodInterceptor()); // 生成代理对象 RealSubject real = (RealSubject)enhancer.create(); // 在调用代理类方法时,会被我们实现的拦截器拦截 real.request(); }}
整个CGLIB动态代理的具体实现步骤大概如下:
- 1、通过Enhancer类生成代理类Class的二进制字节码,并通过Class.forName加载二进制字节码,生成Class对象;
- 2、通过反射机制获取实例构造,并初始化代理类对象。
- 3,代理类将委托类作为自己的父类并为其中的非final委托方法创建两个方法,一个是与委托方法签名相同的方法,它在方法中会通过super调用委托方法;另一个是代理类独有的方法。
- 4,在代理方法中判断是否存在实现了MethodInterceptor接口的对象,若存在则将调用intercept方法对委托方法进行增强代理。
CGLIB动态代理的特点:
- 优点:可以在运行时对类或者是接口进行增强操作,委托类无需实现接口,这正好弥补了JDK动态代理的缺点。
- 缺点:不能对final类以及final方法进行代理。
最后,相信大家对什么时候用JDK动态代理,什么时候用CGLIB动态代理,以及怎么用这两种代理模式,都已经了然了。
- 代理设计模式(JDK与CGLIB)
- Java设计模式--代理模式与JDK动态代理,cglib动态代理
- 设计模式之代理模式(静态代理、JDK动态代理和cglib动态代理)
- 设计模式-代理模式(jdk代理和cglib代理详解)
- Java动态代理(JDK与CGLib)
- 动态代理(JDK与CGLIB)
- 0104 Java设计模式03-动态代理(实战篇JDK和cglib)【进阶】
- Java设计模式(七) Spring AOP JDK动态代理 VS. cglib
- Java设计模式(七) Spring AOP JDK动态代理 vs. Cglib
- jdk与cglib动态代理
- CGlib与JDK动态代理
- java代理模式、动态代理(JDK,CGLIB)
- 设计模式--动态代理(CGLIB)
- jdk动态代理与cglib代理
- JDK动态代理与CGLIB动态代理
- jdk动态代理与cglib动态代理
- 关于jdk代理与cglib代理
- JDK动态代理与CGLIB代理
- HDU6183(线段树)
- [笔记分享] [SCons] Qualcomm SCons User Guide
- github上传问题
- Html+CSS+JavaScript学习
- 如何使用网络库实现应用级消息收发
- 代理设计模式(JDK与CGLIB)
- 网站优化篇
- 多线程在编写代码时一些知识点
- K:栈和队列的比较
- Linux下时间不同步解决
- 一、概述
- Windows下Anaconda2(Python2)和Anaconda3(Python3)的共存
- 2017-8-31
- STORM入门之(TridentAPI,Aggregation)