动态代理广泛应用于:Spring AOP与RPC框架等场景,动态代理原理是基于反射来实现。
动态代理
动态代理是一种在运行时动态创建代理对象的方式,与静态代理相比,它可以更加灵活地生成代理对象,避免了在编译时就需要确定代理对象的类型的限制。
为什么需要动态代理
按照代理的创建时期,代理类可以分为两种:
静态代理是一种代理模式的实现方式,它在编译期间就已经确定了代理对象,但缺点是需要编写大量的代理类源代码。
所以,动态代理就出现了,它在运行期间动态生成代理对象,无需手动编写代理类,可以减少代码冗余和维护成本。
动态代理使用场景
动态代理常用于:框架中的横向切面Spring AOP实现,比如:添加日志、安全控制、事务控制等。
动态代理在不改变原有代码的基础上对其进行增强的目的,此外,动态代理还可以用于解决远程方法调用RPC等场景。
动态代理的实现方式
Java中实现动态代理主要有两种方式:基于JDK的动态代理和基于CGLIB动态代理。
1.JDK动态代理
基于JDK的动态代理使用Java自带的java.lang.reflect.Proxy类来实现。
1)首先,我们需要定义一个接口
public interface UserService { void save(); }
2)然后,我们定义一个实现该接口的类
public class UserServiceImpl implements UserService { @Override public void save() { System.out.println("save user"); } }
3)接下来,我们需要编写一个InvocationHandler的实现类
该类需要实现invoke方法
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class UserServiceProxy implements InvocationHandler { private Object target; public UserServiceProxy(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("before invoke method"); Object result = method.invoke(target, args); System.out.println("after invoke method"); return result; } }
4)调用接口
import java.lang.reflect.Proxy; public class Main { public static void main(String[] args) { UserService userService = new UserServiceImpl(); UserService proxy = (UserService) Proxy.newProxyInstance( userService.getClass().getClassLoader(), userService.getClass().getInterfaces(), new UserServiceProxy(userService) ); proxy.save(); } }
JDK动态代理的原理是在运行时通过反射机制动态创建一个代理类,然后在运行时将需要代理的目标对象的引用传入代理类中,再通过代理类调用目标对象的方法。
2.CGLib动态代理
基于CGLIB动态代理则是使用CGLIB(Code Generation Library)来动态生成代理类。
相较于JDK动态代理只能代理接口,CGLib可以代理任意的普通类,因此它也被称为是一个高性能的代理框架。
CGLIB动态代理的实现主要依赖于ASM这个开源的Java字节码编辑器和解析器。
如下图所示:
CGLib动态代理的基本原理如下:
- 通过目标类的子类来作为代理类,即生成一个目标类的子类;
- 在代理类中实现对目标类方法的调用,并添加增强逻辑;
- 将代理类的字节码加载到JVM中,并返回代理类实例。
CGLib动态代理的优点是:
- 相对于JDK动态代理,它可以代理任意的普通类;
- 生成代理类的速度相对较快,因为CGLib是直接对字节码进行操作,而不是像JDK动态代理那样需要通过反射来获取和调用目标方法。
CGLib动态代理的缺点是:
- 由于它是通过继承目标类来实现代理的,因此如果目标类被final修饰,则无法被代理;
- 由于它是在内存中构建代理类的字节码并加载类,因此对内存的消耗比较大。
以上就是动态代理详解,更多内容请查看:JDK动态代理详解(实现原理及使用示例)
mikechen
mikechen睿哥,10年+大厂架构经验,资深技术专家,就职于阿里巴巴、淘宝、百度等一线互联网大厂。
关注公众号,获知一线技术干货!
