龙空技术网

java动态代理:如何手写一个jdk动态代理

炒股养娃的故事 151

前言:

今天看官们对“java的provider”大概比较注重,咱们都想要学习一些“java的provider”的相关资讯。那么小编也在网上收集了一些关于“java的provider””的相关内容,希望姐妹们能喜欢,各位老铁们一起来学习一下吧!

很多java优秀框架都应用了java里的高级特性,比如java动态代理等。

为了更好地方便大家理解 我后续将会写的文章内容,咱们先来理解下这些特性。

秉承先简后难的原则,这篇文章,先简单手写一个java动态代理。

这是什么意思? 买房子找中介,那么中介就是代理人,卖房人就是被代理的人。

java代理中涉及两个名词:代理对象(增强后的对象)以及目标对象(被代理的对象)

先了解一下静态代理的实现方式

一 java静态代理

1 新建一个 目标对象的接口类 ObjService,接口方法 echoUserName

   /** * 目标对象接口。 */public interface ObjService {    void echoUserName(String name);}

2 新建一个 实现类,ObjServiceImpl

      /** * 目标对象实现类。 */public class ObjServiceImpl  implements ObjService{    @Override    public void echoUserName(String name) {        System.out.println( "hello,"+name);    }}

3 目标对象有了,接下来,就是创建一个 同样实现ObjService接口的 代理类 LogProxyServiceImpl,在构造方法中传入要代理的目标对象。在方法中加入增强的代码逻辑。

/** * 日志代理类 */public class LogProxyServiceImpl implements ObjService{    private ObjService objService;    /**     * 将 目标对象 通过构造函数的方式 加到代理类里     * @param objService     */    public LogProxyServiceImpl(ObjService objService){        this.objService = objService;    }    @Override    public void echoUserName(String name) {      //增强的逻辑        System.out.println("log log log  start $$$");        objService.echoUserName(name);        System.out.println("log log log end $$$");    }}

4 测试一下结果

 public class ProxyTest {    public static void main(String[] args) {        // 创建目标对象            ObjService objService = new ObjServiceImpl();            //为对象加代理类            ObjService logProxy = new LogProxyServiceImpl(objService);            logProxy.echoUserName("小码哥");    }}

结果如下。代理成功了。

可以看到 每实现一个对象的代理类,都得新建一个,如果需要记录执行时间,那只能再加一个 执行时间的代理类。所以一个对象有多个代理类,多个对象呢,类就泛滥了。

  public static void main(String[] args) {        // 创建目标对象            ObjService objService = new ObjServiceImpl();            //为对象加日志代理类            ObjService logProxy = new LogProxyServiceImpl(objService);            //再加入时间代理类            ObjService timeProxy = new TimeProxyServiceImpl(logProxy);            timeProxy.echoUserName("小码哥");    }

二 java动态代理

大家可以想象一下,执行一个java的方法,需要做哪些工作呢?

第一,需要新建一个java文件;第二,编译成.class文件 ;第三,新建获取对象;最后运行。

动态生成java代理类,也是需要这么做的。接下来,咱们就来通过动态代理的方式来实现上述ObjService目标对象的日志代理类。

下面是重点,大家打起十二分精神。

1 新建一个 动态代理生成类DynamicProxy, 方法名称 newProxyInstance

     public  static  Object  newProxyInstance(Object target) throws Exception {            // 获取目标对象的接口,接口可以多个。这里简单点,取第一个            Class targetInfo = target.getClass().getInterfaces()[0];            // (1)通过目标对象 构建代理类文件,返回文件地址            File file = getProxyFile(target,targetInfo);            //(2)编译上面的java文件            compile(file);            //(3)通过类加载器加载类,并构建代理对象            URL[] urls = new URL[]{new URL("file:d:\\\\tmp\\\\")};            URLClassLoader urlClassLoader = new URLClassLoader(urls);            Class cl = urlClassLoader.loadClass("com.example.proxy.$Proxy");            Constructor constructor = cl.getConstructor(targetInfo);            Object result = constructor.newInstance(target);            return  result;        } 

2 通过目标对象 构建代理类文件,返回文件地址。getProxyFile方法的代码如下,里面是通过反射的方式获取的。

private static File getProxyFile(Object target,Class targetInfo) throws Exception{            String content = ""; //java文件的内容。            String packageContent = "package com.example.proxy;"; //文件包名            //接口名   ObjService            String targetInfoName = targetInfo.getSimpleName();            // import  ObjService 路径            String importContent = "import " + targetInfo.getName() + ";";            //声明代理类名$Proxy,并实现 ObjService接口            String classContent = "public class $Proxy implements " + targetInfoName + "{";            //  声明目标对象   private ObjService target            String fieldContent = "private " + targetInfoName + " target;";            //构造方法.          public  $Proxy(ObjService target){ this.target = target}            String constructContent = "public $Proxy(" + targetInfoName + " target){" +                    "this.target = target;" +                    "}";            //方法内容            String methodsContent = "";            //获取ObjService接口的所有方法            Method[] methods = targetInfo.getDeclaredMethods();            for(Method method:methods){                //方法名  echoUserName                String methodName = method.getName();                //返回类型 void                Class returnType = method.getReturnType();                //参数类型 这里是String                Class<?>[] paramTypes = method.getParameterTypes();                String argsContent = "";                String argsNames = "";                int i=0;                for(Class<?> paramType:paramTypes){                    String simpleName = paramType.getSimpleName();                    argsContent += simpleName+" a"+i+",";                    argsNames+= "a"+i+",";                    i++;                }                if(argsContent.length()>0){                    //去掉最后的 逗号                    argsContent = argsContent.substring(0,argsContent.length()-1);                    argsNames = argsNames.substring(0,argsNames.length()-1);                }                //生成文件                methodsContent += "public  "+returnType+" "+methodName+"("+argsContent+"){"                        +"System.out.println(\"log log log  start $$$\");"                        +"target."+methodName+"("+argsNames+");"                        +"System.out.println(\"log log log end $$$\");"                        +"}";            }            //把所有的组件内容 合起来            content+=packageContent+importContent+classContent+fieldContent+constructContent+methodsContent+"}";            //生成 $Proxy.java文件。            File file = new File("D:\\tmp\\com\\example\\proxy\\$Proxy.java");            if(!file.exists()){                file.createNewFile();            }            FileWriter fileWriter = new FileWriter(file);            fileWriter.write(content);            fileWriter.flush();            fileWriter.close();            return file;        }

3 编译java文件。代码如下。这里用系统自带的,不太建议使用,cglib 有更好的方式,大家可以先了解下这个。

private  static void compile(File file) throws Exception{            JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();            StandardJavaFileManager fileManager = compiler.getStandardFileManager(null,null,null);            Iterable units = fileManager.getJavaFileObjects(file);            JavaCompiler.CompilationTask task = compiler.getTask(null,fileManager,null,null,null,units);            task.call();            fileManager.close();        }

经过上面两步,我们这里生产的是这样的文件。

4 通过类加载器 URLClassLoader 加载 class文件。

URL[] urls = new URL[]{new URL("file:d:\\\\tmp\\\\")};            URLClassLoader urlClassLoader = new URLClassLoader(urls);            Class cl = urlClassLoader.loadClass("com.example.proxy.$Proxy");            Constructor constructor = cl.getConstructor(targetInfo);            Object result = constructor.newInstance(target);            return  result;

5 修改测试代码如下。

public class ProxyTest {    public static void main(String[] args)  throws Exception{        // 创建目标对象        ObjService objService = new ObjServiceImpl();//            //为对象加日志代理类//            ObjService logProxy = new LogProxyServiceImpl(objService);//            //再加入时间代理类//            ObjService timeProxy = new TimeProxyServiceImpl(logProxy);        //使用动态代理生成类.        ObjService proxy = (ObjService) DynamicProxy.newProxyInstance(objService);        proxy.echoUserName("小码哥");    }}

运行结果如下。符合预期。

log log log  start $$$hello,小码哥log log log end $$$

上面我们尽量用最简单的方式 写了一个 java动态代理类。真正用于生产实践 里面还有很多难点需要考虑处理。但万变不离其宗。

喜欢的小伙伴多多 关注 点赞 转发。后续会提供更多的技术文章。也希望大家多给点意见。咱们共同进步。

标签: #java的provider