深度解析JAVA动态代理设计模式
来源:互联网 发布:ubuntu jenkins git 编辑:程序博客网 时间:2024/05/20 22:35
基本概念:Proxy模式也叫代理模式,所谓代理,是指具有与代理元(被代理的对象)具有相同的接口的类,客户端必须通过代理与被代理的目标类交互,而代理一般在交互的过程中(交互前后),进行某些特别的处理(比如说,日志记录,事务控制,权限过滤等等)。
(一)静态代理
项目开发中,我们往往要在某些业务方法之前或者之后加上一些日志记录,但是为了使业务方法更加专注于业务流程的实现,我们希望够把这些记录日志的语句从业务方法中脱离出来,使得整个程序的结构更加清晰。那么有什么方案能够实现这个设计思路呢
下面我们写个例子看看使用静态代理的解决方案
首先我们设计一个IHello接口:
- public interface IHello{
- //假设这是一个业务方法
- public void sayHello(String name);
- }
然后我们写个类去实现上面这个接口:
- public class Hello implements IHello{
- public void sayHello(String name){
- System.out.println("你好,"+name);
- }
- }
现在我们设计一个代理类为sayHello这个方法加上日志记录:
- public class ProxyHello implements IHello{
- private IHello hello;
- public ProxyHello(IHello hello){
- this.hello=hello;
- }
- public void sayHello(String name){
- System.out.println("sayHello start......");
- hello.sayHello(name);
- System.out.println("sayHello end......");
- }
- }
最后我们写一个测试类:
- public class Test{
- public static void main(String[] args){
- IHello hello=new ProxyHello(new Hello());
- hello.sayHello("极度暴走");
- }
- }
运行结果:
sayHello start.....
你好,极度暴走
sayHello end ......
从上面的代码我们可以看出,hello对象(代理实例)是由ProxyHello这个代理所创建,无需修改Hello类中的sayHello方法便可为其添加日志记录,并且,当我们以后不需要日志记录时我们只要把生成hello对象的代码改成如下便可:
- public class Test{
- public static void main(String[] args){
- IHello hello=new Hello();
- hello.sayHello("极度暴走");
- }
- }
(二)动态代理
设想一下,如果我们要为项目的所有类的方法加上日志记录,那么根据静态代理的解决方案,我们需要为所有的类设计各自的代理类,这样显然是很麻烦的,并且会使整个程序代码的冗余度很高。这时我们就想,能不能有那么一个类专门用来为我们动态生成代理类,OK,动态代理就因此应运而生了
先来看下动态代理类的设计:
- import java.lang.reflect.InvocationHandler;
- import java.lang.reflect.Method;
- import java.lang.reflect.Proxy;
- public class DynaProxy implements InvocationHandler{
- //委托对象,即要被代理的对象
- private Object delegate;
- //生成指定委托类的代理实例
- public Object bind(Object delegate){
- this.delegate=delegate;
- return Proxy.newProxyInstance(this.delegate.getClass().getClassLoader(), this.delegate.getClass().getInterfaces(), this);
- }
- //实现InvocationHandler的invoke方法,每个代理实例的方法会通过此方法调用委托对象的业务方法,同时加上我们需要的一些操作(如:日志记录)
- public Object invoke(Object proxy, Method method, Object[] args){
- Object result=null;
- try {
- System.out.println("mehtod start......");
- //反射调用委托对象,即被代理对象的业务方法
- result=method.invoke(delegate, args);
- System.out.println("mehtod end......");
- } catch (Exception e) {
- e.printStackTrace();
- }
- return result;
- }
- }
接下来我们设计一个测试类,从测试类中我们便可以看出动态代理的优势:
- public void Test{
- public static void main(){
- IHello hello=new DynaProxy().bind(new Hello());
- hello.sayHello("极度暴走");
- //这里假设我们还有IGoogbye接口和Googbye类
- IGoogbye googbye=new DynaProxy.bind(new Goodbye());
- googbye.sayGoogbye("极度暴走");
- }
- }
运行结果:
method start.....
你好,极度暴走
method end ......
method start.....
再见,极度暴走
method end ......
看出优势了没?
简而言之,动态代理为我们动态创建了委托类的代理实例,所有的类都可以通过DynaProxy这个代理类去生产代理实例,而不用为了每个类写一个各自的代理类。
那么动态代理是如何帮我们创建代理实例的呢?
要解答这个问题,必须搞清楚以下几点:
1 Proxy.newProxyInstance接口
InvocationHandler接口并没有想象中的那么高深莫测,事实上,这个接口只定义了一个invoke方法,我们要求代理类去实现这个接口,也就是要求代理类要去实现这个invoke方法。上面的例子,我们在invoke这个方法中实现了在业务方法(method参数)前后加上了日志记录操作。很显然,我们最后想达到的效果是,当委托类(如Hello类)的某个方法被调用时,我们能够将这个方法对象(Method)传递给我们的invoke方法,然后通过调用invoke方法实现我们需要的代理操作。
那么invoke方法是什么时候被调用的呢?让我们看完下面的内容再来思考这个问题。
2 Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)方法
这个方法实现了代理实例的创建,通过查看JDK的源代码,我发现这个方法的实现主要分下面几个步骤
步骤一:生成一个代理实例的类对象
Class proxyClass = getProxyClass(loader, interfaces);
步骤二:获取proxyClass 对象中含有InvocationHandler.class参数的构造函数
Constructor cons =proxyClass .getConstructor(InvocationHandler.class);
步骤三:调用上面的构造函数,将我们设计的InvocationHandler的实现类传递进去
return (Object) cons.newInstance(new Object[] { handler });
我们来分析一下第一步生成的类对象,这里我贴出生成的代理类的源代码(反编译得来的):
- import java.lang.reflect.InvocationHandler;
- import java.lang.reflect.Method;
- import java.lang.reflect.Proxy;
- import java.lang.reflect.UndeclaredThrowableException;
- public final class $Proxy0 extends Proxy
- implements IHello{
- private static Method m0;
- private static Method m1;
- private static Method m2;
- private static Method m3;
- static
- { //创建我们传递进来的接口中的方法对象
- m3 = Class.forName("com.IHello").getMethod("sayHello", new Class[0]);
- //下面三个方法对象是一个类对象应具有的几个基础方法
- m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
- 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]);
- return;
- }
- //带有InvocationHandler参数的构造函数
- //查看父类Proxy的源代码可以看出,Proxy类中拥有一个InvocationHandler类型的成员变量h
- public $Proxy0(InvocationHandler paramInvocationHandler){
- super(paramInvocationHandler);
- }
- //调用invoke方法实现代理业务
- public final void sayHello(){
- this.h.invoke(this, m3, null);
- return;
- }
- public final boolean equals(Object paramObject){
- return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
- }
- public final int hashCode() {
- return ((Integer)this.h.invoke(this, m0, null)).intValue();
- }
- public final String toString(){
- return ((String)this.h.invoke(this, m2, null));
- }
- }
为了便于使代码变得更简介,更容易理解,上面的代码中我把所有的异常检测语句去掉了。
从代码中我们可以看出,代理实例类中关联着一个InvocationHandler类型的成员变量h,当代理实例的方法被调用时事实上是调用了h对象的invoke方法,
从而实现了上面我们所说的——“当委托类(如Hello类)的某个方法被调用时,我们能够将这个方法对象(Method)传递给我们的invoke方法,然后通过调用invoke方法实现我们需要的代理操作”。
(三)动态代理的拓展应用——Spring AOP
正如上面分析,我们可以通过动态代理来为我们的业务方法加上日志记录,同理,我们也可以通过动态代理实现对业务方法的事务控制(比如在方法前加上beginTransation,在方法后加上commit)。
Spring中使用AOP(面向切面编程)实现事务控制的原理便是:通过的动态代理的机制,动态生成我们需要的代理实例,然后通过依赖注入,在运行时将这些代理实例注入到对应的委托类的对象中。
- 深度解析JAVA动态代理设计模式
- 深度解析JAVA动态代理设计模式
- Java动态代理深度解析
- 深入解析Java设计模式之动态代理
- -java 动态代理设计模式
- java动态代理设计模式
- java 设计模式 动态代理
- java设计模式--动态代理
- java模式设计- 动态代理
- java设计模式-代理模式(静态代理,动态代理)
- Java设计模式-----Proxy模式(动态代理)
- Java设计模式-----Proxy模式(动态代理)
- JAVA设计模式--动态代理模式
- java设计模式—动态代理模式
- java设计模式之动态代理模式!
- java设计模式:动态代理模式 Proxy
- java设计模式之代理模式,java动态代理
- java设计模式之代理模式,java动态代理
- Linux下各文件夹的含义
- 项目报错相关
- 解析Spring IOC原理——工厂模式与反射机制的综合应用
- 使用easyui图片上传的三点注意
- 百度推送iOS
- 深度解析JAVA动态代理设计模式
- 嵌入式Linux下CUPS移植
- CentOS7网络配置
- 昂贵的聘礼
- 浅析 Linux 初始化 init 系统,第 2 部分: UpStart
- 不常用到的EXCEL功能
- 第十六天
- DRBD 的日常管理
- 浅析 Linux 初始化 init 系统,第 3 部分: Systemd