Java动态代理

1 静态代理

1)接口

public interface person{
  void eat();
}

2)被代理类

public class child implements person{
	 void eat(){
	 	System.out.println("小孩吃饭");
	 }
}

3)代理类

public class proxyChild implements person{
	private child c1;
	public proxyChild (child  c1){
		this.c1 = c1;
	}
	void eat(){
		before();
	 	c1.eat();//child的eat函数
	 	after();
	 }
	 void before(){
	 	System.out.println("小孩吃饭前");
	 }
	  void after(){
	 	System.out.println("小孩吃饭后");
	 }
}

缺点:每个代理类只能代理一个类。

2 动态代理

2.1 JDK实现

      利用字节码重组技术,获取传入的被代理对象实现的接口interfaces,增强被代理对象功能的InvocationHandler,来动态生成新的代理类。

2.1.1 InvocationHandler

public class ProxyHandler implements InvocationHandler{
    private Object object;
    public ProxyHandler(Object object){
        this.object = object;
    }
    @Override
    //被代理类调用的函数最终会利用该函数来执行
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //增强逻辑
        System.out.println("Before invoke "  + method.getName());
         //原始child的eat函数
        method.invoke(object, args);
         //增强逻辑
        System.out.println("After invoke " + method.getName());
        return null;
        //可以根据method的名字来进行不同的增强逻辑
        //if("eat".equals(method.getName())){
          //System.out.println("增强1");
          //Object invoke = method.invoke(obj, args);
          //System.out.println("增强1");
      //}else{
       	  //System.out.println("增强2");
          //Object invoke = method.invoke(obj, args);
          //System.out.println("增强2");
      //}
    }
}

2.1.2 动态代理类

class DynamicProxy  {
   public static Object getInatance(Object obj) {
       Class<?>[] interfaces = obj.getClass().getInterfaces();
       Object o = Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new myInvocationHandler(obj));
       //o就是返回的动态生成的代理类
       return o;
   }
}

2.1.3 调用

public class JDKProxy  {
    public static void main(String[] args) {
        UserService userService = (UserService) DynamicProxy.getInatance(new UserServiceImpl());
        userService.login(10);
        //会调用ProxyHandler里面的invoke方法
        userService.register(20);
    }
}

2.1.4 原理

1)动态生成对象

Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new myInvocationHandler(obj));
根据传入的类加载器,被代理类的所有接口,增强功能的类;利用字节码重组生成动态代理类

2)Proxy0:动态代理类。 简单示例

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

final class $Proxy0 extends Proxy implements person{
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);----------**传来的增强句柄**
    }
    
===========================代理类的调用函数=============================
    public final void eat() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
            //  super.h 就是传来的增强句柄
            // 增强句柄中的invoke中加上了增强逻辑的child的eat函数
            //由此可知,通过代理类来调用被代理类的函数,最终会调用InvocationHandler中的invoke函数来执行
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

 public final void walk() throws  {
        try {
            super.h.invoke(this, m4, (Object[])null);
            //  super.h 就是传来的增强句柄
            // 增强句柄中的invoke中加上了增强逻辑的child的eat函数
            //由此可知,通过代理类来调用被代理类的函数,最终会调用InvocationHandler中的invoke函数来执行
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
=====================================================================

    
     public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }
    
    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("person").getMethod("eat", Class.forName("java.lang.String"));
            m4 = Class.forName("person").getMethod("walk", Class.forName("java.lang.String"));
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

2.2 CGlib的动态代理

      通过“继承”可以继承父类所有的公开方法,然后可以重写这些方法,在重写时对这些方法增强,这就是cglib的思想。
生成三个类:

第一个文件:代理类的FastClass类----->根据方法信息,快速找到代理类中的对应方法

第二个文件:代理类,继承自被代理类----->供外界调用

第三个文件:被代理类的FastClass类----->根据方法信息,快速找到被代理类中的对应方法,普通的invoke方法需要意义进行匹配查找

2.2.1 执行流程

1)被代理类

package com.jpeony.spring.proxy.cglib;
 
public class HelloService {
 
    public HelloService() {
        System.out.println("HelloService构造");
    }
 
    /**
     * 该方法不能被子类覆盖,Cglib是无法代理final修饰的方法的
     */
    final public String sayOthers(String name) {
        System.out.println("HelloService:sayOthers>>"+name);
        return null;
    }
 
    public void sayHello() {
        System.out.println("HelloService:sayHello");
    }
}

2)方法拦截器:MethodInterceptor,代理类执行方法会执行这个方法,类似JDK中的InvocationHandler.
    不同的是JDK中的invoke利用反射机制调用被代理类的方法,而CGlib则是利用代理类继承的被代理类的方法来调用的(其中还利用fastClass机制,为每个方法分一个index,将index作为入参执行fastClass中的invoke方法),所以执行速度比较快。
    在调用目标方法时,CGLib会回调MethodInterceptor接口方法拦截,来实现你自己的代理逻辑,类似于JDK中的InvocationHandler接口。

package com.jpeony.spring.proxy.cglib;
 
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
 
import java.lang.reflect.Method;
 
/**
 * 自定义MethodInterceptor
 */
public class MyMethodInterceptor implements MethodInterceptor{
 
    /**
     * sub:cglib生成的代理对象
     * method:被代理对象方法
     * objects:方法入参
     * methodProxy: 代理方法
     */
     
    @Override
    public Object intercept(Object sub, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("======插入前置通知======");
        //invokeSuper是调用父类的方法(被代理类)
        //在调用invokeSuper后,cglib内部会通过代理类的FastClass找到要执行的方法
        Object object = methodProxy.invokeSuper(sub, objects);
        System.out.println("======插入后者通知======");
        return object;
    }
    
}

动态代理类

public class CglibProxyFactory {

    public static Object getProxy(Class<?> clazz) {
        // 创建动态代理增强类
        Enhancer enhancer = new Enhancer();
        // 设置类加载器
        enhancer.setClassLoader(clazz.getClassLoader());
        // 设置被代理类
        enhancer.setSuperclass(clazz);
        // 设置方法拦截器
        enhancer.setCallback(new DebugMethodInterceptor());
        // 创建代理类
        return enhancer.create();
    }
}
HelloService helloService = (HelloService )CglibProxyFactory.getProxy(HelloService .class);
helloService.sayHello("java");

2.2.2 调用过程

1)代理类执行sayHello方法
2)调用方法拦截器的intercept(before–invokeSuper–after)
3)invokeSuper调用CGLIB$sayHello$0(代理类中的同名方法),该方法再调用被代理类的sayHello
invokeSuper通过调用被代理类的FastClass中的invoke方法来运行对应的方法的
在这里插入图片描述

2.2.3 fastClass机制

      CGlib通过继承被代理类生成代理类Class,此外还有另外两个文件,就是为代理类和被代理类生成的FastClass(不是和生成代理类时生成的,而是再第一次调用invoke或者invokeSuper时生成的缓存)。
       普通jdk反射需要根据方法名和参数去查找相应的方法并执行,而falstClass有缓存,可以直接执行 object.targetFun();
1)被代理类的FastClass部分代码如下

public class HelloService$$FastClassByCGLIB$$8656ab6f extends FastClass {
    public HelloService$$FastClassByCGLIB$$8656ab6f(Class var1) {
        super(var1);
    }
=====================================================================
//getIndex方法会为代理类和被代理类的方法分类index,这样每个index会对应到具体方法,在调用时就可以快速找到对应方法
    public int getIndex(Signature var1) {
        String var10000 = var1.toString();
        switch(var10000.hashCode()) {
        case -1488716497:
            if (var10000.equals("sayOthers(Ljava/lang/String;)Ljava/lang/String;")) {
                return 1;
            }
            break;
        }

        return -1;
    }
=====================================================================
//invokeSuper那里,实际上就是用fastClass对象调用的invoke方法,查找是case n,运行相应的方法
    public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
        HelloService var10000 = (HelloService)var2;
        int var10001 = var1;

        try {
            switch(var10001) {
            case 0:
=====================================================================            
                var10000.sayHello();
=====================================================================
                return null;
            case 1:
                return var10000.sayOthers((String)var3[0]);
            case 2:
                return new Boolean(var10000.equals(var3[0]));
            case 3:
                return var10000.toString();
            case 4:
                return new Integer(var10000.hashCode());
            }
        } catch (Throwable var4) {
            throw new InvocationTargetException(var4);
        }
    }
}

2.2.4 代理类

public class Dog$$EnhancerByCGLIB$$4e9e4388 extends Dog implements Factory {
	...............................
	..............................
	..............................
	 final String CGLIB$call$0() {
        return super.call();
    }

    public final String call() {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (this.CGLIB$CALLBACK_0 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        return var10000 != null ? (String)var10000.intercept(this, CGLIB$call$0$Method, CGLIB$emptyArgs, CGLIB$call$0$Proxy) : super.call();
    }
    ..............................
    ..............................
    ..............................
}

2.3 两者对比

  • JDK动态代理是实现了被代理对象的接口,CGlib是继承了被代理对象。
  • 两者都在运行期间生成字节码,JDK方式直接写Class字节码文件,CGlib则是通过ASM框架来写Class,实现更复杂,生成代理类的效率低
  • JDK方式是通过InvocationHandler中的invoke方法,而该方法再通过反射调用被代理类的方法;CGlib则通过FastClass机制来调用方法,故方法调用效率更高

cglib生成代理类慢,方法执行速度略大于jdk,所以比较适合单例模式。spring默认使用jdk动态代理,如果类没有接口,则使用cglib。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值