java的动态代理

目录

概述

静态代理

动态代理

基于接口代理(jdk)

原理解析

基于继承代理(cglib)

Java字节码生成开源框架–ASM:


概述


AOP用到了两种动态代理来实现织入功能:

  • jdk动态代理
  • cglib动态代理

比较:

  • jdk动态代理是由java内部的反射机制来实现的,cglib动态代理底层则是借助asm来实现的。反射机制在生成类的过程中比较高效,而asm在生成类之后的相关执行过程中比较高效(可以通过将asm生成的类进行缓存,这样解决asm生成类过程低效问题)。
  • jdk动态代理的应用前提是目标类必须基于统一的接口。因此,jdk动态代理有一定的局限性,cglib这种第三方类库实现的动态代理应用更加广泛,且在效率上更有优势。

静态代理

  • 建一个接口,接口中定义相应(代理对象和目标对象都要实现)的 方法
public interface Subject{
void test();
}

  • 目标类(实现上述接口)
public class RealSubject implements Subject{
    @Override
    public void tset(){
    System.out.printin("target method");
    }
}

  • 代理类(实现上述接口)
public class Proxy implements Subject{
    private RealSubject realSubject;
    
    public Proxy(RealSubject realSubject){
    this.realSubject = realSubject;
    }


    @Override
    public void test(){
    System.out.printin("Before");
    try{
    realSubject.test();
    }catch(Exception e){
    System.out.println("ex:"+e.getmessage());
    throw e;
    }finally{
    System.out.println("After");
    }
}
}

  • 客户端调用
public class Client{
public static void main(String[] args){
//通过接口
Subject subject = new Proxy(new RealSubject());
subject.test();
}
}


动态代理

静态代理缺点:不灵活,重复代码多

基于接口代理(jdk)

  • 类:java.lang.reflect.Proxy(通过该类动态生成代理类)
  • 代理类实现接口:InvocationHandler
  • jdk代理只能基于接口动态代理(因为生成的proxy class中,继承了Proxy类,实现了需要代理的接口,而Java是单继承,多实现的处理方式)

动态代理类

  • 实现接口的方法
  • 引用目标方法
  • 构造器注入目标方法
  • 通过反射调用目标方法
  public class JdkProxySubject implements InvocationHandler{

    
//引入要代理的真实对象
    private RealSubject realSubject;
    
    //用构造器注入目标方法,给我们要代理的真实对象赋初值
    public JdkProxySubject(RealSubject realSubject){
       this.realSubJect=realSubject;
    }
    //实现接口的方法
    @Override
    public Object invoke(Object proxy,Method method,Object[] args)throws Throwable{
        System.out.println("before");
        Object result = null;
        try{
        //调用目标方法
        //利用反射构造目标对象
        //    当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
        result=method.invoke(realSubject,args);
        
        }catch(Exception e){
        System.out.println("ex:"+e.getMessage());
        throw e; 
        }finally{
            System.out.println("after");
        }
        return result;
    }
    }


客户端调用(使用Proxy)
 

  public class Client{
  public static void main(String[] args){
    //使用Proxy构造对象
    //参数
    //java泛型需要转换一下
    // 通过Proxy的newProxyInstance方法来创建我们的代理对象,我们来看看其三个参数
    //     * 第一个参数 getClassLoader() ,我们这里使用Client这个类的ClassLoader对象来加载我们                
    //        的代理对象
    //     * 第二个参数表示我要代理的是该真实对象,这样我就能调用这组接口中的方法了
    //     * 第三个参数handler,我们这里将这个代理对象关联到了上方的 InvocationHandler这个对象上
    

    Subject subject = 
       (Subject)java.lang.reflect.Proxy.newProxyInstance(Client.class.getClassLoader(),
                new Class[]{Subject.class},new JdkProxySubject(new RralSubject()));
    //调用方法
     subject.test;
    }
    }


原理解析


过程:调用Proxy.newProxyInstance生成代理类的实现类:

  • 调用getProxyClass0寻找或生成指定代理类
  • (从缓存中取,如果没有,就生成一个放在缓存中 : 通过ProxyClassFactory生成)
  • 缓存调用ProxyClassFactory生成代理类,proxy class的生成最终调用ProxyClassFactory的apply方法.
ProxyGenerator.generateProxyClass使用生成的代理类的名称,接口,访问标志生成proxyClassFile字节码
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
            try {
                return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);
            } catch (ClassFormatError e) {
                throw new IllegalArgumentException(e.toString());
            }

  • 生成字节码之后利用反射生成实例:   Proxy.newProxyInstance 创建的代理对象是在jvm运行时动态生成的一个对象,它并不是我们的InvocationHandler类型,也不是我们定义的那组接口的类型,而是在运行是动态生成的一个对象,并且命名方式都是这样的形式,以$开头,proxy为中,最后一个数字表示对象的标号。

InvocationHandler接口:

  • 每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用。

我们来看看InvocationHandler这个接口的唯一一个方法 invoke 方法:

Object invoke(Object proxy, Method method, Object[] args) throws Throwable
我们看到这个方法一共接受三个参数,那么这三个参数分别代表什么呢?

  • -proxy:  指代我们所代理的那个真实对象
  • -method:  指代的是我们所要调用真实对象的某个方法的Method对象
  • -args:  指代的是调用真实对象某个方法时接受的参数

Proxy类

  • Proxy这个类的作用就是用来动态创建一个代理对象的类,它提供了许多的方法,但是我们用的最多的就是 newProxyInstance 这个方法:
  • 这个方法的作用就是得到一个动态的代理对象,其接收三个参数,我们来看看这三个参数所代表的含义:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
  • loader:  一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载
  • interfaces:  一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法
  • h:  一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上

基于继承代理(cglib)

  • 代理类
 public class CglibMethodInterceptor implements MethodInterceptor{//主要的方法拦截类,它是Callback接口的子接口,需要用户实现

        @Override
        public Object intercept(Object obj,Method method,Object
        [] args,MethodProxy proxy )throws Throwable{
        System.out.println("before cgplib");
        Object result = null;
            try{
               //利用反射创建代理对象 
               result = proxy.invokeSuper(obj,args);
            }catch(Exception e){
            System.out.println("ex:"+e.getMessage());
                throw e;
            }finally{
                System.out.println("after cglib");
            }
            return result;
            }
        }
    

  • 调用类
 public class Client{
    public static void main(String[] args){
    // 主要的增强类
    Enhancer enhancer=new Enhancer;
    //  目标类 , 设置父类,被增强的类
    enhancer.setSuperclass(RealSubject.class);
    // 回调对象
    enhancer.setCallback(new CglibMethodInterceptor());
    //生成代理类对象,用cglibProxy来增强RealSubject
    Subject subject=enhancer.create();
    subject.test();
    }
}


原理解析
Cglib是一个优秀的动态代理框架,它的底层使用ASM在内存中动态的生成被代理类的子类,使用CGLIB即使代理类没有实现任何接口也可以实现动态代理功能。CGLIB具有简单易用,它的运行速度要远远快于JDK的Proxy动态代理:
CGLIB的核心类:

net.sf.cglib.proxy.Enhancer – 主要的增强类
net.sf.cglib.proxy.MethodInterceptor – 主要的方法拦截类,它是Callback接口的子接口,需要用户实现
net.sf.cglib.proxy.MethodProxy – JDK的java.lang.reflect.Method类的代理类,可以方便的实现对源对象方法的调用,如使用:
Object o = methodProxy.invokeSuper(proxy, args);//虽然第一个参数是被代理对象,也不会出现死循环的问题。
-net.sf.cglib.proxy.MethodInterceptor接口是最通用的回调(callback)类型,它经常被基于代理的AOP用来实现拦截(intercept)方法的调用。这个接口只定义了一个方法

public Object intercept(Object object, java.lang.reflect.Method method,
Object[] args, MethodProxy proxy) throws Throwable;


第一个参数是代理对像,第二和第三个参数分别是拦截的方法和方法的参数。原来的方法可能通过使用java.lang.reflect.Method对象的一般反射调用,或者使用 net.sf.cglib.proxy.MethodProxy对象调用。net.sf.cglib.proxy.MethodProxy通常被首选使用,因为它更快。


Java字节码生成开源框架–ASM:


ASM 是一个 Java 字节码操控框架。它能够以二进制形式修改已有类或者动态生成类。ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。ASM 从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类。
不过ASM在创建class字节码的过程中,操纵的级别是底层JVM的汇编指令级别,这要求ASM使用者要对class组织结构和JVM汇编指令有一定的了解。

相关文档:Java JDK 动态代理(AOP)使用及实现原理分析_衣舞晨风的博客-CSDN博客_动态代理

相关文档:java动态代理_飞!!!!的博客-CSDN博客_java动态代理
相关文档:Java动态代理的实现原理

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值