jdk的动态代理
至于jdk的动态代理怎么用此处并不多说,现在是更深一步的理解,jdk的Proxy类到底做了什么。
Proxy.newProxyInstance可以生成代理类,此方法有三个参数(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)分别是类加载器,接口数组,InvocationHandler的实现类。
简而言之,Proxy通过newProxyInstance方法生成了一个实现了第二个参数所有接口的实现类,并且内部组合了一个InvocationHandler h,当调用代理类实现的接口(第二个参数)中的方法时,该方法调用InvocationHandler 接口中的invoke方法,把自身作为第一个参数,该方法的Method对象作为第二个参数,该方法的所有参数以Object数组的形式作为第三个参数传递过去,用户可以回调invoke方法,因此自己实现类里的invoke方法的到了调用。
下面是一个自己写的MyProxy.
package com.shalf.myproxy;
public interface Animal {
public void breathe();
}
package com.shalf.myproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Client {
public static void main(String[] args) {
Animal dog = new Dog();
// 使用JDK的java.lang.reflect.Proxy生成代理实例
Animal dogProxy = (Animal) Proxy.newProxyInstance(dog.getClass().getClassLoader(), dog.getClass().getInterfaces(), new ProxyInvoke(dog));
dogProxy.breathe();
System.out.println("---------------优美的分割线在此卖萌---------------");
// 使用自己的代理类生成代理实例
// 需要注意的是这里的MyProxy类和JDK中java.lang.reflect.Proxy并不相同。只是他们的原理大致相同,实现的细节并不同。JDK中生成$Proxy1并不是拼字符串,而是直接生成二进制码。
Animal myDogProxy = (Animal) MyProxy.newProxyInstance(dog.getClass().getClassLoader(), Animal.class, new ProxyInvoke(dog));
myDogProxy.breathe();
// System.out.println(System.getProperty("user.dir"));
// System.out.println(Class.class.getClass().getResource("/").getPath());
}
}
/**
* 此类并不是代理类,只是根据这个类生成代理类,生成的代理类引用InvocationHandler实例的invoke方法,属于回调函数。
*
* @author shalf
*
*/
class ProxyInvoke implements InvocationHandler {
private final Object obj;
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("DogProxy.invoke()");
return method.invoke(obj, args);
}
public ProxyInvoke(Object obj) {
super();
this.obj = obj;
}
}
package com.shalf.myproxy;
public class Dog implements Animal {
@Override
public void breathe() {
System.out.println("Dog.breathe()");
}
}
package com.shalf.myproxy;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
public class MyProxy {
private static String filePath = System.getProperty("user.dir") + "/src/";
// private static String packageName = MyProxy.class.getPackage().getName();
private static String name = "$Proxy1";
private static String fileName = filePath+ name + ".java";
public static Object newProxyInstance(ClassLoader loader, Class<?> infce, InvocationHandler h) {
Object obj = null;
try {
// 创建源文件
String src = createClass(infce);
// 写到文件
toFile(src);
// 编译文件
compileFile();
// 获得实例
obj = getInstance(h);
} catch (Exception e) {
e.printStackTrace();
}
return obj;
}
/**
* 创建类的代码 方法最核心的代码,h是构造$Proxy1时传入的handler,本例中就是DogProxy对象
* 从而达到我们最初要在move方法前加日志逻辑的的目的
* 下面要做的就是把我们拼好的字符串生成类并load到内存就可以了这样就实现了动态生成代理类并加自己想加的逻辑
*
* @param infce
* @return
*/
private static String createClass(Class<?> infce) {
String methodStr = "";
String rt = "\r\n";
// 利用反射,获得infce接口中方法,本例中就是获得Animal接口的方法breathe
Method[] methods = infce.getMethods();
// 拼接infce中所有方法字符串,用来重写infce中的方法,本例中拼出来就是重写的breathe方法
for (Method m : methods) {
methodStr += " @Override" + rt + " public void " + m.getName() + "() {" + rt + " try {" + rt + " Method md = " + infce.getName() + ".class.getMethod(\"" + m.getName() + "\");"
+ rt + " h.invoke(this, md, md.getParameterTypes());" + rt + " }catch(Throwable e) {e.printStackTrace();}" + rt + " }" + rt;
}
String src ="import java.lang.reflect.Method;" + rt + "import java.lang.reflect.InvocationHandler;" + rt + "public class $Proxy1 implements "
+ infce.getName() + "{" + rt + " public $Proxy1(InvocationHandler h) {" + rt + " this.h = h;" + rt + " }" + rt + " InvocationHandler h;" + rt + methodStr + rt + "}";
return src;
}
/**
* load into memory and create an instance加载进内存并创建对象
*
* @param h
* @return
* @throws MalformedURLException
* @throws ClassNotFoundException
* @throws NoSuchMethodException
* @throws InstantiationException
* @throws IllegalAccessException
* @throws InvocationTargetException
*/
private static Object getInstance(InvocationHandler h) throws Exception {
String url = "file:/" + filePath+ "/";
URL[] urls = new URL[] { new URL(url) };
URLClassLoader ul = new URLClassLoader(urls);
Class<?> c = ul.loadClass(name);
//拿到那个(本例也只有这一个)带InvocationHandler参数的构造器
Constructor<?> ctr = c.getConstructor(InvocationHandler.class);
return ctr.newInstance(h);
}
/**
* 编译文件
*
* @throws IOException
*/
private static void compileFile() throws IOException {
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
compiler.getTask(null, fileMgr, null, null, null, fileMgr.getJavaFileObjects(fileName)).call();
fileMgr.close();
}
/**
* 写到文件
*
* @param src
* @throws IOException
*/
private static void toFile(String src) throws IOException {
File f = new File(fileName);
FileWriter fw = new FileWriter(f);
fw.write(src);
fw.flush();
fw.close();
}
下面是生成的代理类的源代码,当然了jdk不是生成源代码,而是在内存直接生成字节码。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class $Proxy1 implements com.shalf.myproxy.Animal {
public $Proxy1(InvocationHandler h) {
this.h = h;
}
InvocationHandler h;
@Override
public void breathe() {
try {
Method md = com.shalf.myproxy.Animal.class.getMethod("breathe");
h.invoke(this, md, md.getParameterTypes());
} catch (Throwable e) {
e.printStackTrace();
}
}
}
}
jdk的动态代理的更多相关文章
- JAVA JDK的动态代理反射实现
动态代理类使用到了一个接口InvocationHandler和一个代理类Proxy ,这两个类配合使用实现了动态代理的功能. 什么是动态代理呢? 普通代理类是指: 给每个具体类写一个代理类,以后要使 ...
- JDK的动态代理-----为接口进行代理
JDK的动态代理是必须掌握的,动态代理的好处就不用我多说了吧 :) 小弟最近在研究mybatis的源码实现,就开始了解mybatis的Mapper代理机制,为什么接口不用实现类也能代理? 好了,废话不 ...
- 基于 JDK 的动态代理机制
『动态代理』其实源于设计模式中的代理模式,而代理模式就是使用代理对象完成用户请求,屏蔽用户对真实对象的访问. 举个最简单的例子,比如我们想要「FQ」访问国外网站,因为我们并没有墙掉所有国外的 IP,所 ...
- JDK的动态代理机制
JDK Proxy OverView jdk的动态代理是基于接口的,必须实现了某一个或多个随意接口才干够被代理,并且仅仅有这些接口中的方法会被代理.看了一下jdk带的动态代理api.发现没有样例实在是 ...
- 动态代理:JDK原生动态代理(Java Proxy)和CGLIB动态代理原理+附静态态代理
本文只是对原文的梳理总结,以及自行理解.自己总结的比较简单,而且不深入,不如直接看原文.不过自己梳理一遍更有助于理解. 详细可参考原文:http://www.cnblogs.com/Carpenter ...
- CGLib与JDK的动态代理
一.CGLib 简单介绍 CGLib (Code Generation Library) 是一个强大的,高性能,高质量的Code生成类库. 它能够在执行期扩展Java类与实现Java接口. Hiber ...
- JDK的动态代理深入解析(Proxy,InvocationHandler)(转)
JDK的动态代理深入解析(Proxy,InvocationHandler)(转) 一.什么是动态代理 动态代理可以提供对另一个对象的访问,同时隐藏实际对象的具体事实.代理一般会实现它所表示的实际对象的 ...
- JDK 原生动态代理是怎么实现的 + 面试题
JDK 原生动态代理是怎么实现的 + 面试题 反射 反射机制是 Java 语言提供的一种基础功能,赋予程序在运行时自省(introspect)的能力.简单来说就是通过反射,可以在运行期间获取.检测和调 ...
- 代理模式详解:静态代理+JDK/CGLIB 动态代理实战
1. 代理模式 代理模式是一种比较好的理解的设计模式.简单来说就是 我们使用代理对象来代替对真实对象(real object)的访问,这样就可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标 ...
随机推荐
- MYSQL数据库备份与恢复
mysqldump -h主机名 -P端口 -u用户名 -p密码 (–database) 数据库名 > 文件名.sql 备份MySQL数据库的命令 mysqldump -hhostname -u ...
- NET Portability Analyzer
NET Portability Analyzer 分析迁移dotnet core 大多数开发人员更喜欢一次性编写好业务逻辑代码,以后再重用这些代码.与构建不同的应用以面向多个平台相比,这种方法更加容易 ...
- hairline!ios实现边框0.5px
在2014WWDC上,Ted O’Connor提出了“retina hairlines”的解决方案,即在ratina屏幕上可以显示0.5px宽度的边框.他的方案是这样的: 1 Standard bor ...
- hdu 4762 && 2013 ACM/ICPC 长春网络赛解题报告
这次的答案是猜出来的,如果做得话应该是应该是一个几何概型的数学题: 答案就是:n/(m^(n-1)); 具体的证明过程: 1.首先枚举这M个点中的的两个端点,概率是:n*(n-1); 2.假设这个蛋糕 ...
- IAR中 C语言位定义
__IO_REG8_BIT( SYS, 0xFFFFF802, __READ_WRITE ) #define __IO_REG8_BIT(NAME, ADDRESS, A ...
- 李洪强iOS开发之-cocopods安装
- Winform的多线程问题
http://blog.csdn.net/Maths_bai/article/details/6000744
- java实现栈的数据结构
栈是一种数据结构,只能从一端进行存储和访问.常规操作有压入栈和弹出栈. 特性:先进先出,LIFO 以下是用ArrayList为内核实现一个栈的数据结构 import java.util.ArrayLi ...
- A WCF-WPF Chat Application
http://www.codeproject.com/Articles/25261/A-WCF-WPF-Chat-Application
- hadoop2.2编程:DFS API 操作
1. Reading data from a hadoop URL 说明:想要让java从hadoop的dfs里读取数据,则java 必须能够识别hadoop hdfs URL schema, 因此我 ...