手写简易版动态代理

基础代码

先在此处声明接口与实现类,后续会用到。
Dao接口:

package dao;

/**
 * @author Livingdd
 * 2020/4/30 10:47
 **/
public interface Dao {
    void queryDataBase();
    String queryDataBase(String param);
}

Dao实现类:

package dao;

/**
 * @author Livingdd
 * 2020/4/30 11:04
 **/
public class RoleDaoImpl implements Dao{
    public void queryDataBase() {
        System.out.println("Query Role Database No Param");
    }

    public String queryDataBase(String param) {
        System.out.println("Query Role Database Param : "+param);
        return param;
    }
}

静态代理

静态代理分为两种,聚合以及继承。

继承

顾名思义,继承要代理的目标对象,对目标对象中的方法进行代理。
假如变态的老板让你用静态代理的方式对Dao层实现类加入日志功能。你的代码可能会这样:

package staticProxy.inherit;

import dao.RoleDaoImpl;

/**
 * @author Livingdd
 * 2020/4/30 11:06
 **/
public class RoleRecordLog extends RoleDaoImpl {
    @Override
    public void queryDataBase() {
        System.out.println("Record Role Log");
        super.queryDataBase();
    }

    @Override
    public String queryDataBase(String param) {
        System.out.println("Record Role Log");
        return super.queryDataBase(param);
    }
}


主方法调用:

 Dao dao = new RoleRecordLog();
 dao.queryDataBase();
 System.out.println("===============");
 System.out.println("Param Return :"+dao.queryDataBase("test"));

假如你的变态老板心情不好,和你说Dao层实现类不需要日志了,要加上事务,你的代码可能会这样:

package staticProxy.inherit;

import dao.RoleDaoImpl;

/**
 * @author Livingdd
 * 2020/4/30 11:06
 **/
public class RoleTransaction extends RoleDaoImpl {
    @Override
    public void queryDataBase() {
        System.out.println("Make Role Transaction");
        super.queryDataBase();
    }

    @Override
    public String queryDataBase(String param) {
        System.out.println("Make Role Transaction");
        return super.queryDataBase(param);
    }
}

主方法调用:

 Dao dao = new RoleTransaction();
 dao.queryDataBase();
 System.out.println("===============");
 System.out.println("Param Return :"+dao.queryDataBase("test"));

假如你的变态老板更加变态了,他可能会让你先做事务再加日志,那么你的代码可能会这样:

package staticProxy.inherit;

/**
 * @author Livingdd
 * 2020/4/30 11:10
 **/
public class TransactionAndRoleRecordLog extends RoleRecordLog{

    @Override
    public void queryDataBase() {
        System.out.println("Make Role Transaction");
        super.queryDataBase();
    }

    @Override
    public String queryDataBase(String param) {
        System.out.println("Make Role Transaction");
        return super.queryDataBase(param);
    }
}

主方法调用:

 Dao dao = new TransactionAndRoleRecordLog();
 dao.queryDataBase();
 System.out.println("===============");
 System.out.println("Param Return :" + dao.queryDataBase("test"));

但是如果你的变态老板让你先写日志再做事务呢,难道要再添加一个代理类去继承写日志的代理类吗?
所以此方法的缺点为会产生类爆炸。

聚合

目标对象与代理对象实现同一个接口,并且传入代理对象作为私有属性。
同一个例子,假如变态的老板让你对Dao层实现类加入日志功能。你的代码可能会这样:

package staticProxy.aggregation;

import dao.Dao;

/**
 * @author Livingdd
 * 2020/4/30 11:19
 **/
public class RoleRecordLog implements Dao {
    private Dao dao;
    public RoleRecordLog(Dao dao){
        this.dao  =dao;
    }
    @Override
    public void queryDataBase() {
        System.out.println("Record Role Log");
        this.dao.queryDataBase();
    }

    @Override
    public String queryDataBase(String param) {
        System.out.println("Record Role Log");
        return this.dao.queryDataBase(param);
    }
}

主方法调用:

 RoleDaoImpl roleDao = new RoleDaoImpl();
 Dao dao = new RoleRecordLog(roleDao);
 dao.queryDataBase();
 System.out.println("===============");
 System.out.println("Param Return :" + dao.queryDataBase("test"));

要是让你加入事务呢?

package staticProxy.aggregation;

import dao.Dao;

/**
 * @author Livingdd
 * 2020/4/30 11:19
 **/
public class RoleTransaction implements Dao {
    private Dao dao;
    public RoleTransaction(Dao dao){
        this.dao  =dao;
    }
    @Override
    public void queryDataBase() {
        System.out.println("Make Role Transaction");
        this.dao.queryDataBase();
    }

    @Override
    public String queryDataBase(String param) {
        System.out.println("Make Role Transaction");
        return this.dao.queryDataBase(param);
    }
}

此时你的老板又变态了,让你又加事务,又加日志,聚合的方式能够比继承的方式灵活一些,你可以这样:

 Dao roleRecordLog = new RoleRecordLog(new RoleDaoImpl());
 Dao dao = new RoleTransaction(roleRecordLog);
 dao.queryDataBase();
 System.out.println("===============");
 System.out.println("Param Return :" + dao.queryDataBase("test"));

可以看出聚合的方式相对于继承的方式,会少产生一些类,但是还是会产生很多类。

动态代理

动态代理原理为,用代码写出代理类,而后编译再获取代理对象,对目标对象进行增强。相比较与动态代理更加方便。。。
生成代理对象代码:

package dynamicProxy;

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

/**
 * @author Livingdd
 * 2020/4/30 11:28
 **/
public class ProxyUtil {
    private static final String lineFeed = "\n";
    private static final String tab = "\t";
    private static final String noReturn ="void";

    public static Object getJavaFile(Object targetObject) throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        Class clazz= targetObject.getClass().getInterfaces()[0];
        //拼接java文件
        String interfaceName = clazz.getSimpleName();
        String content = "";
        //包名
        String packageContent = "package com.livingdd;" + lineFeed + lineFeed;
        //import
        String importContent = "import dao.Dao;" + lineFeed + lineFeed;
        //类描述
        String classDescription = "public class $Proxy implements " + interfaceName + " {" + lineFeed;
        //成员变量
        String paramContent = tab + "private " + interfaceName + " target;" + lineFeed;
        //构造方法
        String constructContent = tab + "public $Proxy(" + interfaceName + " target){" + lineFeed
                + tab + tab + "this.target = target;" + lineFeed
                + tab + "}" + lineFeed;
        //方法,可能有多个,for循环拼接
        String methodContent = "";
        Method[] methods = clazz.getDeclaredMethods();
        for (Method method : methods) {
            String returnTypeName = method.getReturnType().getSimpleName();
            String methodName = method.getName();
            Class<?>[] parameterTypes = method.getParameterTypes();
            methodContent += tab + "public " + returnTypeName + " " + methodName + "(";
            //方法中的参数,可能有多个,for循环拼接
            String argsContent ="";
            //目标对象调用的参数,可能有多个,for循环拼接
            String useArgasContent="";
            int paramCount = 1;
            for (Class parameterType : parameterTypes){
                String parameterTypeSimpleName = parameterType.getSimpleName();
                argsContent += parameterTypeSimpleName+" var"+paramCount+",";
                useArgasContent += "var"+paramCount+",";
            }
            if(argsContent.length()>0){
                argsContent = argsContent.substring(0,argsContent.lastIndexOf(",")-1);
                useArgasContent = useArgasContent.substring(0,useArgasContent.lastIndexOf(",")-1);
            }
            methodContent += argsContent+"){"+lineFeed
                             +tab+tab+"System.out.println(\"Record Log\");"+lineFeed;
            methodContent += tab+tab;
            if(!noReturn.equals(returnTypeName)){
                methodContent += "return ";
            }
            methodContent += "this.target."+methodName+"("+useArgasContent+");"+lineFeed;
            methodContent += tab+"}"+lineFeed;
        }
        content+= packageContent+importContent+classDescription+paramContent+constructContent+methodContent+"}";
        FileWriter fileWriter = null;
        File file = new File("D:\\com\\livingdd\\$Proxy.java");
        try {
            if (!file.exists()) {
                file.createNewFile();
            }
            fileWriter = new FileWriter(file);
            fileWriter.write(content);
            fileWriter.flush();
        }finally {
            if(fileWriter!=null){
                fileWriter.close();
            }
        }

        //编译java文佳
        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();

        //反射获取java对象
        //这里只需要填写d盘即可,因为反射出来的类包名为com.livingdd,并且在D盘下
        URL[] urls = new URL[]{new URL("file:D:\\\\")};
        URLClassLoader classLoader = new URLClassLoader(urls);
        Class<?> loadClass = classLoader.loadClass("com.livingdd.$Proxy");
        Constructor constructor = loadClass.getConstructor(clazz);
        Object o = constructor.newInstance(targetObject);
        return o;
    }
}

主方法调用:

Dao dao = (Dao) ProxyUtil.getJavaFile(new RoleDaoImpl());

附 生成的java文件代码:

package com.livingdd;

import dao.Dao;

public class $Proxy implements Dao {
    private Dao target;
    public $Proxy(Dao target){
        this.target = target;
    }
    public void queryDataBase(){
        System.out.println("Record Log");
        this.target.queryDataBase();
    }
    public String queryDataBase(String var){
        System.out.println("Record Log");
        return this.target.queryDataBase(var);
    }
}

本文只是写了简易版的动态代理,目的在于了解大致原理,还有许多待改进的地方比如异常处理、动态传入增强的逻辑等。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值