动态代理

动态代理

摘要

先简单介绍代理模式是什么, 核心其实就是代理类, 动态代理不过就是动态生成代理类. 之后会讲讲怎么模拟java动态代理机制, 只谈模拟的思路.

代理模式是什么?

代理模式其实说起来很简单, 简单的描述一下, 然后给出一个简单案例, 加以说明, 最后给出类图.

其实就是给被代理对象套一层(组合), 延迟调用. 下面给出一个案例, 来理解这个描述.

class 被代理的对象:s {
    被代理的方法:f(){System.out.println("doSomeService");}
}

下面代理, 为什么叫套一层.

class 代理:Proxy {
    被代理对象引用 s;
    构造方法 Proxy(ProductService s) { this.s = s; }
    代理方法:invoke() {
        before();//预处理
        s.f();//调用代理的方法
        after();//后处理
    }
    private void before(){....}
    private void after(){....}
}

本质上代理一个常用的组合引用. 为了全面性, 下面给出类图

在这里插入图片描述

看起来很复杂, 解释一下, 核心部分是RealSubject和Proxy, Proxy自然就是代理者, 前者就是被代理对象, 也就是套一层. 至于两者都实现了Subject, 只是多态的使用而已.

尽管看起来多此一举, 但是代理模式保证了我们不修改代理对象的前提下, 对代理的方法进行增强, 可以参考Spring AOP中相关的实例, 代理模式在web开发中是非常重要的.

建议参考 一文, 理解为什么会有大量的编写代理的需要.

动态代理是什么?

上面说到, 开发中会有大量的方法需要我们编写代理对象, 于是就会出现一个需求, 代理对象能自动帮我们编写吗? 我们会发现, 代理对象的模式几乎都是一摸一样. 这也就是所谓的动态代理了.

动态代理, 就是要实现自动生成代理对象的模式. 以下是谈怎么实现的问题, 其实重点就是这句话, 这也是最终我们看到的结果. 怎么实现, 理解即可.

为了讲清楚, 我先简单用一个案例介绍一下java的动态代理机制. 再从模拟java动态代理方向谈谈动态代理的原理.

JAVA动态代理简单了解

先给出案例. 案例网上随便找的, 可以说明问题.

以下为被代理对象, 以及它的父类接口.

public interface PersonDao {  
    public void say();  
}  
public class PersonDaoImpl implements PersonDao{  
    @Override  
    public void say() {  
        System.out.println("time to eat");  
}  
}  

使用java的动态代理, 没有使用过handler这里看起来可能有点难懂, 先不要管, 后面会从模拟方向讲解原理.

简单来说, 这里实现的handler就是我们自定义的增强处理, java帮我们生成的动态代理对象中, 若调用代理方法, 则就会调用handler.invoke(…), 我们自定义的处理逻辑. 这只是一个常见的抽离自定义操作封装为对象的方式而已.

public class PersonHandler implements InvocationHandler {
	private Object obj;
	public PersonHandler(Object obj){
		this.obj=obj;
	}
	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		System.out.println("before");
		Object result = method.invoke(obj, args);
		System.out.println("after");
		return result;
	}

}

下面的test类

public class PersonTest {  
    @Test  
    public void test(){  
        PersonDao pDao = new PersonDaoImpl();  
        PersonHandler handler = new PersonHandler(pDao);  
        PersonDao proxy = (PersonDao)Proxy.newProxyInstance(pDao.getClass().getClassLoader(), pDao.getClass().getInterfaces(), handler);  
        proxy.say();  
    }  
} 

上面才是动态代理的关键, 1,2句初始化了我们被代理的对象及被代理的方法(已经被抽离出来成对象handler). 第3句使用动态代理得到了最终的代理对象, 在内部, java给我们编写了代理类, 通过newProxyInstance方法, 我们得到到代理类的对象.

最后一句, 当我们调用代理对象的代理方法say时, 内部就会调用handler.Invoke().

以上就是一个简单的java动态代理的使用. 看不懂的话先读后面再回过头来看就会非常好理解了.

从模拟JAVA动态代理的角度理解原理

回到动态代理的目的, 动态代理只不过就是给我们生成了一个代理类而已, 大致如下.

proxy{
    PersonDao p;
    invoke(...){
        before();
        p.say();
        after();
    }
}

考虑上handler, 那么大致如下, 就是把我们invoke中的行为都抽离成一个对象, 方便我们自定义

proxy{
    InvocationHandler h;//代理方法的封装对象
    invoke(...){
        h.invoke(...);
    }
}
//我们可以自由编写代理方法,只要最终传给proxy就可以
自定义行为 implements InvocationHandler{
    PersonDao p;//代理对象放到了这里
    invoke(..){//实现我们想要的增强方法
        before();
        p.say();
        after();
    }
    before(){..}
    after(){..}
}

以上就是我们需要的代理类了, 那么java怎么给我们生成呢?以下需要java反射机制的基础.

有个思路很简单:

  1. 生成上面代理类的文本(理解成字符串也行), 也就是生成.java文件
  2. 命令java读取文本并命令它编译, 也就是生成.class文件
  3. 命令java加载类到内存, 使得我们可以从中新建

生成代码, 编译, 加载新建对象, 其实上面也就是我们平时写代码背后发生的事, 只不过以前都是我们来做的(javac编译之类), 但现在要交给java来做. 你可能觉得好像没区别, 这个理解需要理解java的反射机制, 和反射机制一样, 最终, 我们的目的是要达成配置少数配置文件, java就给我们生成自动生成类, 编译类, 新建对象.

具体而言, 代码大致思路如下.

//先看使用, 其中proxy是我们自己编写的实现动态代理的类
//可以对比上面java的动态代理机制
main(){
	PersonDao t = new PersonDaoImpl();
	InvocationHandler h = new 自定义行为(t);
	PersonDao m = (PersonDao)Proxy.newProxyInstance(PersonDao.class, h);
	m.say();
}
class Proxy {
	newProxyInstance(Class infce, InvocationHandler h){ 
		//1. 生成代码
        从传入的接口中, 我们可以得到所有的方法名, 参数等等, 所以我们可以生成代理类的代码文本. 细节可以参考java反射机制.
        然后通过io写到硬盘上.
		//2. 编译
        jdk是有相关库的,比如
		//3. 加载到内存创建对象 
        这个同样涉及到反射机制
		return 代理对象;
	}
}

其实我们只需要知道, 上面提到的3件事----生成.java文件, 编译, 加载创建对象, java中是完全可以通过代码做到的, jdk中已经提供了相关的库.

生成代码, 编译, 加载.

非得知道具体细节的话, 参考一下.

		//1. 生成代码
        从传入的接口中, 我们可以得到所有的方法名, 参数等等, 所以我们可以生成代理类的代码文本. 细节可以参考java反射机制.
        /* 
        
        给出一个样例参考,其实就是拼凑代码文本
        	String src = 
			"package com.bjsxt.proxy;" +  rt +
			"import java.lang.reflect.Method;" + rt +
			"public class $Proxy1 implements " + infce.getName() + "{" + rt +
			"    public $Proxy1(InvocationHandler h) {" + rt +
			"        this.h = h;" + rt +
			"    }" + rt +
			
			"    com.bjsxt.proxy.InvocationHandler h;" + rt +
							
			methodStr +
			"}";
        	
        
        */
        然后通过io写到硬盘上.
		
		//2. 编译
        jdk是有相关库的,比如
            
        /* 
        
        这是编译器对象, 功能自然是可以拿来编译
			JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
      
        */

		
		//3. 加载到内存创建对象 
        这个同样涉及到反射机制
        /*
        
        这是加载的基本方法
            Class c = ul.loadClass("....proxy.$Proxy1");
            
        从Class对象创建对象的基本方法
			Constructor ctr = c.getConstructor(InvocationHandler.class);
			Object m = ctr.newInstance(h);      
        */
		

		return m;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值