Proxy Pattern 是一种结构模式,指为其余对象提供一种代理以控制对这个对象的访问。java
代理模式通常包含三种角色: node
Subject:
定义的RealSubject和Proxy的共有方法,这个类能够是接口,也能够是抽象类。这样全部使用RealSubject的地方均可以用Proxy代替。框架
RealSubject:
被代理类,此类定义了真实的Subject实现。ide
Proxy:
代理类通常要持有一个被代理的对象的引用,并可能负责建立和删除被代理对象。性能
代理模式的实现大致上能够分为两种,一种是静态代理,一种是动态代理。两种代理从JVM加载类的角度来说,本质上都是同样的。this
上面的类图就是传统的静态代理,代码不贴了。(然而相关代码接下来的demo里有用到。)spa
优势:.net
缺点:代理
JAVA的SDK中已经有了实现方法,Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h);
使用JDK动态代理须要注意,目标类必须实现的某个接口,若是某个类没有实现接口则不能生成代理对象。 code
优势:
动态代理扩展性好。
缺点:
public static class SubjectInvocationHandler implements InvocationHandler { private Object object; public SubjectInvocationHandler(Object object) { this.object = object; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("before 动态代理..."); System.out.println(proxy.getClass().getName()); System.out.println(this.object.getClass().getName()); return method.invoke(object, args); } } public static void main(String[] args) { Subject o = (Subject) Proxy.newProxyInstance(Main.class.getClassLoader(), new Class[]{Subject.class}, new SubjectInvocationHandler(new RealSubject())); o.request(); }
<dependency> <groupId>cglib</groupId> <artifactId>cglib-nodep</artifactId> <version>2.2</version> </dependency>
public static void main(String[] args) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(RealSubject.class); enhancer.setCallback((MethodInterceptor) (obj, method, args1, proxy) -> { System.out.println("before method run..."); Object result = proxy.invokeSuper(obj, args1); System.out.println("after method run..."); return result; }); RealSubject subject = (RealSubject) enhancer.create(); subject.request(); }
Cglib原理是针对目标类生成一个子类,覆盖其中的全部方法,因此目标类和方法不能声明为final类型。
Cglib采用ASM框架写Class字节码,实现比JDK复杂,因此生产代理类比JDK动态代理效率低。
JDK是采用反射机制调用的,而CGLib经过FastClass机制直接调用方法,因此只需效率更高。
FastClass不使用反射类(Constructor或Method)来调用委托类方法,而是动态生成一个新的类(继承FastClass),向类中写入委托类实例直接调用方法的语句,用模板方式解决Java语法不支持问题,同时改善Java反射性能。
动态类为委托类方法调用语句创建索引,使用者根据方法签名(方法名+参数类型)获得索引值,再经过索引值进入相应的方法调用语句,获得调用结果。