Java代理模式汇总
简介
代理模式即Proxy Pattern,23种java常用设计模式之一。其定义为:对其他对象提供一种代理以控制对这个对象的访问。
UML类图
静态代理
目标接口
public interface Subject { public void execute(); }
目标实现类
public class RealSubject implements Subject { private String a; public RealSubject(String a) { this.a = a; } @Override public void execute() { System.out.println("do biz, " + a); } }
代理类
public class StaticProxy implements Subject { private Subject subject; public StaticProxy(Subject subject) { this.subject = subject; } @Override public void execute() { System.out.println("before executing"); subject.execute(); System.out.println("after executing"); } }
测试类
public class StaticProxyTest { public static void main(String[] args) { Subject subject = new RealSubject("hello"); StaticProxy proxy = new StaticProxy(subject); proxy.execute(); } }
运行结果输出
before executing do biz, hello after executing
通过这种方法,利用代理类在目标类执行核心方法前后添加了相应辅助逻辑。
但值得注意的是,当在代码阶段规定这种代理关系,StaticProxy类通过编译器编译成.class文件,当系统运行时,此.class已经存在了。这种静态的代理模式固然在访问无法访问的资源,增强现有的接口业务功能方面有很大的优点,但是大量使用这种静态代理,会使我们系统内类的规模增大,且不易维护;并且由于StaticProxy和RealSubject的功能本质上是相同的,StaticProxy只是起到了中介的作用,这种代理在系统中的存在,会导致系统结构比较臃肿和松散。
JDK动态代理
JDK从1.3版本起自带的动态代理机制由java.lang.reflect.Proxy实现,使用时必须创建一个实现了java.lang.reflect.InvocationHandler接口的动态代理类,该接口只有一个方法:
Object invoke(Object proxy,
Method method,
Object[] args)
throws Throwable
参数:
proxy
- 在其上调用方法的代理实例method
- 对应于在代理实例上调用的接口方法的 Method
实例。
Method
对象的声明类将是在其中声明方法的接口,该接口可以是代理类赖以继承方法的代理接口的超接口。
args
- 包含传入代理实例上方法调用的参数值的对象数组,如果接口方法不使用参数,则为 null
。基本类型的参数被包装在适当基本包装器类(如 java.lang.Integer
或 java.lang.Boolean
)的实例中。
动态代理工作的基本模式就是将自己的方法功能的实现交给 InvocationHandler角色,外界对Proxy角色中的每一个方法的调用,Proxy角色都会交给InvocationHandler来处理,而InvocationHandler则调用具体对象角色的方法。如下图所示:
再来看Proxy如何创建出一个代理对象,常用的方法为:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
参数:loader
- 定义代理类的类加载器interfaces
- 代理类要实现的接口列表h
- 指派方法调用的调用处理程序该方法返回的对象是实现了参数interfaces中指明的接口的子类对象,因此强制要求目标类必须是接口。
接下来通过示例代码演示动态代理的使用方式
目标接口
public interface Subject { public void doBiz(); }
目标实现类
public class RealSubject implements Subject { private String a; public RealSubject(String a) { this.a = a; } @Override public void doBiz() { System.out.println("do biz, " + a); } }
动态代理类
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class DynamicProxy implements InvocationHandler { private Object target; public DynamicProxy(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("before executing"); Object result = method.invoke(target, args); System.out.println("after executing"); return result; } }
测试类
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; public class DynamicProxyTest { public static void main(String[] args) { Subject subject = new RealSubject("hello"); InvocationHandler handler = new DynamicProxy(subject); Subject proxy = (Subject) Proxy.newProxyInstance( Subject.class.getClassLoader(), new Class[] { Subject.class }, handler); // 由于第二个参数传入了Subject.class,因此返回的代理对象实现了该接口,可以转换为Subject对象 System.out.println("proxy = " + proxy.getClass().getName()); if (proxy instanceof Proxy) { System.out.println("proxy implements java.lang.reflect.Proxy"); } if (proxy instanceof Subject) { System.out.println("proxy implements Subject"); } proxy.doBiz(); } }
运行结果输出
proxy = com.sun.proxy.$Proxy0 proxy implements java.lang.reflect.Proxy proxy implements Subject before executing do biz, hello after executing
第一行输出为代理对象的名称,第二、三行表明代理对象实现了java.lang.reflect.Proxy接口和Subject接口,后面的打印结果与静态代理时一致。
所以JDK动态代理有个显著的特点(限制):某个类必须有实现的接口,而生成的代理类也只能代理某个类接口定义的方法。下面介绍的Cglib动态代理就不存在此类限制。
Cglib动态代理
Cglib是一个第三方类库,用于创建动态代理,包括Spring AOP在内多个框架都内部集成了Cglib,而它底层也利用了ASM操纵字节码。Cglib类库的框架图如下:
使用Cglib创建动态代理与JDK原生的动态代理不同之处在于它并不强制要求目标类为接口,因此可以对于普通Java类进行代理。
使用时需要在pom.xml配置两个依赖:
<dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>2.2.2</version> </dependency> <dependency> <groupId>asm</groupId> <artifactId>asm</artifactId> <version>3.3.1</version> </dependency>
动态代理类需要实现net.sf.cglib.proxy.MethodInterceptor接口(等价于JDK动态代理中的InvocationHandler接口),其中只有一个方法:
public Object intercept(Object object, Method method, Object[] args,
MethodProxy proxy) throws Throwable
参数:
object
- 被代理的对象。
method -
被代理对象的方法。
args
- 包含传入代理实例上方法调用的参数值的对象数组。
proxy -
代理对象。
接下来通过示例代码演示Cglib动态代理的使用方式
目标类
public class RealSubject { private String a; public RealSubject(String a) { this.a = a; } public void doBiz() { System.out.println("do biz, " + a); } }
代理类
import java.lang.reflect.Method; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; public class CglibProxy implements MethodInterceptor { @Override public Object intercept(Object object, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("before executing"); Object result = proxy.invokeSuper(object, args); System.out.println("after executing"); return result; } }
目标类工厂
import net.sf.cglib.proxy.Enhancer; public class RealSubjectFactory { public static RealSubject getInstance(CglibProxy proxy) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(RealSubject.class); enhancer.setCallback(proxy); RealSubject subject = (RealSubject) enhancer.create( new Class[] { String.class }, new Object[] { "hello" }); // 返回值是RealSubject的子类对象;如果目标类的构造方法不含参数,则这儿无需传入create()方法的参数
return subject; } }
测试类
public class CglibProxyTest { public static void main(String[] args) { CglibProxy proxy = new CglibProxy(); RealSubject subject = RealSubjectFactory.getInstance(proxy); subject.doBiz(); } }
运行结果输出
before executing do biz, hello after executing
输出结果与静态代理一致。
Javassist动态代理
此外,利用javassist字节码生成框架也可以以类似的方式实现动态代理,使用时需要在pom.xml配置依赖:
<dependency> <groupId>org.javassist</groupId> <artifactId>javassist</artifactId> <version>3.18.1-GA</version> </dependency>
接下来直接展示示例代码。
目标接口
public interface Subject { public void doBiz(); }
目标实现类
public class RealSubject implements Subject { private String a; public RealSubject(String a) { this.a = a; } @Override public void doBiz() { System.out.println("do biz, " + a); } }
代理类
import java.lang.reflect.Method; import javassist.util.proxy.MethodHandler; public class JavassistProxy implements MethodHandler { private Object target; public JavassistProxy(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Method method2, Object[] args) throws Throwable { System.out.println("before executing"); Object result = method.invoke(target, args); System.out.println("after executing"); return result; } }
测试类
import javassist.util.proxy.ProxyFactory; import javassist.util.proxy.ProxyObject; public class JavassistProxyTest { public static void main(String[] args) throws InstantiationException, IllegalAccessException { Subject subject = new RealSubject("hello"); ProxyFactory proxyFactory = new ProxyFactory(); proxyFactory.setInterfaces(new Class[] { Subject.class }); Subject proxy = (Subject) proxyFactory.createClass().newInstance(); ((ProxyObject) proxy).setHandler(new JavassistProxy(subject)); proxy.doBiz(); } }
运行结果输出
before executing do biz, hello after executing
输出结果与静态代理一致。
REFERENCES
[1] http://jnb.ociweb.com/jnb/jnbNov2005.html
[2] http://www.cnblogs.com/flyoung2008/archive/2013/08/11/3251148.html
[3] http://www.cnblogs.com/xiaoluo501395377/p/3383130.html
[4] http://blog.csdn.net/dreamrealised/article/details/12885739
[5] http://blog.csdn.net/jackiehff/article/details/8621517
[6] https://dzone.com/articles/cglib-missing-manual
[7] http://www.360doc.com/content/14/0801/14/1073512_398598312.shtml
[8] http://javatar.iteye.com/blog/814426
为尊重原创成果,如需转载烦请注明本文出处:http://www.cnblogs.com/fernandolee24/p/6182924.html ,特此感谢