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

发布时间:2021-06-23 14:53:37 作者:chen
来源:亿速云 阅读:141

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

1.什么是代理

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

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

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();
    }

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

聚合

目标对象和代理对象都要实现同一接口。 编写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怎么实现从静态代理到动态代理
那么有不同的需求就要有不同的实现方式,每一种都要编写不同的代理实现逻辑。就会写大量的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();

目录下已经生成了对应的文件
java怎么实现从静态代理到动态代理
查看生成的代理类代码
java怎么实现从静态代理到动态代理
控制台打印的日志,实现了对方法加上日志打印 java怎么实现从静态代理到动态代理
我们要实现对其他目标对象的代理,只需要调用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();
    }

只需测试,查询日志,达到了对订单接口的代理
java怎么实现从静态代理到动态代理
通过动态代理我们可以在不手动编写代理对象的方式,实现对不同目标对象的代理。增强了代码的可扩展。ps:java底层也有动态代理的工具类proxy。他的实现原理也是相同的。

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

推荐阅读:
  1. Java动态代理静态代理实例分析
  2. JAVA中静态代理与动态代理的区别有哪些

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

java

上一篇:CSS中margin属性有什么用

下一篇:CSS中 margin-right属性如何使用

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》