千家信息网

java怎么实现从静态代理到动态代理

发表于:2024-09-22 作者:千家信息网编辑
千家信息网最后更新 2024年09月22日,这篇文章主要介绍"java怎么实现从静态代理到动态代理",在日常操作中,相信很多人在java怎么实现从静态代理到动态代理问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答"j
千家信息网最后更新 2024年09月22日java怎么实现从静态代理到动态代理

这篇文章主要介绍"java怎么实现从静态代理到动态代理",在日常操作中,相信很多人在java怎么实现从静态代理到动态代理问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答"java怎么实现从静态代理到动态代理"的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

1.什么是代理

引用网上的一段话 代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗的来讲代理模式就是我们生活中常见的中介。

举个例子来说明:假如说我现在想买一辆二手车,虽然我可以自己去找车源,做质量检测等一系列的车辆过户流程,但是这确实太浪费我得时间和精力了。我只是想买一辆车而已为什么我还要额外做这么多事呢?于是我就通过中介公司来买车,他们来给我找车源,帮我办理车辆过户流程,我只是负责选择自己喜欢的车,然后付钱就可以了。用图表示如下:

2.java中如何实现代理

①. 代理的名词

代理对象:增强后的对象 目标对象:被增强的对象

②. 静态代理

先来假设一个场景,查询用户,在查询之前要写日志记录。

public class UserDaoImpl {    public void query(){        System.out.println("查询用户信息");    }}

上面代码我们要实现对其功能的增强,可以通过修改代码来实现。

public class UserDaoImpl {    public void query(){        System.out.println("log");        System.out.println("查询用户信息");    }}

这样实现可以达到效果,但是破坏了面向对象的开闭原则。况且有的时候,我们是不能拿到该Dao的源码,也没有办法对其修改。所以可以考虑代理模式来实现。

继承

编写UserDaoImpl的一个子类,复写query方法.

public class ProxyUserDao extends UserDaoImpl{    @Override    public void query() {        System.out.println("log");        super.query();    }}

编写测试,执行测试。

    public static void main(String[] args) {        ProxyUserDao proxyUserDao = new ProxyUserDao();        proxyUserDao.query();    }

聚合

目标对象和代理对象都要实现同一接口。 编写UserDao接口

public interface UserDao {    public void query();}

UserDaoImpl实现

public class UserDaoImpl implements UserDao{    public void query(){        System.out.println("查询用户信息");    }}

编写代理类

public class ProxyUserDao implements UserDao{    //目标对象    UserDao userDao;    //通过构造函数传入目标对象    public ProxyUserDao(UserDao userDao){        this.userDao = userDao;    }    @Override    public void query() {        System.out.println("log");        userDao.query();    }}

改写测试

ProxyUserDao proxyUserDao = new ProxyUserDao(new UserDaoImpl());proxyUserDao.query();

执行后,也可以完成代理。上面实现的都是先打印log,在执行查询,那么要实现先执行查询,再打印log,我们就要再写一个代理类,岂不蛋疼。

现在我们来假设要实现多个功能的增强,以前是日志记录,log,我们还要家人时间记录time,那么这里就有多种组合方式。
那么有不同的需求就要有不同的实现方式,每一种都要编写不同的代理实现逻辑。就会写大量的java代理类。我们回头看一下继承的方式来实现的话,还更加麻烦,代理类会更多。

两种方式的缺点与总结

继承:代理类过多,比较复杂 聚合:也会有大量的代理类,不过相比继承要好很多。

所以,在不确定的情况下,就不要去使用静态代理,因为会编写大量的代理类来满足不同的需求。比较麻烦。这里模拟的还仅仅是一个UserDao,若有多个目标代理对象,那么通过硬编码的方式,就真的很不理智了。

③. 动态代理

我们可以通过动态代理来避免编写大量的代理对象的困扰。

手动实现动态代理

我们先来分析一下,生成一个对象的途径。 第一步:编写.java文件
第二步:编译.java文件,获得.class文件 第三步:类记载机制加载.class文件,new出对象。

先来分析第一步:我们编写java文件以外,我们就是要为了不编写大量的类代码,所以这种方式无效,此外还可以通过编写程序实现输出一个java文件。 下面直接贴源码:

public class ProxyUtil {    /**     *  content --->string     *  .java  io     * .class     * .new   反射----》class     * @return     */    public static Object newInstance(Object target){        Object proxy=null;        Class targetInf = target.getClass().getInterfaces()[0];        Method methods[] =targetInf.getDeclaredMethods();        String line="\n";        String tab ="\t";        String infName = targetInf.getSimpleName();        String content ="";        //进行拼装代理类代码        String packageContent = "package com.google;"+line;        String importContent = "import "+targetInf.getName()+";"+line;        String clazzFirstLineContent = "public class $Proxy implements "+infName+"{"+line;        String filedContent  =tab+"private "+infName+" target;"+line;        String constructorContent =tab+"public $Proxy ("+infName+" target){" +line                                  +tab+tab+"this.target =target;"                                  +line+tab+"}"+line;        String methodContent = "";        for (Method method : methods) {            String returnTypeName = method.getReturnType().getSimpleName();            String methodName =method.getName();            // Sting.class String.class            Class args[] = method.getParameterTypes();            String argsContent = "";            String paramsContent="";            int flag =0;            for (Class arg : args) {                String temp = arg.getSimpleName();                //String                //String p0,Sting p1,                argsContent+=temp+" p"+flag+",";                paramsContent+="p"+flag+",";                flag++;            }            if (argsContent.length()>0){                argsContent=argsContent.substring(0,argsContent.lastIndexOf(",")-1);                paramsContent=paramsContent.substring(0,paramsContent.lastIndexOf(",")-1);            }            //代理打印log日志功能(这里是写死的)            methodContent+=tab+"public "+returnTypeName+" "+methodName+"("+argsContent+") {"+line                          +tab+tab+"System.out.println(\"log\");"+line                          +tab+tab+"target."+methodName+"("+paramsContent+");"+line                          +tab+"}"+line;        }        //类中的代码字符串        content=packageContent+importContent+clazzFirstLineContent+filedContent+constructorContent+methodContent+"}";        File file =new File("//$Proxy.java");        try {            if (!file.exists()) {                file.createNewFile();            }            //将content写入java文件            FileWriter fw = new FileWriter(file);            fw.write(content);            fw.flush();            fw.close();            //进行java文件编译  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.google.$Proxy");            //获取构造函数            Constructor constructor = clazz.getConstructor(targetInf);            /反射创建实例            proxy = constructor.newInstance(target);        }catch (Exception e){            e.printStackTrace();        }        return proxy;    }}

上面是编写好的代理工具类,会实现java代理文件的生成,java的编译,加载,反射创建实例。调用newInstance就返回了代理对象。不过这里将代理功能写死的。

编写测试类。并执行

UserDao o = (UserDao) ProxyUtil.newInstance(new UserDaoImpl());o.query();

目录下已经生成了对应的文件

查看生成的代理类代码

控制台打印的日志,实现了对方法加上日志打印
我们要实现对其他目标对象的代理,只需要调用newInstance传入目标对象即可。

如:我们要实现对订单OrderDao查询订单增加日志打印

//订单Dao接口public interface OrderDao {    public void query();}//订单Dao接口实现   目标对象public class OrderDaoImpl implements OrderDao {    public void query(){        System.out.println("查询订单");    }}//测试函数    public static void main(String[] args) {        OrderDao o = (OrderDao) ProxyUtil.newInstance(new OrderDaoImpl());        o.query();    }

只需测试,查询日志,达到了对订单接口的代理

通过动态代理我们可以在不手动编写代理对象的方式,实现对不同目标对象的代理。增强了代码的可扩展。ps:java底层也有动态代理的工具类proxy。他的实现原理也是相同的。

到此,关于"java怎么实现从静态代理到动态代理"的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注网站,小编会继续努力为大家带来更多实用的文章!

0