一: 前言:
代理模式分为 静态代理 和 动态代理,今天我要讲的是动态代理的两种常见、也是被广泛使用的实现方式-------jdk动态代理 和 Cglib动态代理
二:Jdk动态代理实现分析:
结构示意图如下,我定义了一个UserService接口,有一个getUserName方法。然后定义了一个实现类UserServiceimpl实现该接口。
JDK动态代理步骤
1.创建一个实现接口InvocationHandler的类,它必须实现invoke方法
2.创建被代理的类以及接口
3.通过Proxy的静态方法newProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler h)创建一个代理
4.通过代理调用方法
这里就直接上代码了。
UserService 代理的接口对象
public interface UserService {
public void getUserName();
}
UserServiceimpl 代理的实现类
public class UserServiceimpl implements UserService{
@Override
public void getUserName() {
System.out.println("老墨,强哥说他想吃鲨鱼");
}
}
JDKProxy
public class JDKProxy implements InvocationHandler {
Object target;
public JDKProxy(Object target) {
this.target = target;
}
/**
* 该方法负责集中处理动态代理类上的所有方法调用。
* 调用处理器根据这三个参数进行预处理或分派到委托类实例上反射执行
*
* @param proxy 代理类实例
* @param method 被调用的方法对象
* @param args 调用参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 前置增强方法
System.out.println("告诉老墨我想吃鱼了");
// 执行方法
Object invoke = method.invoke(target, args);
// 后置增强方法
System.out.println("好勒,我待会给强哥送去");
return invoke;
}
}
main 方法
public static void main(String[] args) throws Exception{
// 创建实体类对象
UserService userService=new UserServiceimpl();
// 创建一个与代理对象相关联的InvocationHandler
InvocationHandler UserHandler= new JDKProxy(userService);
System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles","true");
//创建一个代理对象stuProxy来代理UserHandler,代理对象的每个执行方法都会替换执行Invocation中的invoke方法
UserService jdkUserService= (UserService)Proxy.newProxyInstance(UserService.class.getClassLoader(), new Class<?>[]{UserService.class},UserHandler);
// 代理执行方法
jdkUserService.getUserName();
}
打印:
告诉老墨我想吃鱼了
老墨,强哥说他想吃鲨鱼
好勒,我待会给强哥送去
进程已结束,退出代码0
可以看出这一行代码是我们jdk动态代理的关键代码,那么这一行代码到底做了什么呢?
UserService jdkUserService= (UserService)Proxy.newProxyInstance(UserService.class.getClassLoader(), new Class<?>[]{UserService.class},UserHandler);
我们通过System.getProperties().put(“jdk.proxy.ProxyGenerator.saveGeneratedFiles”,“true”);这一行代码会生产临时对象的二进制文件,通过反编译成java文件。
Proxy0 代理对象
/**
*
*/
public final class $Proxy0 extends Proxy implements UserService {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
/**
* 首先我们看到他有一个有参构造方法,InvocationHandler var1,生产代理对象时,通过构造方法传递一个InvocationHandler 对象。
*/
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
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 void getUserName() throws {
try {
super.h.invoke(this, m3, (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("byprocuration.spring.service.UserService").getMethod("getUserName");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
1.Proxy0 实现了UserService 接口,继承了Proxy 类
2. 有一个构造方法,通过构造方法传递一个InvocationHandler 对象,而这个对象就是我们实现InvocationHandler 接口的JDKProxy。并给父类Proxy的h属性赋值。
protected InvocationHandler h;
protected Proxy(InvocationHandler h) {
Objects.requireNonNull(h);
this.h = h;
}
- 有一大堆变量m0,m1,m2,m3,他们对应的就是方法名称。
- 当我们调用getUserName()方法时,他会调用super.h.invoke(this, m3, (Object[])null)时,其实也就是调用我们定义的JDKProxy的invoke方法。
总结:
- 在我们采用jdk动态代理时,jdk为我们生成了$Proxy0 代理类,这个类文件是放在内存中的,我们在创建代理对象时,就是通过反射获得这个类的构造方法,然后创建的代理实例。
- 当我们调用getUserName方法时,他是会先调用父类属性h的invoke方法。而在实例化时,会把我们定义的JDKProxy给h进行赋值。
- 我们会发现$Proxy0是需要实现UserService 接口的,也就相当于JDK动态代理是基于接口进行代理的。
3.Cglib动态代理实现分析:
使用Cglib代理需要先导入asm版本包,在实现MethodInterceptor接口
cglibProxy
public class cglibProxy implements MethodInterceptor {
private Enhancer enhancer = new Enhancer();
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("告诉老墨我想吃鱼了");
Object result = methodProxy.invokeSuper(o,objects);
System.out.println("好勒,我待会给强哥送去");
return result;
}
public Object getProxy(Class clazz){
//设置创建子类的类,即指定为哪个类产生代理类
enhancer.setSuperclass(clazz);
/*设置回调函数
* setCallback设置被代理类的public非final方法被调用时的处理类
* */
enhancer.setCallback(this);
//创建子类实例
return enhancer.create();
}
}
执行main方法调用
public static void main(String[] args) {
// 实例化CglibProxy对象
cglibProxy cglibProxy=new cglibProxy();
// //获取代理对象
UserServiceimpl cglibProxyUserService =(UserServiceimpl)cglibProxy.getProxy(UserServiceimpl.class);
cglibProxyUserService.getUserName();
}
打印:
告诉老墨我想吃鱼了
老墨,强哥说他想吃鲨鱼
好勒,我待会给强哥送去
Cglib是通过Enhancer来创建代理类对象。且采用了非常底层的字节码技术( ASM),其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。
Enhancer是cglib中使用频率很高的一个类,它是一个字节码增强器,可以用来为无接口的类创建代理。它的功能与java自带的Proxy类挺相似的。它会根据某个给定的类创建子类,并且所有非final的方法都带有回调钩子。
可以查看这篇文章去了解
总结:
最后我们总结一下JDK动态代理和Gglib动态代理的区别:
1.JDK动态代理是实现了被代理对象的接口,Cglib是继承了被代理对象。
2.JDK和Cglib都是在运行期生成字节码,JDK是直接写Class字节码,Cglib使用ASM框架写Class字节码,Cglib代理实现更复杂,生成代理类比JDK效率低。
3.JDK调用代理方法,是通过反射机制调用,Cglib是通过FastClass机制直接调用方法,Cglib执行效率更高
。