面对新鲜事物,我们要先了解在去探索事物的本质-默
目录
一.介绍二者代理模式
1.1.Jdk代理模式
1.2cglib代理模式
1.3二者区别
1.3.1有无接口
1.3.2灵活性
1.4对于两种代理模式的总结
1.4.1jdk代理模式
1.4.2cglib代理模式
二.两种代理模式应用场景
2.1jdk代理模式应用场景
2.1.1基于接口的代理
2.1.2细粒度的方法拦截
2.2cglib代理模式应用场景
2.2.1非基于接口的代理
2.2.2覆盖父类中的方法
2.2.3高性能的代理
三.代码演示
3.1jdk代理模式
3.1.1源码
3.2思路解释
3.2cglib代理模式
3.2.1源码
3.2.1思路解释
一.介绍二者代理模式
1.1.Jdk代理模式
JDK代理是通过接口实现的动态代理方式。当目标类实现了至少一个接口时,Spring AOP会使用JDK代理。JDK代理通过在运行时创建一个实现了目标接口的代理类来实现代理功能。代理对象和目标对象实现了同一个接口,因此只能代理接口中定义的方法。
1.2cglib代理模式
CGLIB代理是通过继承实现的动态代理方式。当目标类没有实现任何接口时,Spring AOP会使用CGLIB代理。CGLIB代理通过在运行时创建目标类的子类来实现代理功能。代理对象继承自目标对象,并覆盖了目标对象的非final方法,因此可以代理目标对象的所有方法。
1.3二者区别
1.3.1有无接口
JDK代理要求目标类必须实现接口,而CGLIB代理不需要,可以代理普通的Java类。
1.3.2灵活性
JDK代理基于接口,适用于对接口的代理,更加灵活,但无法代理没有接口的类。
CGLIB代理是通过生成目标类的子类来实现代理,适用于对类的代理。CGLIB代理具有更高的性能,因为它不需要通过反射调用目标对象的方法。
1.4对于两种代理模式的总结
1.4.1jdk代理模式
JDK 动态代理是基于接口的代理方式。它通过创建一个实现了目标接口的代理类,并在代理类中实现代理逻辑。代理类在运行时动态生成,并在其中调用原始对象的方法
JDK 动态代理通过 java.lang.reflect.Proxy
类和 java.lang.reflect.InvocationHandler
接口来实现。代理对象是在运行时动态生成的。
无法代理非公有类:JDK 动态代理不能代理那些声明为 final 的类,因为 final 类不能被继承。另外,由于代理类是在运行时动态生成的,因此也无法代理那些没有默认构造函数的类。
方法拦截:代理对象在调用方法时,会将方法调用转发给 InvocationHandler
的实现类,在这个实现类中可以添加自定义的逻辑,完成方法的拦截、增强等操作。
性能相对较低:相比于 CGLIB 动态代理,JDK 动态代理的性能较低。这主要是因为 JDK 动态代理需要通过反射来调用目标对象的方法。
总的来说,JDK 动态代理是一种比较常用的代理方式,适用于代理接口的场景,易于使用和理解。但它的局限性在于只能代理实现了接口的类,并且在性能方面稍逊于 CGLIB 动态代理。
1.4.2cglib代理模式
CGLIB 动态代理是一种基于继承的代理方式。它通过创建目标类的子类来实现代理。代理类继承自目标类,并重写其中的方法,在重写的方法中添加了增强逻辑。
类代理:CGLIB 动态代理可以代理类,无论是否实现了接口。这使得它可以代理那些没有实现接口的类,不包括 final 类。
代理生成:CGLIB 动态代理使用字节码生成库来创建目标类的子类。代理类在运行时动态生成,并且不需要目标类实现任何接口。
方法拦截:被代理的方法在执行时,会调用代理类中的方法。这样,我们可以在代理类中添加拦截器(MethodInterceptor
),并在拦截器中实现自定义的逻辑。
性能相对较高:相比于 JDK 动态代理,CGLIB 动态代理的性能更高。这是因为 CGLIB 通过继承来实现代理,避免了反射调用的开销。
对于 final 方法和私有方法的限制:CGLIB 默认无法代理 final 方法,因为 final 方法无法被重写。此外,CGLIB 也不能代理私有方法,因为代理类无法访问目标类的私有方法。但是可以通过设置 CGLIB 的 Enhancer 对象的 setUseReflection(true)
方法来强制代理私有方法。这样一来,CGLIB 将使用反射调用私有方法,但这可能会导致性能下降。
需要注意的是,CGLIB 代理依赖于字节码生成,在代理类生成时会占用一定的时间和内存。
总结来说,CGLIB 动态代理适用于代理类的场景,可以代理没有实现接口的类,并且具有较高的性能。
二.两种代理模式应用场景
2.1jdk代理模式应用场景
2.1.1基于接口的代理
如果目标对象实现了接口,可以使用JDK动态代理。它要求目标对象实现一个接口,并且生成的代理对象也会实现相同的接口。
2.1.2细粒度的方法拦截
JDK动态代理可以在目标对象的方法调用前后添加额外的逻辑。例如,在方法调用前打印日志、进行权限验证或事务管理等
2.2cglib代理模式应用场景
2.2.1非基于接口的代理
CGLIB动态代理可以代理没有实现接口的类。因此,当目标对象没有实现任何接口时,可以使用CGLIB动态代理。
2.2.2覆盖父类中的方法
CGLIB动态代理通过继承目标类并覆盖其中的方法来创建代理对象。这使得它可以代理目标类中的非公有方法。
2.2.3高性能的代理
相比JDK动态代理,CGLIB动态代理通常具有更高的性能,因为它直接操作字节码而不需要通过反射调用方法。
三.代码演示
3.1jdk代理模式
3.1.1源码
package com.daili.jdk;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @author lz
* @create 2023-08-21 9:33
*/
public interface UserService {
void saveUser(String username);
}
class UserServiceImpl implements UserService {
public void saveUser(String username) {
System.out.println("Saving user: " + username);
}
}
class UserServiceProxy implements InvocationHandler {
private Object target;
public UserServiceProxy(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before method: " + method.getName());
Object result = method.invoke(target, args);
System.out.println("After method: " + method.getName());
return result;
}
}
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.saveUser("刘兵");
}
}
3.2思路解释
- 定义了一个接口
UserService
,其中包含了一个抽象方法saveUser(String username)
。 - 实现了接口
UserService
的具体类UserServiceImpl
,该类实现了saveUser
方法,用于保存用户信息。 - 创建了一个代理类
UserServiceProxy
,实现了InvocationHandler
接口。在invoke
方法中,对目标方法进行前置和后置处理,即在目标方法执行前输出 "Before method: " + 方法名,在目标方法执行后输出 "After method: " + 方法名。 - 在
Main
类的main
方法中,创建了一个UserServiceImpl
的实例。 - 通过
Proxy.newProxyInstance()
方法创建代理对象,传入目标对象的类加载器、接口数组和UserServiceProxy
的实例。返回的代理对象将会实现UserService
接口。 - 调用代理对象的
saveUser("John")
方法,实际上会触发代理对象的invoke
方法,从而实现了动态代理。
在代码执行过程中,代理对象的 invoke
方法会先输出 "Before method: saveUser",然后调用目标对象的 saveUser
方法,最后输出 "After method: saveUser"。
通过 JDK 动态代理,可以在不修改目标对象代码的情况下,对其方法进行增强、添加日志等操作。
3.2cglib代理模式
3.2.1源码
package com.daili.cglib;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* @author lz
* @create 2023-08-21 9:49
*/
public class UserService {
public void saveUser(String username) {
System.out.println("Saving user: " + username);
}
}
class UserServiceInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("Before method: " + method.getName());
Object result = proxy.invokeSuper(obj, args);
System.out.println("After method: " + method.getName());
return result;
}
static class Main {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserService.class);
enhancer.setCallback(new UserServiceInterceptor());
UserService proxy = (UserService) enhancer.create();
proxy.saveUser("刘兵");
}
}
}
3.2.1思路解释
- 定义了一个类
UserService
,其中包含了一个方法saveUser(String username)
,用于保存用户信息。 - 创建了一个代理类
UserServiceInterceptor
,实现了MethodInterceptor
接口。在intercept
方法中,对目标方法进行前置和后置处理,即在目标方法执行前输出 "Before method: " + 方法名,在目标方法执行后输出 "After method: " + 方法名。使用MethodProxy
对象调用原始方法。 - 在
Main
类的main
方法中,创建了一个Enhancer
对象。 - 通过
enhancer.setSuperclass(UserService.class)
设置目标类为UserService
。 - 通过
enhancer.setCallback(new UserServiceInterceptor())
设置回调处理器为UserServiceInterceptor
的实例。 - 调用
enhancer.create()
方法返回代理对象,将其转换为UserService
类型。 - 调用代理对象的
saveUser("John")
方法,会触发代理对象的intercept
方法,从而实现了动态代理。
在代码执行过程中,代理对象的 intercept
方法会先输出 "Before method: saveUser",然后调用目标对象的 saveUser
方法,最后输出 "After method: saveUser"。
通过 CGLIB 动态代理,可以在不修改目标对象代码的情况下,对其方法进行增强、添加日志等操作。与 JDK 动态代理不同的是,CGLIB 动态代理可以代理类而不仅限于接口。