动态代理源码分析

动态代理

代理模式的解释:为其他对象提供一种代理以控制对这个对象的访问,增强一个类中的某个方法,对程序进行扩展。

比如,现在存在一个UserService类:

public class UserService  {

	public void test() {
		System.out.println("test...");
	}

}

此时,我们new一个UserService对象,然后执行test()方法,结果是显而易见的。

如果我们现在想在不修改UserService类的源码前提下,给test()增加额外逻辑,那么就可以使用动态代理机制来创建UserService对象了,比如:

// 被代理的对象target
UserService target = new UserService();

// 通过cglib技术
Enhancer enhancer = new Enhancer();
// 设置代理哪个类UserService
enhancer.setSuperclass(UserService.class);

// 设置方法拦截器 定义额外逻辑,也就是代理逻辑
enhancer.setCallbacks(new Callback[]{new MethodInterceptor() {
	@Override
    // 当前代理对象正在执行的方法method,入参objects 这里是空
	public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
		System.out.println("before...");
		Object result = methodProxy.invoke(target, objects);
        method.invoke(target,)
		System.out.println("after...");
		return result;
	}
}});

// 动态代理所创建出来的UserService对象
UserService userService = (UserService) enhancer.create();

// 执行这个userService的test方法时,就会额外会执行一些其他逻辑
userService.test();

得到的都是UserService对象,但是执行test()方法时的效果却不一样了,这就是代理所带来的效果。

上面是通过cglib来实现的代理对象的创建,是基于父子类的,被代理类(UserService)是父类,代理类是子类,代理对象就是代理类的实例对象,代理类是由cglib创建的,对于程序员来说不用关心。

CGLIB源码分析

阅读代理

这个对应的Method对象就是UserService这个类的test方法

cglib在58行会拿到那个方法拦截器然后在65行执行方法拦截器的intercept方法。

一开始肯定拿到是个空,所以在60行才会获得真正的方法拦截器,传入的参数是代理对象

传进来的就是UserService的代理对象,然后强转成代理对象,一开始进去if就把false改为true,就不再进来了

ThreadLocal

从当前线程ThreadLocal拿到CALLBACKWIntercept过滤器,然后给属性赋值

后续就可以使用,执行他的intercept方法

在这个方法给ThreadLocal赋值的,那这个方法在哪调用的?

在各种newInstance里面

=============================================================================

newInstance()

newInstance()这个方法在CGLIB提供的Factory接口提供的。相当于某个代理对象UserService实现了Factory工厂

相当于可以拿这个Factory生成另一个代理对象。【用得少】

=============================================================================

其实ThreadLocal都在这个Ehancer的create()方法里设置的

Enhancer是个增强器,目前只是设置了要去产生代理对象的参数。

利用参数去创建代理对象

一开始把传进去的参数封装成对象key【包含了要代理的参数 等下要进行缓存 存到个map里去的】

这个key对应的value就是最后所产生的代理类不是代理对象

根据设置的代理参数,去缓存一个代理类【不可能每次调用都去产生一个代理类 因为参数不会变化】

所以后续create就直接从缓存拿到代理类。

紧接着调用create方法,最终返回结果就是代理对象了

这里传进去的obj就是我们刚才所生成的代理类,

然后传进来的代理类就会执行firstInstance()方法

根据代理类创建我们的代理对象

传进的参数是我们刚才new Enhancer()里面设置的Callback和代理类

这个是我们刚才找的Callback方法

一旦执行这个方法,就会设置进ThreadLocal里面【代理类生成后,代理对象生成之前】

小总结:

传入代理类和你在Enhancer里面设置的方法拦截器传进去,

然后把刚才那个设置ThreadLocal的方法名字,和代理类传进去,找到Method对象,

然后用反射,因为是static,所以obj是null,把方法拦截器座位参数callbacks传进去

最后就成功设置到ThreadLocal这个属性上面去

有值之后,接下来就会产生代理对象,代理对象执行test方法

就会拿到刚才设置的Callback,然后执行Callback里的方法拦截器方法,从而执行到我们所设置的intercept方法里面去

代理类怎么产生的?

这里的obj就是代理类

默认会去找缓存,如果设置不开缓存

那每次都会走gennerate方法去生成代理类。

设置了缓存,直接从缓存里get

拿到了就直接返回。

没有拿到就走createEntry方法。最终还是会调用到生成代理类的那个generate方法

apply()方法

来看看cglib是怎么生成代理类的?

Generate()方法

如果有设置superClass就设置代理类前缀

底层用ASM技术,生成具体的类的名字,构造方法,属性啊 ,类里面各种各样的方法啊【直接去生成字节码 这里其实是经过idea反编译之后 这里其实是生产字节码的指令】

大总结

①首先通过Ehancer对象设置要代理的类【supperClass】,拦截器方法【methodInterceptor】,然后调用create方法,根据这些参数生成一个Object类型的key【包含了要代理的参数 等下要进行缓存 存到个map里去的】这个key对应的value就是最后所产生的代理类不是代理对象】根据设置的代理参数,去缓存一个代理类【不可能每次调用都去产生一个代理类 因为参数不会变化】然后调用create()方法。

代理类哪里来的?

默认会去找缓存,如果设置不开缓存,那每次都会走gennerate方法去生成代理类。如果有设置superClass就设置代理类前缀,底层是用ASM技术,会生成具体的类的名字,构造方法,属性啊 ,类里面各种各样的方法啊【直接去生成字节码 这里其实是经过idea反编译之后 这里其实是生产字节码的指令】

②create()方法里面,会传入代理类【从缓存取或者gennerate】并执行firstInstance()方法。然后会调用代理类的static方法去setThreadLocal【3个参数 new Enhancer()里面设置的方法拦截器和代理类和设置代理类的ThreadLocal的方法名字】,然后用反射,最后就成功把方法拦截器设置到ThreadLocal这个属性上面去。然后根据代理类创建我们的代理对象

最后执行代理对象的test()方法然后就会拿到刚才ThreadLocal里设置的Callback,然后执行Callback里的方法拦截器方法,从而执行到我们所设置的intercept方法里面去,实现对方法的增强

=============

MethodProxy

methodProxy代理的就是你当前正在执行的方法method

代理类里其实是有2个test的方法的,执行test就会执行增强逻辑,

执行CGLIB$test$4就不会执行增强逻辑【直接执行UserService的test方法】。

method其实代理了两个方法【名字是固定的,test方法和CGLIB$test$4方法】

invoke他就只会执行test()方法,invokeSuper就会执行CGLIB$test$4()方法,他不关心你传的对象里有没有这个方法

第二个会报错,因为被代理的对象里头没有CGLIB$test$4这个方法。

但是实际上报错是报的不能转换

换成对象o【(即代理类的实例),这个类是目标类的子类】就能正常执行不报错,相当于执行super.test()只会执行一次。

这个会报错,25行又会执行增强的test方法,里面又会执行方法拦截器,相当于递归了。

MethodProxy对象什么时候创建的?

对象在这里传进去的

代理类生成的时候里面的静态代码块构造出来的

test方法和增强的test方法返回值都是void,所以传一个就好了

构造MethodProxy对象,

用方法名字和返回值生成2个方法签名对象赋值给MethodProxy对象,

createInfo存放代理类和被代理类的信息,MethodProxy只关心test方法,其他方法不关心的。

MethodProxy 对象具体关心的是被代理对象的单个方法。当你创建一个CGLIB代理时,对于被代理对象的每个方法,CGLIB会生成一个相应的MethodProxy实例

在代理对象调用test()方法时候就会用到MethodProxy,就能用它的invoke方法和invokeSuper方法

传进来后用它的invoke和invokeSuper方法

invoke()方法实现

invokeSuper()方法

两个方法很像,区别一个是f1,i1

一个是f2,i2

就是对应的一个是invoke一个是invokeSuper

fastClassInfo这个属性一开始是空的

在init()方法赋值

Init()方法详解

用了MethodProxy对象后就会多2个类出来,这2个类都会继承FastClass类

都是FastClass的代理类

  1. 被代理类的FastClass:这个FastClass是针对原始被代理类生成的。它包含了被代理类所有方法的索引,并能够快速地调用这些方法。
  2. 代理类的FastClass:CGLIB还会为代理类自身生成一个FastClass。由于代理类重写了被代理类的所有方法,在这些重写的方法中通常会调用MethodInterceptor,代理类的FastClass同样包含了这些重写方法的索引,并可以快速调用它们。

如果用反射的话,首先得生成test方法和CGLIB$test$4方法的Method对象再去通过反射调用,效率比较低。

那FastClass如何加速?

FastClass该类会针对UserService的各个方法去返回对应的下标。传入方法签名,拿到下标

helper方法会生成对应的类

然后调用getIndex方法拿到test方法和CGLIB$test$4方法签名的下标索引是什么

找到下标后就设置到fastClassInfo属性里去

然后调用MethodProxy对象的invoke方法时候,就会传入test()方法或者CGLIB$test$4()方法对应的下标。

然后直接调用的你传入对象的test()方法或者CGLIB$test$4()方法,所以会更快

但是invokeSuper方法在执行CGLIB$test$4()方法,也就是会执行你想要代理的那个对象的原本的test方法前会强制转换

强制转换成UserService的代理类【里面才有CGLIB$test$4()方法】

所以这里才会报错转换失败,因为target是你自己new的需要被代理的那个对象,不能把UserService转成UserService的代理类型 ,因为UserService是父类。你只能把UserService的代理类型 转成UserService。

MethodProxy总结

代理类生成的时候里面的静态代码块构造出来的MethodProxy对象,当你用MethodProxy对象调用invoke或者invokeSuper方法的时候,会传入代理对象o或者被代理对象【自己new出来的需要被代理的那个类 比如UserService】和参数,然后调用init()方法,里面的helper方法会生成

2个FastClass的代理类【继承了FastClass的两个类 代理类的FastClass和被代理类的FastClass】,然后传入方法签名【静态代码块的create方法里就赋值好的方法前面 比如test方法】拿到索引下标。然后调用MethodProxy对象的invoke方法时候,就会传入test()方法或者CGLIB$test$4()方法对应的下标,就会去执行下标对应的方法,如果是执行invokeSuper还会强制转换成xxx的代理类型,如果你传的是xxx【父类】强转成子类肯定报错。底层不是用的反射,所以更快叫FastClass

JDK动态代理源码分析

除开cglib技术,jdk本身也提供了一种创建代理对象的动态代理机制,但是它只能代理接口,也就是UserService得先有一个接口才能利用jdk动态代理机制来生成一个代理对象,比如:

public interface UserInterface {
	public void test();
}

public class UserService implements UserInterface {

	public void test() {
		System.out.println("test...");
	}

}

利用JDK动态代理来生成一个代理对象:

UserService target = new UserService();

// UserInterface接口的代理对象   传入类加载器   你要代理的接口 new一个InvocationHandler 里面写你要增强的方法
Object proxy = Proxy.newProxyInstance(UserService.class.getClassLoader(), new Class[]{UserInterface.class}, new InvocationHandler() {
	@Override
    // proxy代理对象,method当前执行的方法,args参数 
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		System.out.println("before...");
		Object result = method.invoke(target, args);
		System.out.println("after...");
		return result;
	}
});

UserInterface userService = (UserInterface) proxy;
userService.test();

如果你把new Class[]{UserInterface.class},替换成new Class[]{UserService.class},允许代码会直接报错:

Exception in thread "main" java.lang.IllegalArgumentException: com.zhouyu.service.UserService is not an interface

表示一定要是个接口。

由于这个限制,所以产生的代理对象的类型是UserInterface,而不是UserService,这是需要注意的。

如何根据接口构造代理对象的?

生成代理类cl

拿到代理类的构造器和Handler对象,传递给cons的构造方法。

把你自己写的Handler传给代理类的构造方法去生成代理对象

代理类如何产生?

从缓存里拿的

key就是要代理的接口

value就是去生成代理类,作为value存起来

key就是ClassLoader,Parameter就是接口

这个key就是最终缓存的key,根据接口生产的subKey

根据接口个数返回key名字

每个ClassLoader有个单独的小的map去缓存某个我这个ClassLoader里面 某个接口所对应的代理类

传入ClassLoader,接口,key,缓存的map都传进去

根据ClassLoader和接口传给这个方法生成代理类最后装进缓存map中。

apply()生产代理类

遍历每个接口,拿到方法,然后包信息啊各种信息拿到后

去生成具体的class文件

会继承Proxy类实现你传进去的那个接口

然后调用代理类的构造方法,传进你写的Handler那个类

调用的是super()也就是JDK本身提供的类,赋值给h属性

然后调用h属性的invoke方法,也就是自己写的那个Handler的invoke方法,把代理对象自己传进去,m3就是接口的test方法的Method对象,test方法没有参数,所以传的null。

JDK动态代理源码总结

ASM技术

可以直接操作字节码,用的字节码指令

javasist这个工具比ASM更好用【Dubbo】

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值