我是一名很普通的双非大三学生。我会坚持写博客,输出知识的同时巩固自己的基础,记录自己的成长和锻炼自己,奥利给!!
今年找个21届实习真的好难呀,擦干眼泪,,埋头苦干,可能你们看到这篇文章的时候,已经过去很久了。
我们大家都直接或者间接的使用过动态代理,无论是日志框架还是Spring 框架,它们都包含了动态代理。
代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。主要有静态代理和动态代理,静态代理这里就不赘述了。
动态代理是程序在运行期间动态构建代理对象和动态调用代理方法的一种机制。
这篇文章我们就来学习一下, 如何实现动态代理?JDK Proxy 和 CGLib 有什么区别?
说到动态代理,我想大家脑子里可能第一个蹦出来的词是**反射**,动态代理的常用实现方式是反射。
注意!是常用实现方式,不是所有的代理都是反射实现的哦~,**反射机制是指什么呢?**
是指程序在运行期间可以访问、检测和修改其本身状态或行为的一种能力,使用反射我们可以调用任意一个类对象,以及类对象中包含的属性及方法。
更多反射内容可看这篇文章 => 万字总结之反射(框架之魂)
但动态代理不止有反射一种实现方式,例如,动态代理很出名的CGLib,CGLib就是基于ASM(一个Java字节码操作框架)而非反射实现的。简单来说,动态代理是一种行为方式,而 反射或ASM只是它的一种实现手段 而已。
JDK Proxy 和 CGLib 的区别主要体现在以下几个方面:
### JDK 动态代理实现 JDK Proxy 动态代理的实现无需引用第三方类,只需要实现 InvocationHandler 接口,重写 invoke() 方法即可,整个实现代码如下所示:
public interface HelloService { void sayHello(String name); void sayHello1(String name); } public class HelloServiceImpl implements HelloService { @Override public void sayHello(String name) { System.out.println("Hello" + name); } @Override public void sayHello1(String name) { System.out.println("Hello" + name); } } public class HelloServiceInvocationHandler implements InvocationHandler { private Object target; public Object bind(Object target){ this.target = target; return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("############我是jdk动态代理##############"); System.out.println("我准备说hello"); Object result = method.invoke(target,args); System.out.println("我说过hello了"); return result; } } public class HelloServiceJdkMain { public static void main(String[] args) { HelloServiceInvocationHandler helloServiceInvocationHandler = new HelloServiceInvocationHandler(); HelloService proxy = (HelloService) helloServiceInvocationHandler.bind(new HelloServiceImpl()); proxy.sayHello("张三"); proxy.sayHello1("李四"); } } 复制代码
运行结果如下: 可以看出 JDK Proxy 实现动态代理的核心是实现 Invocation 接口,一般主要涉及到以下两个类 InvocationHandler
h和 Proxy
我们查看 Invocation 的源码,会发现里面其实只有一个 invoke() 方法,源码如下:
public interface InvocationHandler { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable; } 复制代码
第一个参数 proxy
一般是指代理类, method
是被代理的方法, args
为该方法的参数数组。
Proxy
即为动态代理类
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 复制代码
获得一个代理类,其中loader是类装载器,interfaces是真实类所拥有的 全部接口的数组 。返回代理类的一个实例,返回后的代理类可以当作被代理类使用。
使用 CGLib 的话,我们需要在项目中引入 CGLib 框架,在 pom.xml 中添加如下配置:
<dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.2.5</version> </dependency> 复制代码
CGLib 实现代码如下:
public class HelloServiceMethodInterceptor implements MethodInterceptor { private Object target; public HelloServiceMethodInterceptor(Object target) { this.target = target; } @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("我是cglib动态代理"); /** * 这里需要注意invoke和invokeSuper的区别 * 建议看这篇文章:https://juejin.im/post/5a8f750af265da4e983f2369 */ methodProxy.invokeSuper(o,objects); return null; } } public class HelloServiceCglibMain { public static void main(String[] args) { // 输出cglib生成的class文件 // System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D://proxy"); HelloServiceImpl helloService = new HelloServiceImpl(); Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(HelloServiceImpl.class); enhancer.setCallback(new HelloServiceMethodInterceptor(helloService)); helloService = (HelloServiceImpl) enhancer.create(); helloService.sayHello("11"); } } 复制代码
运行截图: 从上述的两个简单案例来看, CGLib 和 JDK Proxy 的实现代码是比较类似的,都是通过实现代理器的接口,再调用某一个方法完成动态代理的。
不同的是:
总的来说,JDK 动态代理利用接口实现代理,CGLIB 动态代理利用继承的方式实现代理。
静态代理其实就是事先写好代理类,可以手工编写也可以使用工具生成,但它的缺点是每个业务类都要对应一个代理类,特别不灵活也不方便,于是就有了动态代理。 动态代理的常见使用场景有 RPC 框架的封装、 AOP(面向切面编程)的实现(高频面试题) 、JDBC 的连接等。
Spring 框架中同时用到了两种动态代理 JDK Proxy 和 CGLib,当 Bean 实现了接口时,Spring 就会使用 JDK Proxy,在没有实现接口时就会使用 CGLib。