动态代理源码分析(一)模拟动态代理

本次内容:

  1. java实现代理的两种方式
  2. 静态代理
  3. 自己模拟动态代理

<一>:java实现代理的两种方式

  • 静态代理
  • 动态代理
    <二>:静态代理

静态代理又分为两种:
继承
聚合

现在我要加入一个关于日志的记录,你会把代码逻辑加在哪儿?

/*
*
 * 现在我要进行日志的记录
 * 我们可以怎么办?写一个公有的日志方法
 * 但是我们这个方法要放在哪儿?放在哪儿都不合适,放在query()当中,如果query我们调用的是jar包里面的方法,那么我们就没有办法进行写入
 * 同时我们也破坏了单一职责,因为我们的query(),就只是来查询数据库,不用来记录日志
 *
 * 我们放在调用方法Test()里面,也不可以,如果我们内容庞大,不可能每一个调用方法之前进行插入日志的记录
 *
 * 所以我们可以使用继承?
 *
 * 代理对象继承目标对象,重写需要增强的方法
 *
 * 也可以使用聚合:目标对象和代理对象实现同一个接口,代理对象当中要包含目标对象。
*/

继承的方式

public interface UserDao {
    public void query();
}
public class UserDaoLogImpl  extends UserDaoimpl {

    @Override
    public void query() {
        System.out.println("-----log-----");//加入log的逻辑
        super.query();
    }
}
public class UserTimerLogImpl extends UserDaoLogImpl{//链继承
    @Override
    public void query() {
        System.out.println("time");//此时先处理Time再处理Log
        super.query();
    }
}

继承缺点:

如果我现在想要加入时间或者权限判断呢?继续创建类,继承UserDao,重写query()
如果我现在想要同时记录时间和记录权限,并且先进行权限验证再进行时间记录,这样我们会产生一个顺序的问题,链式继承,再加上顺序要求,就会要导致类爆炸

聚合的方式

public class UserDaoimpl implements UserDao {
    public void query(){
        System.out.println("假装查询数据库");
    }
}
public class UserLogImpl2 implements UserDao {
    UserDao dao;
    public UserLogImpl2(UserDao dao){
        this.dao=dao;
    }
    public void query() {
        System.out.println("聚合的Log逻辑");
        dao.query();
    }
}
UserTimerImpl2 time=new UserTimerImpl2(new UserDaoimpl());
UserLogImpl2 log=new UserLogImpl2(time);
log.query();

聚合的Log逻辑
聚合的Timer逻辑
假装查询数据库

聚合的缺点:

这样会解决顺序的问题,因为再链式继承的问题上,我们不需要进行创建类,顺序在传参的时候就可以确定,但是如果Dao很多的情况下,也会导致类爆炸

总结:如果在不确定的情况下,尽量不要去使用静态代理。因为一旦你写代码,就会产生类,一旦产生类就爆炸。

在这里插入图片描述

自己模拟的动态代理
不需要手动创建类文件(因为一旦手动创建类文件,就会产生类爆炸),通过接口反射生成一个类文件,然后调用第三方的编译技术,动态编译这个产生的类文件成class文件,继而利用UrlclassLoader(因为这个动态产生的class不在工程当中所以需要使用UrlclassLoader)把这个动态编译的类加载到jvm当中,最后通过反射把这个类实例化。

按照这个程序来模拟

package com.luban.proxy1;  //packageContent
import com.lubandao.UserDao; //importContent 
public class UserDaoLog implemetns UserDao{ //clazzFirstLineContent 
  private UserDao target;//filedContent  
  public UserDaoLog(UserDao target){//constructorContent 
     this.target=target;
  }
  
  public void query(String i1,String i2){//methodContent
  
     System.out.println("log");
	 target.query();
  }
  }

//这个代码是按照上面的UserDaoLog的逻辑来写的
package com.luban.proxy;

import com.sun.jndi.toolkit.url.UrlUtil;

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

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();//获取到target的类名
        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="";//调用target的方法,传入的参数
            int flag =0;//eg:String p1,String p2
            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);
            }

            methodContent+=tab+"public "+returnTypeName+" "+methodName+"("+argsContent+") {"+line
                          +tab+tab+"System.out.println(\"log\");"+line//此处为也为加入的业务逻辑
                          +tab+tab+"target."+methodName+"("+paramsContent+");"+line
                          +tab+"}"+line;

        }
       //所有的内容都是拼接出来的,然后使用IO进行生成.java文件
        content=packageContent+importContent+clazzFirstLineContent+filedContent+constructorContent+methodContent+"}";

        File file =new File("G:\\com\\google\\$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();
            //开始加载.class文件成为Class对象
            URL[] urls = new URL[]{new URL("file:G:\\\\")};
            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;
    }
}

在这里插入图片描述

//测试
 public class Test {
    public static void main(String[] args) {

        LubanDao proxy = (LubanDao) ProxyUtiltwo.newInstance(new LubanDaoImpl());
        // System.out.println(proxy.getClass().getSimpleName());
        proxy.query();

    }
}
log
luban
public class UserDaoImpl implements UserDao{

    public void query(){
        System.out.println("假装查询数据库");
    }

    public void query(String aa){
        System.out.println("带参数 "+aa+" 进行测试");
    }
}
//带参数测试
public class Test {
    public static void main(String[] args) {

        //LubanDao proxy = (LubanDao) ProxyUtiroltwo.newInstance(new LubanDaoImpl());
        UserDao proxydao = (UserDao) ProxyUtiltwo.newInstance(new UserDaoImpl());
        // System.out.println(proxy.getClass().getSimpleName());
        proxydao.query("proxydao");

    }
}
log
带参数 proxydao 进行测试

这样我们就写了一个ProxyUtil就可以完成所有的代理!!!
明天继续AOP的源码动态代理的分析!!!
有错误,请提醒改正哦!!!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值