JDK动态代理


静态代理的缺点

1、会产生很多的代理类
2、产生的代理类只能代理既定的接口


动态代理

动态代理就是,在程序运行期,创建目标对象的代理对象,并对目标对象中的方法进行功能性增强的一种技术。在生成代理对象的过程中,目标对象不变,代理对象中的方法是目标对象方法的增强方法。可以理解为运行期间,对象中方法的动态拦截,在拦截方法的前后执行功能操作。

动态代理相比于静态代理的优点:

抽象角色中(接口)声明的所有方法都被转移到调用处理器一个集中的方法中处理,这样,我们可以更加灵活和统一的处理众多的方法。

JDK 动态代理

JDK 的动态代理是基于拦截器和反射来实现的。JDK代理是不需要第三方库支持的,只需要 JDK 环境就可以进行代理

  • 必须实现 InvocationHandler 接口
  • 使用 Proxy.newProxyInstance 产生代理对象
  • 被代理的对象必须要实现接口

JDK 动态代理不能对类进行代理:因为代理类($Proxy0)已经继承了 Proxy 类,由于 Java 语言只支持单继承,所以 JDK 动态代理不能对类进行代理

JDK 动态代理简单实现

接口

public interface MDao {

    public String query(int id, String name);
}

接口实现类

@Slf4j(topic = "e")
public class MemberDao implements MDao {
    @Override
    public String query(int id, String name) {
        log.debug("MemberDao--------------query-------------logic");
        return name+"-return";
    }
}

实现 InvocationHandler 接口

@Slf4j(topic = "e")
public class CustomInvocationHandler implements InvocationHandler {

    //目标对象
    Object object;
    public CustomInvocationHandler(Object object){
        this.object = object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //代理的before业务逻辑
        log.debug("before----xxxxxxxxxxx----JDKProxy");
        //执行目标对象的方法
        Object invoke = method.invoke(object, args);
        //代理的after业务逻辑
        log.debug("after----xxxxxxxxxxx----JDKProxy");
        return invoke;
    }
}

测试类

@Slf4j(topic = "e")
public class TestApp {

    public static void main(String[] args) {
        MDao mDao = new MemberDao();

//        new ProxyCustom().createProxy(MDao.class);
        MDao dao = (MDao) Proxy.newProxyInstance(TestApp.class.getClassLoader(),
                                        new Class[]{MDao.class},
                                        new CustomInvocationHandler(mDao));
        log.debug("{}",dao.query(12,"aa"));

    }
}
  • 此时调用的dao,query()方法,实际上是代理对象增强后的query()方法
  newProxyInstance()方法的实现:
   1、他会产生一段字符串代理类的源码
   2、把这个字符串输出到一个.java ($Proxy.java)文件当中
   3、会把这个$Proxy.java动态编译他成为一个$Proxy.class
   4、会通过一个类加载器把这个$Proxy.cLass加载到JVM当中
   5、CLass.forname("xxxx").newInstance反射实例化这个对象 proxyObject  (产生的代理对象是对目标对象的增强)
   
   JDK省略1,2步,直接再内存中产生.class字节码文件

动态生成代理类的逻辑

public class ProxyCustom {

    /*
    * 他能返回一个对象--符合我们期望的代理对象
    * class文件
    * java 文件
    * 代码
    *
    * io把我们的代码写道一个.java文件当中,然后再手动把这个.java文件编译
    * 编译完成之后肯定会产生一个.class文件,继而把这个.class文件Loader到门JVM当中
    * 然后通过反射区实例化这个对象,最终返回出去
     * */

    public Object createProxy(Class infce){
        // 方法的字符串
        String methodStr = "";
        String rt = "\r\n";
        String tab = "\t";
        String r = "\n";

        //获取接口当中的所有方法,方便后面遍历方法构建代理类的字符串
        Method[] methods = infce.getMethods();
        //每个方法的返回类型
        String rtype = "";
        //表示该方法有几个参数
        int args = 0;

        for (Method m : methods) {
            // 得到方法的返回类型的字符串
            rtype = m.getReturnType().getSimpleName();
            // 参数的字符串有可能有参或者无参
            String argsStr = "";
            // 最后执行invoke方法的时候需要传入的参数值
            String argsValueStr = "";
            //得到这个方法所有的参数个数
            int parameterCount = m.getParameterCount();
            Class[] classesParamArr = null;
            //再反射得到目标方法的时候需要的参数个数和类型的字符串
            String getMethodParamStr = "new Class[]{ ";
            if (parameterCount>0){
                classesParamArr = new Class[parameterCount];
                // 得到所有的参数个数和类型
                Class<?>[] parameterTypes = m.getParameterTypes();
                int pc = 0;
                for (Class<?> parameterType : parameterTypes) {
                    // clssesParamArr[pc]=parameterType;
                    getMethodParamStr += parameterType.getSimpleName() + ".class,";
                    argsStr += parameterType.getSimpleName() + " p" + pc + ",";
                    argsValueStr += "p" + pc + ", ";
                    pc++;
                }
                //截取最后一个逗号
                getMethodParamStr = getMethodParamStr.substring(0,getMethodParamStr.length()-1);

                //截取最后一个逗号
                argsStr = argsStr.substring(0, argsStr.length()-1);
                //截取最后一个逗号
                argsValueStr = argsValueStr.substring(0, argsValueStr.length()-1);
            }
            getMethodParamStr += "}";
            rtype = m.getReturnType().getSimpleName();
            String endReturnStr = "";
            String returnStr= "";
            //是否需要强转类型
            String convertStr = "";
            if( !rtype.equals("void")) {
                returnStr = "return ";
                endReturnStr = "return null;";
                convertStr = "(" + rtype + ")";
            }

            methodStr += tab+"@Override" + rt +
                    tab+"public " + rtype +" "+ m.getName() + "("+argsStr+") {" + rt +tab+tab+"try {" + rt +
                    tab+tab+tab+"Method md = " + infce.getSimpleName() + ".class.getMethod(\""+ m.getName()+"\"," + getMethodParamStr+");" + rt +
                    tab+tab+tab+returnStr+ convertStr + "h.invoke(this, md,new Object[] {" +argsValueStr+"});" + rt +
                    tab+tab+"}catch(Exception e) {"+rt+
                    tab+tab+tab+"e.printStackTrace(); " + r + tab+tab+" }"+r+
                    tab+tab+ endReturnStr + r +
                    tab+"}";
        }

        String src =
                "package com.it.proxy ;" +rt +
                "import java.lang.reflect.Method;" +rt +
                "import "+infce.getName()+";" + rt +
                "import com.it.proxy.CInvocationHandler;" + rt +
                "public class $Proxy1 implements " + infce.getSimpleName() + "{" + rt +
                tab + "CInvocationHandler h;" + rt +
                tab + "public $Proxy1(CInvocationHandler h) {" + rt +
                tab + tab+ "this.h = h;" + rt +
                tab + "}" + rt +

                methodStr
                + r +"}"
            ;
//        System.out.println(str);
}

在内存动态生成的类

package com.it.test ;
import java.lang.reflect.Method;
import com.it.dao.MDao;
import com.it.proxy.CInvocationHandler;
public class $Proxy1 implements MDao{
    CInvocationHandler h;
    public $Proxy1(CInvocationHandler h) {
        this.h = h;
    }
    @Override
    public String query(int p0,String p1) {
        try {
            Method md = MDao.class.getMethod("query",new Class[]{ int.class,String.class});
            return  h.invoke(this, md,new Object[] {p0, p1,}).toString();
        }catch(Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

JDK 动态代理原理总结

  • 在程序运行的过程中,根据被代理的接口来动态生成代理类 $Proxy0 的 class 字节码文件
  • 产生的代理类 $Proxy0 继承了 Proxy 类,同时实现了被代理类的接口(如 MDao);所以才能强制将代理对象转换为被代理类的接口的类型(如 MDao),然后可以调用 $Proxy0 中的 query() 方法
  • 而在代理对象 $Proxy0 调用其实现了接口方法(如 query())时,其本质就是使用了代理对象 $Proxy0 调用了 invoke() 方法
  • 而 invoke() 方法的实现是自定义的,如 CustomInvocationHandler类,在这个类中可以处理相应的代理业务逻辑
  • JDK 动态代理不能对类进行代理:因为代理类($Proxy0)已经继承了 Proxy 类,由于 Java 语言只支持单继承,所以 JDK 动态代理不能对类进行代理,只能对接口类型进行代理
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值