java代理模式
来源:互联网 发布:阿甘 java 编辑:程序博客网 时间:2024/06/06 19:05
代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能.
这里使用到编程中的一个思想:不要随意去修改别人已经写好的代码或者方法,如果需改修改,可以通过代理的方式来扩展该方法
举个例子来说明代理的作用:假设我们想邀请一位明星,那么并不是直接连接明星,而是联系明星的经纪人,来达到同样的目的.明星就是一个目标对象,他只要负责活动中的节目,而其他琐碎的事情就交给他的代理人(经纪人)来解决.这就是代理思想在现实中的一个例子
用图表示如下:
代理模式的关键点是:代理对象与目标对象.代理对象是对目标对象的扩展,并会调用目标对象。
代理实现可以分为静态代理和动态代理(JDK动态代理(基于接口实现)和Cglib动态代理(基于继承实现))。
Spring-AOP对两种动态代理的选择:
1.如果目标对象实现了接口,默认采用JDK动态代理
2.如果目标对象没有实现接口,则采用Cglib进行动态代理
3.如果目标对象实现了接口,且强制使用cglib代理,则使用cglib代理,比如springboot注解@EnableAspectJautoProxy(ProxyTargetClass = true)
多个AOP作用于同一个目标对象如何叠加?
spring-AOP采用了责任链模式(filterChain也是采用了该模式)
由于实现方式的原因,无法拦截static、final、private方法;也无法拦截内部方法调用(例如通过this调用类内部的aop目标方法),该问题可以通过ApplicationContext获取bean实例,然后通过实例调用解决。
静态代理
静态代理模式其实很常见,比如买火车票这件小事:黄牛相当于是火车站的代理,我们可以通过黄牛买票,但只能去火车站进行改签和退票。在代码实现中相当于为一个委托对象realSubject提供一个代理对象proxy,通过proxy可以调用realSubject的部分功能,并添加一些额外的业务处理,同时可以屏蔽realSubject中未开放的接口。
2、Subject 是委托类和代理类的接口;
3、request() 是委托类和代理类的共同方法;
具体代码实现如下:
interface Subject { void request();}class RealSubject implements Subject { public void request(){ System.out.println("RealSubject"); }}class Proxy implements Subject { private Subject subject; public Proxy(Subject subject){ this.subject = subject; } public void request(){ System.out.println("begin"); subject.request(); System.out.println("end"); }}public class ProxyTest { public static void main(String args[]) { RealSubject subject = new RealSubject(); Proxy p = new Proxy(subject); p.request(); }}
静态代理实现中,一个委托类对应一个代理类,代理类在编译期间就已经确定。
JDK动态代理
动态代理中,代理类并不是在Java代码中实现,而是在运行时期生成,相比静态代理,动态代理可以很方便的对委托类的方法进行统一处理,如添加方法调用次数、添加日志功能等等,动态代理分为jdk动态代理和cglib动态代理,下面通过一个例子看看如何实现jdk动态代理。
1、定义业务逻辑
public interface Service { //目标方法 public abstract void add(); } public class UserServiceImpl implements Service { public void add() { System.out.println("This is add service"); } }
2、利用java.lang.reflect.Proxy
类和java.lang.reflect.InvocationHandler
接口定义代理类的实现。
class MyInvocatioHandler implements InvocationHandler { private Object target; public MyInvocatioHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("-----before-----"); Object result = method.invoke(target, args); System.out.println("-----end-----"); return result; } // 生成代理对象 public Object getProxy() { ClassLoader loader = Thread.currentThread().getContextClassLoader(); Class<?>[] interfaces = target.getClass().getInterfaces(); return Proxy.newProxyInstance(loader, interfaces, this); }}
3、使用动态代理
public class ProxyTest { public static void main(String[] args) { Service service = new UserServiceImpl(); MyInvocatioHandler handler = new MyInvocatioHandler(service); Service serviceProxy = (Service)handler.getProxy(); serviceProxy.add(); }}
执行结果:
-----before-----This is add service-----end-----
代理对象的生成过程由Proxy类的newProxyInstance方法实现,分为3个步骤:
1、ProxyGenerator.generateProxyClass
方法负责生成代理类的字节码,生成逻辑比较复杂,有兴趣的同学可以继续分析源码 sun.misc.ProxyGenerator;
// proxyName:格式如 "com.sun.proxy.$Proxy.1";// interfaces:代理类需要实现的接口数组;// accessFlags:代理类的访问标识;byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);
2、native方法Proxy.defineClass0
负责字节码加载的实现,并返回对应的Class对象。
Class clazz = defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);
3、利用clazz.newInstance
反射机制生成代理类的对象;
反编译代理类
为了更清楚的理解动态代理,通过以下方式把代理类字节码生成class文件。
byte[] classFile = ProxyGenerator.generateProxyClass("com.sun.proxy.$Proxy.1", service.getClass().getInterfaces());FileOutputStream out = new FileOutputStream("com.sun.proxy.$Proxy.1.class");out.write(classFile);out.flush();
使用 反编译工具 jad jad com.sun.proxy.$Proxy.1
看看代理类如何实现,反编译出来的java代码如下:
public final class $proxy1 extends Proxy implements Service { public $proxy1(InvocationHandler invocationhandler) { super(invocationhandler); } public final boolean equals(Object obj) { try { return ((Boolean)super.h.invoke(this, m1, new Object[] { obj })).booleanValue(); } catch(Error _ex) { } catch(Throwable throwable) { throw new UndeclaredThrowableException(throwable); } } public final String toString() { try { return (String)super.h.invoke(this, m2, null); } catch(Error _ex) { } catch(Throwable throwable) { throw new UndeclaredThrowableException(throwable); } } public final void add() { try { super.h.invoke(this, m3, null); return; } catch(Error _ex) { } catch(Throwable throwable) { throw new UndeclaredThrowableException(throwable); } } public final int hashCode() { try { return ((Integer)super.h.invoke(this, m0, null)).intValue(); } catch(Error _ex) { } catch(Throwable throwable) { throw new UndeclaredThrowableException(throwable); } } private static Method m1; private static Method m2; private static Method m3; private static Method m0; static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") }); m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); m3 = Class.forName("zzzzzz.Service").getMethod("add", new Class[0]); m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); } catch(NoSuchMethodException nosuchmethodexception) { throw new NoSuchMethodError(nosuchmethodexception.getMessage()); } catch(ClassNotFoundException classnotfoundexception) { throw new NoClassDefFoundError(classnotfoundexception.getMessage()); } }}
从上述代码可以发现:
1、生成的$proxy1继承自Proxy类,并实现了Service接口。
2、执行代理对象的方法,其实就是执行InvocationHandle对象的invoke方法,传入的参数分别是当前代理对象,当前执行的方法和参数。
super.h.invoke(this, m3, null);
jdk动态代理使用的局限性
通过反射类Proxy
和InvocationHandler
回调接口实现的jdk动态代理,要求委托类必须实现一个接口,但事实上并不是所有类都有接口,对于没有实现接口的类,便无法使用该方方式实现动态代理。
- Java代理之代理模式
- java代理模式---静态代理
- java代理模式--动态代理
- 代理模式&java动态代理
- JAVA代理模式--静态代理
- JAVA代理模式--动态代理
- JAVA动态代理 代理模式
- Java代理模式-静态代理
- java代理模式-动态代理
- Java代理模式 静态代理 动态代理
- JAVA代理模式与动态代理模式
- JAVA代理模式与动态代理模式
- JAVA代理模式与动态代理模式
- JAVA代理模式与动态代理模式
- JAVA代理模式与动态代理模式
- JAVA代理模式与动态代理模式
- Java代理模式和kotlin代理模式
- Java中的代理模式
- 第十周——项目一(1)—二叉树的层次遍历算法
- Android Context完全解析
- CSS——img的baseline问题
- JavaWeb导出Excel出错
- Qt实用技巧:在Qt Gui程序中嵌入qml界面(可动态覆盖整个窗口)
- java代理模式
- 第七周 项目6
- 第十周 【项目1
- numpy.array 的创建
- 自定义 View 之炫酷的成绩展示界面
- ORACLE隐式游标
- Android 精进:简述 JVM 基础(三):垃圾收集器与内存分配策略
- 第七周项目四C/C++数据结构实践——队列数组
- SQL Error: 1364, SQLState: HY000