一. 前言:
小熙昨天刚汇总完代理模式,今天记录下吧。代理模式在23种设计模式中偏向于结构模式,在访问对象不适合或者不能直接引用目标对象时,代理对象可以作为访问对象和目标对象之间的中介,并且可以前置或后置等加强目标对象的方法。
二. 过程:
-
图解
-
静态代理
/** * 静态代理模式 * 满足开闭原则,但是局限于某一个服务(这里是移动方法)去代理,其他服务,则需要重写。接口如果变化则代理类都需要改变。 * * @author chengxi * @date 2020/8/25 10:37 */ public class Person implements Movable { @Override public void move() { System.out.println("闲庭漫步。。。。。。"); } } class ProxyPerson implements Movable { Movable movable; public ProxyPerson(Movable movable) { this.movable = movable; } @Override public void move() { before(); movable.move(); after(); } public void before(){ System.out.println("移动方法的前置增强"); } public void after(){ System.out.println("移动方法的后置增强"); } } interface Movable { /** * 移动方法 */ void move(); } class testMain{ public static void main(String[] args) { ProxyPerson proxyPerson = new ProxyPerson(new Person()); proxyPerson.move(); } }
-
JDK动态代理
/** * jdk的动态代理 * 缺点是代理类必须实现接口(接口耦合),其设计就注定了有此遗憾 * * @author chengxi * @date 2020/8/25 11:10 */ public class JavaPerson implements Movable { @Override public void move() { System.out.println("闲庭漫步。。。。。"); } } interface Movable { /** * 移动方法 */ void move(); } /** * 代理方法执行处理器 */ class MoveHandler implements InvocationHandler { Movable movable; public MoveHandler(Movable movable) { this.movable = movable; } /** * 代理执行方法 * @param o 被代理的对象 * @param method 被执行的方法 * @param objects 方法的入参 * @return * @throws Throwable */ @Override public Object invoke(Object o, Method method, Object[] objects) throws Throwable { before(); Object invokeObject = method.invoke(movable, objects); after(); return invokeObject; } public void before(){ System.out.println("移动方法的前置增强"); } public void after(){ System.out.println("移动方法的后置增强"); } } class TestMain { public static void main(String[] args) { JavaPerson javaPerson = new JavaPerson(); // java的动态代理获取代理实例,第一个参数是被代理对象的类的加载器,第二个参数是被代理对象需要实现的接口类数组,第三个参数是执行处理器(里面主要是代理方法的执行与增强等) Movable movable = (Movable)Proxy.newProxyInstance(JavaPerson.class.getClassLoader(), new Class[]{Movable.class}, new MoveHandler(javaPerson)); // 调用代理方法 movable.move(); } }
-
CGLIB动态代理
/** * cglib 动态代理 * 缺点是创建时间比,jdk动态代理的时间要长的多的多,并且final修饰的方法不可代理。在某些单例场景下还是比较适合的。 * @author chengxi * @date 2020/8/25 11:37 */ public class CglibPerson { public void move(){ System.out.println("闲庭漫步。。。。。"); } } /** * cglib 代理类 */ class CglibProxy implements MethodInterceptor { private Object target; /** * 获取对象实例 * Enhancer类是CGLib中的一个字节码增强器,它可以方便的对你想要处理的类进行扩展 * @param target * @return */ public Object getInstance(final Object target) { this.target = target; Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(this.target.getClass()); enhancer.setCallback(this); return enhancer.create(); } /** * 执行方法 * @param o 被代理的对象 * @param method 被执行的方法 * @param objects 入参 * @param methodProxy 代理的方法 * @return * @throws Throwable */ @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { before(); Object invokeObject = methodProxy.invoke(target, objects); after(); return invokeObject; } public void before(){ System.out.println("移动方法的前置增强"); } public void after(){ System.out.println("移动方法的后置增强"); } } class TestMain{ public static void main(String[] args) { CglibPerson cglibPerson = new CglibPerson(); CglibProxy cglibProxy = new CglibProxy(); // 使用cglib代理类获取被代理类的子类实现,父类接收 CglibPerson proxyPerson = (CglibPerson)cglibProxy.getInstance(cglibPerson); // 调用移动方法 proxyPerson.move(); } }
三. 后语:
小熙写的以上两种动态代理的底层都是 asm(Java字节码操纵框架) 实现的,有兴趣的可以查看下,但是门槛有些高。
jdk通过asm生成$proxy(代理对象),若是一个接口,则取代理缓存池中查找,有则使用,无则生成。而cglib封装了asm,可以在运行期动态生成新的 class。