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)的访问,这样就可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标 ...
随机推荐
- (转载)将DELPHI数据库连接写进INI配置文件中
将DELPHI数据库连接写进INI配置文件中 procedure TDM.DataModuleCreate(Sender: TObject); var piececonfg:Tinifile; pat ...
- 如何彻底删除PPA软件库
添加一个PPA源 sudo add-apt-repository ppa:user/ppa-name 如添加cairo-dock到weekly update源 sudo add-apt-reposit ...
- Mongoengine 使用笔记
1.直接将某个document对象导出对应的json数据. #models class Feed(Document): """ @summary: 所有订阅内容 &quo ...
- C语言面向对象的简便方法
都知道C语言是面向过程的,但是现在软件规模越来越大,通过面向对象的方式可以简化开发.业余时间想了个简单的方法,在C中使用一部分面向对象的基本功能.由于C语言自身的限制,并不完善,只能将就用,聊胜于无, ...
- 【python之路6】pycharm的使用
1.pycharm简介 PyCharm 是我众多python编辑器中比较好的一个.而且可以跨平台,在macos和windows下面都可以用,这点比较好. PyCharm是一种Python IDE,带有 ...
- 学习TextKit框架(上)
TextKit简介 在iOS7之前我们要实现图文混排要使用CoreText,iOS6时有了Attribute string 可以解决一些简单的富文本需求.直到iOS7 苹果推出了TextKit,Tex ...
- swift 语法 - 以及学习资料
附上一些swift的一下学习资料: 1.Swift语法介绍官方英文版:The Swift Programming Language 2.Swift与Objective-C相互调用Using Swift ...
- Hibernate一对一双向关联(外键)
网站上各种搜索,都是一些清晰或者不清晰的例子,但是用下来一是确实不给力,二是完全不知道所以然. 后来终于在书中查到了就在这里记一下. 首先要说明,这里只解释双向一对一只有一个表里面有外键的情况. 就以 ...
- 修改.htaccess实现子目录绑定示例分享
<IfModule mod_rewrite.c>RewriteEngine On RewriteBase /# 把 www.jb51.net改为你要绑定的域名.# 如果是域名:Rewri ...
- bzoj 3144: [Hnoi2013]切糕 最小割
3144: [Hnoi2013]切糕 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 681 Solved: 375[Submit][Status] ...