动态代理的实现分析
来源:互联网 发布:淘宝秒杀验证码看不懂 编辑:程序博客网 时间:2024/06/06 00:36
下边是我对动态代理底层实现的一些认识了解, 如有不对,敬请赐教:
1.聚合和组合的区别
2.静态代理的两种方法:
* 继承:会产生很多类。比如某个目标对象的方法需要写日志,则创建个日志代理类;现在该目标对象的方法需要增加权限,则创建
权限代理类;现在该目标对象的方法需要既写日志又权限,则还需要新建个权限日志代理类
* 聚合:针对继承的第三种情况:目标对象的方法需要既写日志又权限时,实用聚合时不需要再创建新的代理类,只需要把写日志的
代理类作为写权限的代理类的传入参数即可。这是因为使用聚合实现的代理满足两个条件:1.实现于接口类,因此写日志的
代理类本身就是接口类型,可以作为写权限代理类的参数。2.以接口类作为属性(而继承是使用方法重写的方式,实现代理
类),就这点来说聚合和组合类似
因此:聚合比继承好
3.聚合静态代理:
实现和目标相同的接口
4.代理和装饰者模式相近,只不过装饰者模式有一部分实现类是没包含接口作为其属性的。而代理类都是以接口作为属性的,在调用接口
属性的方法同时,在加一些独立的功能
5.当你发现在很多方法内都写相同的代码时,你需要抽离封装,否则你需要修改时,要修改很多地方
代理:
在目标对象基础上添加独立的功能,提供外部使用者的是代理类,这样外部使用者既能是用目标对象的功能,又能实现代理添加的独
立的功能,而不用通过修改目标对象的代码
静态代理:
让静态代理类实现目标对象的接口类,同时以目标对象实现的接口类型作为属性,然后在静态代理类中添加独立的功能,同时
也实现目标对象的方法.
针对每一类接口对象至少要实现一个代理类, 如果有很多接口对象, 则需要写大量的代理类(为什么这里写的是接口对象而不是
目标对象呢,因为同一个接口的实现类可以共用同一份静态代理类,只需在spring的配置文件中配置就依赖对象即可)
不仅造成代理类膨胀, 也会造成事务处理功能的代码存放于大量类中, 给修改维护带来很大工作量.
针对这些缺点, 用动态代理来解决: 使用JDK提供的Proxy类的方法就可以在JVM内存中产生一份二进制字节码,完全限定类名为:$Proxy0,
$Proxy0类扮演的角色就相当于一个静态代理类,只不过$Proxy0只是调用Proxy类:
newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)方法时,才在JVM内存中动态生成并且返回。
根本上$Proxy0就是一个代理类,不过需要把他指向接口类的引用对象
下边分析JDK使用Proxy类实现动态代理的原理:
newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h), 动态地创建目标对象的代理类
loader: 加载$Proxy0源代码,在JVM内存中产生的二进制内容的代理类时,所使用的加载器,一般设置为目标对象所使用的ClassLoader
interface: 目标对象实现的接口类,该参数的作用时,作为$Proxy0类的implements 接口
h: 代理实例, 该类含有Ojbect obj属性, 创建该类时, 目标对象作为其构造函数参数, 从而在通过重写InvocationHandler接口的方法:
invoke(Object proxy, Method method, Object[] args),添加代理的独立功能,并且调用目标的方法
注意:Object proxy的完全限定名为:$Proxy0
下边是我针对用户管理,使用动态代理的一个简单范例:
************************************************************************
package com.liucw.proxy;
//用户数据库操作接口类
public interface UserDAO(){
public int addUser(String userName, String password);
}
************************************************************************
package com.liucw.proxy;
//MySql数据库的用户操作实现类
public class UserDAO4MySql implements UserDAO {
public int addUser(String userName, String password) {
System.out.println("1.添加记录到用户管理表TB_SM_USER");
System.out.println("2.事务处理");
return 1;
}
}
*************************************************************************
package com.liucw.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
//实现InvocationHandler类,能处理Transaction的
public class TransactionInvocationHandler implements InvocationHandler{
private Object object;
public TransactionInvocationHandler(Object object){
this.object = object;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("invoke first param type: " + proxy.getClass().getName());
System.out.println("----- Transaction start -----");
Object ret = method.invoke(object, args);
System.out.println("----- Transaction end -----");
return ret;
}
}
************************************************************************
package com.liucw.proxy;
import java.lang.reflect.Proxy;
import junit.framework.TestCase;
//使用JUnit单元测试动态代理运行过程
public class ProxyTest extends TestCase {
public void testProxy1(){
UserDAO userDAO = new UserDAO4MySql();
TransactionInvocationHandler handler = new TransactionInvocationHandler(userDAO);
UserDAO userDAOProxy = (UserDAO)Proxy.newProxyInstance(userDAO.getClass().getClassLoader(),
userDAO.getClass().getInterfaces(), handler);
userDAOProxy.addUser("test", "123456");
}
}
执行过程是:
返回接口类userDAOProxy.addUser("test", "123456") ( 相当于UserDAO userDAOProxy = new $Proxy0() )
---> $Proxy0.addUser("test", "123456")
---> $Proxy0.addUser方法内调用: InvocationHandler.invoke(this, method, args); 根据多态的原理,实际上调用的是:
TransactionInvocationHandler的invoke(Object proxy, Method method, Object[] args)
*************************************************************************
运行结果:
invoke first param type: $Proxy0
----- Transaction start -----
1.添加记录到用户管理表TB_SM_USER
2.事务处理
----- Transaction end -----
其中在JVM内存中生成的$Proxy0二进制字节类的源代码为:
import java.lang.reflect.Method
public class $Proxy0 implements com.liucw.proxy.UserDAO{
private InvocationHandler h;
public $Proxy0(InvocationHandler h){
this.h =h;
}
@override
public int addUser(String userName, String password){
try{
Method md = com.liucw.proxy.UserDAO.class.getMethod("addUser");
Object[] args = new Object[]{userName, password};
h.invoke(this, md, args);
}catch(Exception e){
e.printStackTrace();
}
}
}
------------------------------------------------------------------------
$Proxy0的生成过程:
1.Proxy的newProxyInstance(..)方法内有一份字符串:代理类的源代码
2.有两种方式处理这份代理类的源代码:
* 使用jdk 6提供的Compiler API
* 使用更底层的CGLIB/ASM 直接把字符串,编译,在JVM生成.class二进制码
两者的区别:使用第一种方式会使用输出流在本地生成一份.java文件,然后然后使用Compiler把.java文件编译成.class字节码
文件,然后使用指定的ClassLoader对.class字节码进行类加载,在JVM内存中生成一份Class实例,然后根据方法:
newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)的Interfaces的Class
类型和InvocationHandler,利用反射Constructor,创建返回一个代理类
使用第二种方式:不会再本地生成代理类的.java文件
处理过程:
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
Iterable units = fileMgr.getJavaFileObjects(fileName);
CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);
t.call();
fileMgr.close();
//load into memory and create an instance
URL[] urls = new URL[] {new URL("file:/" + "d:/src/")};
URLClassLoader ul = new URLClassLoader(urls);
Class c = ul.loadClass("com.bjsxt.proxy.$Proxy1");
System.out.println(c);
Constructor ctr = c.getConstructor(InvocationHandler.class);
Object m = ctr.newInstance(h);
使用动态代理我觉得最重要的一点是: 调用Proxy的newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h), 在心中要有一份完全限定名为:$Proxy0的.java文件,$Proxy0类似我们写的一份静态代理类
- 动态代理的实现分析
- 动态代理的实现
- 动态代理的实现
- 动态代理的实现
- JAVA 动态代理(proxy)的实现和源码分析
- JAVA 动态代理(proxy)的实现和源码分析
- 动态代理的内存分析
- JDK动态代理实现及原理分析
- JDK动态代理实现彻底分析
- 代理模式及JDK动态代理(InvocationHandler)的简单实现与分析
- JDK动态动态代理和cglib动态代理的实现
- 实现简单的动态代理!
- 实现简单的动态代理!
- Java动态代理的实现
- 浅析动态代理的实现
- 动态代理的简单实现
- Java动态代理的实现
- Java动态代理的实现
- 详解finalize()方法
- 从qglobal.h中可以得到的信息-我们应该多研究优秀软件的源码
- ibatis 学习笔记 3
- 四选一电路的几种写法
- 使用WPF/silverlight作为我们的开发平台
- 动态代理的实现分析
- Javascript 高亮显示搜索到的关键字
- 2010-03-22
- WEB 工作流设计器开发日记3
- 搜索引擎这样可能更好
- 最容易被淘汰的人
- Java 中对文件的读写操作之比较
- 线程函数的设计以及线程同步要点(MsgWaitForMultipleObjects等)
- dxf详解A