动态代理是一种运行时生成代理类的机制,能够在代理类中处理被代理类的方法调用。 代理类是自己定义好的,
在程序运行之前就已经编译完成。然而动态代理,代理类并不是在Java代码中定义的,而是在运行时根据我们在
Java代码中的指示动态生成的。相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数进行统一的
处理,而不用修改每个代理类中的方法。
动态代理是一种设计模式,它允许在运行时创建代理对象,并将方法调用重定向到不同的实际对象。它使我们能够
在不修改现有代码的情况下增加或改变某个对象的行为。
动态代理是反射的一个非常重要的应用场景。动态代理常被用于一些 Java 框架中。例如 Spring 的 AOP ,Dubbo
的 SPI 接口,就是基于 Java 动态代理实现的。
动态代理提供了一种灵活且非侵入式的方式,可以对对象的行为进行定制和扩展。它在代码重用、解耦和业务逻辑
分离、性能优化以及系统架构中起到了重要的作用。
增强对象的功能:通过动态代理,可以在不修改原始对象的情况下,对其方法进行增强或添加额外的行为。可
以在方法执行前后进行一些操作,比如日志记录、性能监测、事务管理等。
解耦和业务逻辑分离:动态代理可以将对象的特定操作从业务逻辑中解耦,使得代码更加模块化和可维护。代
理对象可以负责处理一些通用的横切关注点,而业务对象可以专注于核心业务逻辑。
实现懒加载:通过动态代理,可以延迟加载对象,只有在真正需要使用对象时才会进行创建和初始化,从而提
高性能和资源利用效率。
实现远程方法调用:动态代理可以用于实现远程方法调用(RPC)和分布式系统中的服务代理。客户端通过代理
对象调用远程服务,并隐藏了底层网络通信的细节。
实现AOP编程:动态代理是实现面向切面编程(AOP)的基础。通过代理对象,可以将横切关注点(如日志、事
务、安全性)与业务逻辑进行解耦,提供更高层次的模块化和可重用性。
两种方式:
1、JDK动态代理:利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处
理。
2、CGLIB 动态代理,利用ASM(开源的Java字节码编辑库,操作字节码)开源包,对代理对象类的class文件加载
进来,通过修改其字节码生成子类来处理。
Java动态代理和 cglib 比较,区别:
生成代理类技术不同:
java动态代理:jdk自带类ProxyGenerator生成class字节码
cglib:通过ASM框架生成class字节码文件
生成代理类的方式不同:
java动态代理:代理类继承java.lang.reflect.Proxy,实现被代理类的接口
cglib:代理类继承被代理类,实现net.sf.cglib.proxy.Factory
生成类数量不同
java动态代理:生成一个proxy类
cglib:生成一个proxy,两个fastclass类
调用方式不同
java动态代理:代理类->InvocationHandler->反射调用被代理类方法
cglib:代理类->MethodInterceptor->调用索引类invoke->直接调用被代理类方法
JDK代理只能对实现接口的类生成代理;CGlib是针对类实现代理,对指定的类生成一个子类,并覆盖其中
的方法,这种通过继承类的实现方式,不能代理final修饰的类。
在Java中,可以使用Java的反射机制来实现动态代理。
Java提供了 java.lang.reflect.Proxy 类和 java.lang.reflect.InvocationHandler 接口来实现动态代理。
package com.example.dynamicproxy.jdkproxy;
/**
* 目标接口类
*/
public interface UserManager {
void addUser(String id, String password);
void delUser(String id);
}
package com.example.dynamicproxy.jdkproxy;
/**
* 接口实现类
*/
public class UserManagerImpl implements UserManager {
@Override
public void addUser(String id, String password) {
System.out.println("调用了UserManagerImpl.addUser()方法!");
}
@Override
public void delUser(String id) {
System.out.println("调用了UserManagerImpl.delUser()方法!");
}
}
实现InvocationHandler接口重写invoke方法。
package com.example.dynamicproxy.jdkproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* JDK动态代理类
* 实现InvocationHandler接口
*/
public class UserProxy implements InvocationHandler {
/**
* 需要代理的目标对象
*/
private Object targetObject;
public Object newProxy(Object targetObject) {
// 将目标对象传入进行代理
this.targetObject = targetObject;
// 返回代理对象
return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), targetObject.getClass().getInterfaces(), this);
}
// invoke方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("动态代理前置操作");
// 进行逻辑处理的函数
checkPopedom();
// 调用invoke方法
Object ret = method.invoke(targetObject, args);
System.out.println("动态代理后置操作");
return ret;
}
private void checkPopedom() {
// 模拟检查权限
System.out.println("检查权限:checkPopedom()!");
}
}
package com.example.dynamicproxy.jdkproxy;
public class JdkDynamicProxyExample {
public static void main(String[] args) {
// 创建目标对象
UserManager userService = new UserManagerImpl();
// 创建InvocationHandler实例
UserProxy userProxy = new UserProxy();
UserManager proxy = (UserManager) userProxy.newProxy(userService);
proxy.addUser("admin","admin");
proxy.delUser("admin");
}
}
# 程序输出
动态代理前置操作
检查权限:checkPopedom()!
调用了UserManagerImpl.addUser()方法!
动态代理后置操作
动态代理前置操作
检查权限:checkPopedom()!
调用了UserManagerImpl.delUser()方法!
动态代理后置操作
CGlib不像是JDK动态代理,CGlib需要导入Jar包,那么我用SpringBoot直接导入依赖。
<dependency>
<groupId>cglibgroupId>
<artifactId>cglibartifactId>
<version>3.3.0version>
dependency>
package com.example.dynamicproxy.cglibproxy;
public class UserServiceImpl {
public void addUser() {
System.out.println("添加了一个用户");
}
public void deleteUser() {
System.out.println("删除了一个用户");
}
}
创建代理类,里面主要有2个方法,一个为获取代理的实例方法,另一个为需要重写的方法,此类需要实现
MethodInterceptor接口,该接口很简单,此包含一个方法intercept。
package com.example.dynamicproxy.cglibproxy;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class UserServiceCGlib implements MethodInterceptor {
/**
* 指定cglib代理模式的代理类
*/
private Object target;
public UserServiceCGlib(){
}
public UserServiceCGlib(Object target) {
this.target = target;
}
/**
* 返回一个代理对象,是target对象的代理对象
* 在创建实例前,我们需要使用一个叫Enhancer的对象,此对象用来创建代理对象,设置目标类为超类后的同时需要回调当前类
*
* @return
*/
public Object getProxyInstance() {
// 1. 创建一个工具类
Enhancer enhancer = new Enhancer();
// 2. 设置父类
enhancer.setSuperclass(target.getClass());
// 3. 设置回调函数
enhancer.setCallback(this);
// 4. 创建子类对象,即代理对象
return enhancer.create();
}
/**
* @param o 表示目标对象
* @param method 表示当前调度的方法
* @param objects 表示执行方法的参数列表
* @param methodProxy 表示执行目标方法的代理对象
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
// 注意,这里传入的o是代理对象,而不是目标类对象
System.out.println("增强开始~~~");
// invokerSuper或许可以理解为调用目标对象方法
Object result = methodProxy.invokeSuper(o, objects);
System.out.println("增强结束~~~");
return result;
}
}
package com.example.dynamicproxy.cglibproxy;
public class CglibDynamicProxyExample {
public static void main(String[] args) {
UserServiceCGlib serviceCGlib = new UserServiceCGlib(new UserServiceImpl());
UserServiceImpl userService = (UserServiceImpl) serviceCGlib.getProxyInstance();
userService.addUser();
userService.deleteUser();
}
}
# 程序输出
增强开始~~~
添加了一个用户
增强结束~~~
增强开始~~~
删除了一个用户
增强结束~~~
package com.example.dynamicproxy.cglibproxy;
import net.sf.cglib.proxy.Enhancer;
public class CglibDynamicProxyExample1 {
public static void main(String[] args) {
//c生成一个增强器
Enhancer enhancer = new Enhancer();
// 设置要代理的类
enhancer.setSuperclass(UserServiceImpl.class);
// 设置回调的拦截器数组
enhancer.setCallback(new UserServiceCGlib());
// 创建代理对象
UserServiceImpl userService= (UserServiceImpl) enhancer.create();
userService.addUser();
userService.deleteUser();
}
}
# 程序输出
增强开始~~~
添加了一个用户
增强结束~~~
增强开始~~~
删除了一个用户
增强结束~~~
到这里相信各位小伙伴们已经基本掌握了JDK动态代理和CGlib动态代理的区别和实现。
那么,这两个动态代理的使用场景是什么呢?
答案:Spring AOP
以下是Spring AOP创建代理的方法:
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
//如果
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理。
2、如果目标对象实现了接口,也可以强制使用CGLIB。
3、如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换。
如果需要强制使用CGLIB来实现AOP,需要配置spring.aop.proxy-target-class=true或
@EnableAspectJAutoProxy(proxyTargetClass = true)
1、JDK代理使用的是反射机制实现aop的动态代理,CGLIB代理使用字节码处理框架asm,通过修改字节码生成子
类。所以jdk动态代理的方式创建代理对象效率较高,执行效率较低,cglib创建效率较低,执行效率高;
2、JDK动态代理机制是委托机制,具体说动态实现接口类,在动态生成的实现类里面委托hanlder去调用原始实现
类方法,CGLIB则使用的继承机制,具体说被代理类和代理类是继承关系,所以代理类是可以赋值给被代理类的,
如果被代理类有接口,那么代理类也可以赋值给接口。
动态代理在许多地方都有用处,比如日志记录、性能监测、权限验证等。这种动态代理的设计模式使得我们能够以
一种非侵入式的方式对对象的行为进行定制和扩展,提供了更高的灵活性和可维护性。