目录
- 一、 啥是代理?为啥要有代理?(明星经纪人版)
- 二、 JDK动态代理:接口的守护者(接口明星的专属经纪人)
- 三、 CGLIB代理:类的守护者(全能经纪人)
- 四、 JDK动态代理 vs CGLIB代理:谁更胜一筹?(经纪人PK)
- 五、 在SpringBoot中,它们是如何应用的?(SpringBoot的智能经纪人系统)
- 六、 总结
如果喜欢作者的讲解方式,关注作者不迷路,同时也可以看看我的其他文章! 感谢!!!
被重复代码逼疯?AOP来当“舔狗”!日志/事务/权限,随叫随到!
咱今儿个来聊聊JDK动态代理和CGLIB代理,保证你听得懂,记得住,还能用得上!
想象一下,你是个超级巨星,每天行程排得满满当当,签合同、拍广告、跑通告,忙到飞起!你哪有时间事事亲力亲为?这时候,就需要一个经纪人来帮你打理一切!这个经纪人就是你的代理!
在编程世界里,代理也是一样的道理!它可以让你在不修改原有代码的情况下,增强对象的功能,控制对象的访问。简直是程序员的福音!
JDK动态代理,就像一个只服务于有接口的明星的经纪人。它要求你的明星(也就是你的类)必须有接口,才能为你保驾护航!
原理:JDK动态代理是利用Java反射机制,在运行时动态地生成一个代理类。这个代理类实现了你的接口,并且可以拦截对接口方法的调用。就像经纪人时刻关注着你的行程,随时准备为你处理突发状况!
通俗易懂的解释:
Star
(明星)。就像你和经纪公司签的合同,规定了你的权利和义务。RealStar
(真正的明星)。你就是那个闪耀的明星!✨InvocationHandler
(经纪人),它知道如何处理对Star
接口方法的调用。经纪人熟知你的所有行程,知道如何帮你处理各种事务。Proxy.newProxyInstance()
方法,JDK会动态地生成一个代理类,这个代理类也实现了Star
接口。就像经纪公司为你配备了一个专属的代理人!sing()
方法时,实际上是调用了InvocationHandler
的invoke()
方法。在这个方法里,你可以做一些额外的操作,比如记录日志、权限校验等等。经纪人会在你唱歌前后记录日志,确保一切顺利!例子:
// 1. 定义接口
interface Star {
void sing(String song);
}
// 2. 实现类
class RealStar implements Star {
@Override
public void sing(String song) {
System.out.println("RealStar singing: " + song);
}
}
// 3. InvocationHandler
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
class StarInvocationHandler implements InvocationHandler {
private Object target; // 被代理的对象
public StarInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before singing, record the log."); // 增强功能:记录日志
Object result = method.invoke(target, args); // 调用真实对象的方法
System.out.println("After singing, record the log."); // 增强功能:记录日志
return result;
}
}
// 4. 使用代理
import java.lang.reflect.Proxy;
public class Main {
public static void main(String[] args) {
RealStar realStar = new RealStar();
StarInvocationHandler handler = new StarInvocationHandler(realStar);
Star proxy = (Star) Proxy.newProxyInstance(
realStar.getClass().getClassLoader(), //获取目标类对象的类加载器
realStar.getClass().getInterfaces(), //获取目标类对象实现的接口
handler //增强功能的类
);
proxy.sing("Hello World"); // 调用代理对象的方法
}
}
// 假设有一个权限校验的接口
interface PermissionChecker {
boolean hasPermission(String user, String operation);
}
// 实现类
class AdminPermissionChecker implements PermissionChecker {
@Override
public boolean hasPermission(String user, String operation) {
// 模拟权限校验逻辑
if ("admin".equals(user) && "delete".equals(operation)) {
return true;
}
return false;
}
}
// 修改InvocationHandler
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
class StarInvocationHandler implements InvocationHandler {
private Object target;
private PermissionChecker permissionChecker;
public StarInvocationHandler(Object target, PermissionChecker permissionChecker) {
this.target = target;
this.permissionChecker = permissionChecker;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 权限校验
if (method.getName().equals("sing")) {
if (!permissionChecker.hasPermission("user", "sing")) {
System.out.println("No permission to sing!");
return null;
}
}
System.out.println("Before singing, record the log.");
Object result = method.invoke(target, args);
System.out.println("After singing, record the log.");
return result;
}
}
// 使用代理
import java.lang.reflect.Proxy;
public class Main {
public static void main(String[] args) {
RealStar realStar = new RealStar();
AdminPermissionChecker permissionChecker = new AdminPermissionChecker();
StarInvocationHandler handler = new StarInvocationHandler(realStar, permissionChecker);
Star proxy = (Star) Proxy.newProxyInstance(
realStar.getClass().getClassLoader(),
realStar.getClass().getInterfaces(),
handler
);
proxy.sing("Hello World"); // 调用代理对象的方法
}
}
好处:
缺点:
CGLIB代理,就像一个更厉害的经纪人,它不需要你的明星有接口,直接就能代理你的类!简直是全能经纪人!
原理:CGLIB(Code Generation Library)是一个强大的高性能的代码生成库。它通过生成目标类的子类来实现代理。就像经纪人直接接管了你的生活,为你安排一切!️
通俗易懂的解释:
例子(由易到难):
// 1. 定义类(不需要接口)
class Singer {
public void sing(String song) {
System.out.println("Singer singing: " + song);
}
}
// 2. MethodInterceptor
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
class SingerMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("Before singing, record the log."); // 增强功能:记录日志
Object result = proxy.invokeSuper(obj, args); // 调用父类(目标类)的方法
System.out.println("After singing, record the log."); // 增强功能:记录日志
return result;
}
}
// 3. 使用代理
import net.sf.cglib.proxy.Enhancer;
public class Main {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Singer.class); // 设置父类(目标类)
enhancer.setCallback(new SingerMethodInterceptor()); // 设置回调
Singer proxy = (Singer) enhancer.create(); // 创建代理对象
proxy.sing("Hello World"); // 调用代理对象的方法
}
}
// 1. 定义类(不需要接口)
class Calculator {
public int add(int a, int b) {
return a + b;
}
}
// 2. MethodInterceptor
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
class PerformanceMonitorInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
long startTime = System.nanoTime();
Object result = proxy.invokeSuper(obj, args);
long endTime = System.nanoTime();
System.out.println(method.getName() + " execution time: " + (endTime - startTime) + " ns");
return result;
}
}
// 3. 使用代理
import net.sf.cglib.proxy.Enhancer;
public class Main {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Calculator.class);
enhancer.setCallback(new PerformanceMonitorInterceptor());
Calculator proxy = (Calculator) enhancer.create();
proxy.add(1, 2);
}
}
好处:
缺点:
特性 | JDK动态代理 | CGLIB代理 |
---|---|---|
接口要求 | 必须实现接口 | 不需要实现接口 |
实现方式 | 基于反射,动态生成代理类 | 基于字节码生成,生成目标类的子类 |
依赖 | JDK自带,无需额外依赖 | 需要引入CGLIB库 |
代理final类 | 可以代理 | 不能代理 |
效率 | 相对较高 | 相对较低(但通常可以忽略不计) |
总结:
在SpringBoot中,AOP(面向切面编程)是代理模式的典型应用场景。SpringBoot默认使用JDK动态代理,如果目标类没有实现接口,则会自动切换到CGLIB代理。就像SpringBoot有一个智能经纪人系统,会自动为你选择合适的经纪人!
例子:使用AOP记录方法执行时间(SpringBoot的智能经纪人帮你记录表演时间)
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class TimeAspect {
@Around("@annotation(com.example.demo.annotation.TimeLog)") // 拦截带有@TimeLog注解的方法
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
Object proceed = joinPoint.proceed();
long executionTime = System.currentTimeMillis() - start;
System.out.println(joinPoint.getSignature() + " executed in " + executionTime + "ms");
return proceed;
}
}
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface TimeLog {
}
import org.springframework.stereotype.Service;
@Service
public class MyService {
@TimeLog
public void doSomething() {
try {
Thread.sleep(1000); // 模拟耗时操作
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
在这个例子中,TimeAspect
就是一个切面,它使用@Around
注解来拦截带有@TimeLog
注解的方法,并在方法执行前后记录时间。SpringBoot会自动使用代理模式来实现AOP,如果MyService
实现了接口,则使用JDK动态代理,否则使用CGLIB代理。就像SpringBoot的智能经纪人系统会自动为你选择合适的经纪人,并帮你记录表演时间!⏱️
JDK动态代理和CGLIB代理都是代理模式的实现方式,它们可以让你在不修改原有代码的情况下,增强对象的功能,控制对象的访问。选择哪种代理方式,取决于你的类的具体情况。在SpringBoot中,AOP是代理模式的典型应用场景,SpringBoot会自动选择合适的代理方式来实现AOP。
希望这篇丰富、生动有趣的文章讲解能让你彻底理解JDK动态代理和CGLIB代理! 祝你早日成为编程界的超级巨星!