代理模式及手写模拟jdk动态代理

什么是代理

简而言之,代理就是增强一个对象的功能。目标对象,指被增强对象,代理对象,指增强后的对象。

java实现代理的两种方法

静态代理
继承
代理对象继承目标对象,重写需要增强的方法;
缺点:会代理类过多,非常复杂

聚合
目标对象和代理对象实现同一个接口,代理对象当中要包含目标对象。
缺点:也会产生类很多,只不过比继承少一点点

总结:如果在不确定的情况下,尽量不要去使用静态代理。

动态代理
jdk的动态代理:

public interface TestDao {
    String query(String str) throws Exception;
    void doPrint(String str) throws Exception;
    void doPrint() throws Exception;
}
public class TestDaoImpl implements TestDao {
    @Override
    public String query(String str) {
        System.out.println("执行了query():"+str);
        return "ok";
    }

    @Override
    public void doPrint(String str) {
        System.out.println("执行了doPrint(String str):"+str);
    }

    @Override
    public void doPrint() {
        System.out.println("执行了doPrint()");
    }
}
public class JdkInvocationHandler implements InvocationHandler {

    private Object target;

    public JdkInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //执行增强逻辑
        System.out.println("此处执行了增强逻辑......");
        return method.invoke(target,args);
    }
}
public class TestJdkProxy {
    public static void main(String[] args) {
        TestDao proxyInstance = (TestDao)Proxy.newProxyInstance(TestJdkProxy.class.getClassLoader(), new Class[]{TestDao.class},
                new JdkInvocationHandler(new TestDaoImpl()));
        proxyInstance.doPrint("hello");

    }
}

通过ide反编译工具可以查看一下jdk生成的代理类文件:
在这里插入图片描述
由此可以得到jdk动态代理只能实现接口的原因,因为代理类$Proxy已经extends Proxy了,而java是单继承。

手写一个自己的模拟jdk动态代理:

import java.lang.reflect.Method;
public interface MyInvocationHandler {

    Object invoke(Method method);

    Object invoke(Object proxy, Method method, Object[] args);

}
import java.lang.reflect.Method;
public class MyJdkInvocationHandler implements MyInvocationHandler {

    private Object target;
    public MyJdkInvocationHandler(Object target) {
        this.target = target;
    }
    @Override
    public Object invoke(Method method) {
        //此处实现无参方法增强逻辑
        System.out.println("实现了无参方法增强逻辑...");
        try {
            return method.invoke(target);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) {
        //此处实现有参方法增强逻辑
        System.out.println("实现了有参增强逻辑...");
        try {
            return method.invoke(target,args);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;

/**
 * 我自己写的proxy类
 */
public class Proxy {
    public static Object newInstance(ClassLoader loader,
                                     Class<?>[] interfaces,
                                     MyInvocationHandler h){
        Object proxy=null ;
        //只实现一个接口
        Class targetInf = interfaces[0];
        //获取接口的所有方法
        Method methods[] =targetInf.getDeclaredMethods();
        //接口名
        String infName = targetInf.getSimpleName();
        /**
         * 接下来开始拼接生成代理类的.java文件
         */
        String content = generateProxyContent(targetInf,infName,methods);

        File file =new File("d:\\com\\su\\$Proxy.java");
        try {
            if (!file.exists()) {
                file.createNewFile();
            }
            FileWriter fw = new FileWriter(file);
            fw.write(content);
            fw.flush();
            fw.close();

            /**
             * 将.java编译成.class 这段代码不是关键
             */
            JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
            StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
            Iterable units = fileMgr.getJavaFileObjects(file);
            JavaCompiler.CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);
            t.call();
            fileMgr.close();

            URL[] urls = new URL[]{new URL("file:D:\\\\")};
            URLClassLoader urlClassLoader = new URLClassLoader(urls);
            Class clazz = urlClassLoader.loadClass("com.su.$Proxy");

            Constructor constructor = clazz.getConstructor(MyInvocationHandler.class);
            proxy = constructor.newInstance(h);
        }catch (Exception e){
            e.printStackTrace();
        }
        return proxy;
    }

    private static String generateProxyContent(Class targetInf,String infName,Method methods[]){
        String line="\n";
        String tab ="\t";
        String content ="";
        String packageContent = "package com.su;"+line;
        String importContent = "import "+targetInf.getName()+";"+line
                +"import com.proxy.util.MyInvocationHandler;"+line
                +"import java.lang.Exception;"+line
                +"import java.lang.reflect.Method;"+line;
        String clazzFirstLineContent = "public class $Proxy implements "+infName+"{"+line;
        String filedContent  =tab+"private MyInvocationHandler h;"+line;
        String constructorContent =tab+"public $Proxy (MyInvocationHandler h){" +line
                +tab+tab+"this.h =h;"
                +line+tab+"}"+line;
        String methodContent = "";
        for (Method method : methods) {
            String returnTypeName = method.getReturnType().getSimpleName();
            String methodName =method.getName();
            Class args[] = method.getParameterTypes();
            int parameterCount = method.getParameterCount();
            String argsContent = "";
            String paramsContent="";
            String argsField = "";
            int flag =0;
            for (Class arg : args) {
                String temp = arg.getSimpleName();
                //String
                //String arg0,Sting arg1,
                argsContent+=temp+" arg"+flag+",";
                argsField+="arg"+flag+",";
                paramsContent+=arg.getSimpleName()+".class,";
                flag++;
            }
            if (argsContent.length()>0){
                argsContent=argsContent.substring(0,argsContent.lastIndexOf(",")-1);
                argsField=argsField.substring(0,argsField.lastIndexOf(",")-1);
                paramsContent=paramsContent.substring(0,paramsContent.lastIndexOf(","));
            }
            //方法无参
            if(parameterCount==0){
                if("void".equals(returnTypeName)){
                    methodContent+=tab+"public "+returnTypeName+" "+methodName+"("+argsContent+")throws Exception {"+line
                            +tab+tab+"Method method = Class.forName(\""+targetInf.getName()+"\").getDeclaredMethod(\""+methodName+"\");"+line
                            +tab+tab+"h.invoke(method);"+line;
                    methodContent+=tab+"}"+line;
                }else{
                    methodContent+=tab+"public "+returnTypeName+" "+methodName+"("+argsContent+")throws Exception {"+line
                            +tab+tab+"Method method = Class.forName(\""+targetInf.getName()+"\").getDeclaredMethod(\""+methodName+"\");"+line
                            +tab+tab+"return ("+returnTypeName+")h.invoke(method);"+line;
                    methodContent+=tab+"}"+line;
                }
            }else{
                //方法有参
                if("void".equals(returnTypeName)){
                    methodContent+=tab+"public "+returnTypeName+" "+methodName+"("+argsContent+")throws Exception {"+line
                            +tab+tab+"Method method = Class.forName(\""+targetInf.getName()+"\").getDeclaredMethod(\""+methodName+"\","+paramsContent+");"+line
                            +tab+tab+"h.invoke(null,method,new Object[]{"+argsField+"});"+line;
                    methodContent+=tab+"}"+line;
                }else{
                    methodContent+=tab+"public "+returnTypeName+" "+methodName+"("+argsContent+")throws Exception {"+line
                            +tab+tab+"Method method = Class.forName(\""+targetInf.getName()+"\").getDeclaredMethod(\""+methodName+"\","+paramsContent+");"+line
                            +tab+tab+"return ("+returnTypeName+")h.invoke(null,method,new Object[]{"+argsField+"});"+line;
                    methodContent+=tab+"}"+line;
                }
            }
        }
        content=packageContent+importContent+clazzFirstLineContent+filedContent+constructorContent+methodContent+"}";
        return content;
    }
}
public class TestMyJdkProxy {
    public static void main(String[] args) throws Exception{
        TestDao proxyInstance = (TestDao)Proxy.newInstance(TestMyJdkProxy.class.getClassLoader(),new Class[]{TestDao.class},
                new MyJdkInvocationHandler(new TestDaoImpl()));
       // proxyInstance.doPrint("hello");
       // proxyInstance.doPrint();
        proxyInstance.query("hello");
    }
}

中间生成的.java文件如下:
在这里插入图片描述
我们发现,调用代理对象的某个方法时候,会调用h.invoke()方法,这也解释了为什么会执行invoke。

至此完成了自己的动态代理,但和jdk的动态代理相比还有许多的缺点,比如进行了磁盘IO,没有使用缓存等会加大性能开销。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值