jdk 动态代理
代码实现
满足代理模式应用场景的三个必要条件
两个角色: 执行者, 被代理对象
注重过程, 必须要做, 被代理对象没时间做或者不想做 (怕羞羞), 不专业
执行者必须拿到被代理对象的个人资料 (执行者持有被代理对象的引用)
jdk 的动态代理通过调用 Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) 方法, 生成目标对象的代理类, interfaces 参数为目标对象所实现的全部接口, InvocationHandler 的实现类负责在调用方法前后处理自定义逻辑, 下面我们以媒婆介绍对象为背景实现:
1 首先是需要找对象的人
- public interface Person {
- void findLove();
- String getSex();
- String getName();
- }
- ---
- public class XiaoFang implements Person{
- private String sex = "女";
- private String name = "小芳";
- @Override
- public void findLove() {
- System.out.println("我叫" + this.name + ", 性别:" + this.sex + "我找对象的要求是:");
- System.out.println("高富帅");
- System.out.println("有房有车的");
- System.out.println("身高要求 180cm 以上, 体重 70kg");
- }
- public String getSex() {
- return sex;
- }
- public void setSex(String sex) {
- this.sex = sex;
- }
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- }
2 小芳不好意思找对象, 就要找媒婆来搭桥
- public class Meipo implements InvocationHandler {
- private Person target; // 需要代理的目标对象
- // 获取被代理人的个人资料
- // 将目标对象传入进行代理
- public Object getInstance(Person target) throws Exception{
- this.target = target;
- Class clazz = target.getClass();
- System.out.println("被代理对象的 class 是:"+clazz);
- return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);// 返回代理对象
- }
- @Override
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- System.out.println("开始进行海选...");
- System.out.println("------------");
- // 代理找对象
- Object ret = method.invoke(this.target, args);
- System.out.println("------------");
- System.out.println(" 如果合适的话, 就准备结婚);
- return ret;
- }
- }
3 正式开始委托媒婆找对象
- public class TestFindLove {
- public static void main(String[] args) {
- try {
- Person obj = (Person)new Meipo().getInstance(new XiaoFang());
- System.out.println(obj.getClass());
- obj.findLove();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
手写实现 jdk 动态代理
这里我们不用 jdk 提供的 reflect API, 自己手写实现 jdk 动态代理, 来进一步看一下动态代理的内部实现.
1 需要找对象的人, 参照上面的 person 接口及实现
2 媒婆需要获取被代理人的个人信息, 并生成一个替代品 (代理对象)
- public class MyPorxy {
- private static String ln = "\r\n";
- public static Object newProxyInstance(MyClassLoader classLoader, Class<?>[] interfaces, MyInvocationHandler h){
- try{
- //1, 生成源代码
- String proxySrc = generateSrc(interfaces[0]);
- //2, 将生成的源代码输出到磁盘, 保存为. java 文件
- String filePath = MyPorxy.class.getResource("").getPath();
- File f = new File(filePath + "$Proxy0.java");
- FileWriter fw = new FileWriter(f);
- fw.write(proxySrc);
- fw.flush();
- fw.close();
- //3, 编译源代码, 并且生成. class 文件
- JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
- StandardJavaFileManager manager = compiler.getStandardFileManager(null, null, null);
- Iterable iterable = manager.getJavaFileObjects(f);
- CompilationTask task = compiler.getTask(null, manager, null, null, null, iterable);
- task.call();
- manager.close();
- //4. 将 class 文件中的内容, 动态加载到 JVM 中来
- //5. 返回被代理后的代理对象
- Class proxyClass = classLoader.findClass("$Proxy0");
- Constructor c = proxyClass.getConstructor(MyInvocationHandler.class);
- f.delete();
- return c.newInstance(h);
- }catch (Exception e) {
- e.printStackTrace();
- }
- return null;
- }
- private static String generateSrc(Class<?> interfaces){
- StringBuffer src = new StringBuffer();
- src.append("package com.lh.reflect;" + ln);
- src.append("import java.lang.reflect.Method;" + ln);
- src.append("public class $Proxy0 implements" + interfaces.getName() + "{" + ln);
- src.append("MyInvocationHandler h;" + ln);
- src.append("public $Proxy0(MyInvocationHandler h) {" + ln);
- src.append("this.h = h;" + ln);
- src.append("}" + ln);
- for (Method m : interfaces.getMethods()) {
- src.append("public" + m.getReturnType().getName() + "" + m.getName() +"(){" + ln);
- src.append("try{" + ln);
- src.append("Method m =" + interfaces.getName() + ".class.getMethod(\"" +m.getName()+"\",new Class[]{});" + ln);
- src.append("this.h.invoke(this,m,null);" + ln);
- src.append("}catch(Throwable e){e.printStackTrace();}" + ln);
- src.append("}" + ln);
- }
- src.append("}");
- return src.toString();
- }
- }
3 MyClassLoader 代码:
- // 代码生成, 编译, 重新动态 load 到 JVM
- public class MyClassLoader extends ClassLoader{
- private File baseDir;
- public MyClassLoader(){
- String basePath = MyClassLoader.class.getResource("").getPath();
- this.baseDir = new java.io.File(basePath);
- }
- @Override
- protected Class<?> findClass(String name) throws ClassNotFoundException {
- String className = MyClassLoader.class.getPackage().getName() + "." + name;
- if(baseDir != null){
- File classFile = new File(baseDir,name.replaceAll("\\.", "/") + ".class");
- if(classFile.exists()){
- FileInputStream in = null;
- ByteArrayOutputStream out = null;
- try{
- in = new FileInputStream(classFile);
- out = new ByteArrayOutputStream();
- byte [] buff = new byte[1024];
- int len;
- while ((len = in.read(buff)) != -1) {
- out.write(buff, 0, len);
- }
- return defineClass(className, out.toByteArray(), 0,out.size());
- }catch (Exception e) {
- e.printStackTrace();
- }finally{
- if(null != in){
- try {
- in.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- if(null != out){
- try {
- out.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- classFile.delete();
- }
- }
- }
- return null;
- }
- }
4 接下来媒婆介绍对象的环节就水到渠成了
- public interface MyInvocationHandler {
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
- }
- ---
- public class MyMeipo implements MyInvocationHandler {
- private Person target;
- // 获取被代理人的个人资料
- public Object getInstance(Person target) throws Exception{
- this.target = target;
- Class clazz = target.getClass();
- System.out.println("被代理对象的 class 是:"+clazz);
- return MyPorxy.newProxyInstance(new MyClassLoader(), clazz.getInterfaces(), this);
- }
- @Override
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- System.out.println("开始进行海选...");
- System.out.println("------------");
- Object ret = method.invoke(this.target, args);
- System.out.println("------------");
- System.out.println("如果合适的话, 就准备结婚");
- return ret;
- }
- }
总结
jdk 动态代理原理:
拿到被代理对象的引用, 然后获取它的接口
JDK 代理重新生成一个类, 同时实现我们给的代理对象所实现的接口
把被代理对象的引用也拿到了
重新动态生成一个 class 字节码
然后编译
来源: https://www.cnblogs.com/winner2016/p/9868533.html