深入解析Spring源码,揭示JDK动态代理的工作原理。

简介: 深入解析Spring源码,揭示JDK动态代理的工作原理。

JDK动态代理是Java中一种强大的机制,它允许在运行时创建代理对象,并拦截对这些对象方法的调用。Spring框架广泛使用JDK动态代理来实现AOP(Aspect-Oriented Programming)功能。下面将深入解析Spring源码,揭示JDK动态代理的工作原理。

 

JDK动态代理简介

 

JDK动态代理基于反射机制,它主要涉及两个核心接口:

1. `InvocationHandler`:定义了处理代理实例上的方法调用的逻辑。

2. `Proxy`:提供了静态方法来创建代理实例。

 

Spring 使用 JDK 动态代理的场景

 

Spring AOP在以下情况下会使用JDK动态代理:

- 目标对象实现了一个或多个接口。

- 没有强制使用CGLIB(即没有设置`proxyTargetClass=true`)。

 

JDK动态代理的核心类

 

`ProxyFactory`

 

`ProxyFactory`是Spring AOP框架中用于创建代理对象的重要类。它根据目标对象是否实现接口选择合适的代理策略。

 

```java
public class ProxyFactory extends AdvisedSupport implements AopProxyFactory {
    // 省略其他代码...
 
    @Override
    public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
        if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
            return new CglibAopProxy(config);
        } else {
            return new JdkDynamicAopProxy(config);
        }
    }
 
    private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
        Class<?>[] interfaces = config.getProxiedInterfaces();
        return (interfaces.length == 0 || (interfaces.length == 1 && SpringProxy.class.isAssignableFrom(interfaces[0])));
    }
}
```

 

在上面的代码中,如果目标对象实现了接口且未强制使用CGLIB,`createAopProxy`方法会返回一个`JdkDynamicAopProxy`实例。

 

`JdkDynamicAopProxy`

 

`JdkDynamicAopProxy`类负责使用JDK动态代理创建代理对象。

 

```java
public class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {
    private final AdvisedSupport advised;
 
    public JdkDynamicAopProxy(AdvisedSupport config) {
        Assert.notNull(config, "AdvisedSupport must not be null");
        this.advised = config;
    }
 
    @Override
    public Object getProxy(@Nullable ClassLoader classLoader) {
        return Proxy.newProxyInstance(classLoader, this.advised.getProxiedInterfaces(), this);
    }
 
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        MethodInvocation invocation = new ReflectiveMethodInvocation(proxy, this.advised.getTargetSource().getTarget(), method, args, this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, this.advised.getTargetClass()));
        return invocation.proceed();
    }
}
```

 

1. 在`getProxy`方法中,通过`Proxy.newProxyInstance`创建代理实例,该实例会将所有方法调用委托给`invoke`方法。

2. `invoke`方法是代理实例的核心逻辑。它创建一个`MethodInvocation`对象并调用其`proceed`方法,以执行链式拦截器和目标方法。

 

`ReflectiveMethodInvocation`

 

`ReflectiveMethodInvocation`是Spring AOP中具体的方法调用实现。

 

```java
public class ReflectiveMethodInvocation implements ProxyMethodInvocation, Cloneable {
    protected final Object proxy;
    protected final Object target;
    protected final Method method;
    protected Object[] arguments;
    private final List<Object> interceptorsAndDynamicMethodMatchers;
    private int currentInterceptorIndex = -1;
 
    public ReflectiveMethodInvocation(Object proxy, Object target, Method method, Object[] arguments, List<Object> interceptorsAndDynamicMethodMatchers) {
        this.proxy = proxy;
        this.target = target;
        this.method = method;
        this.arguments = arguments;
        this.interceptorsAndDynamicMethodMatchers = interceptorsAndDynamicMethodMatchers;
    }
 
    @Override
    public Object proceed() throws Throwable {
        if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
            return this.method.invoke(this.target, this.arguments);
        }
 
        Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
        if (interceptorOrInterceptionAdvice instanceof MethodInterceptor) {
            MethodInterceptor interceptor = (MethodInterceptor) interceptorOrInterceptionAdvice;
            return interceptor.invoke(this);
        } else {
            return proceed();
        }
    }
}
```

 

1. `proceed`方法是拦截器链执行的核心。

2. 如果所有拦截器都已执行,则直接调用目标方法。

3. 否则,获取下一个拦截器并调用其`invoke`方法。

 

示例代码

 

让我们通过一个简单的例子来展示Spring中JDK动态代理的实际应用。

 

示例接口和实现

 

```java
public interface HelloService {
    void sayHello();
}
 
public class HelloServiceImpl implements HelloService {
    @Override
    public void sayHello() {
        System.out.println("Hello, World!");
    }
}
```

 

自定义拦截器

 

```java
public class CustomInterceptor implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("Before method: " + invocation.getMethod().getName());
        Object result = invocation.proceed();
        System.out.println("After method: " + invocation.getMethod().getName());
        return result;
    }
}
```

 

创建代理并使用

 

```java
public class Main {
    public static void main(String[] args) {
        HelloService target = new HelloServiceImpl();
 
        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.setTarget(target);
        proxyFactory.addAdvice(new CustomInterceptor());
 
        HelloService proxy = (HelloService) proxyFactory.getProxy();
        proxy.sayHello();
    }
}
```
 
#### 输出
 
```
Before method: sayHello
Hello, World!
After method: sayHello
```

 

总结

 

通过深入解析Spring的源码,我们了解了JDK动态代理是如何在Spring AOP中工作的。`ProxyFactory`根据目标对象的情况选择合适的代理实现,而`JdkDynamicAopProxy`通过`Proxy.newProxyInstance`创建代理对象,并在`invoke`方法中管理拦截器的执行。最后,通过`ReflectiveMethodInvocation`类,Spring实现了对方法调用的拦截和增强。这种设计使得Spring AOP能够灵活地处理各种切面需求,提供了强大的AOP功能。

目录
相关文章
|
1月前
|
存储 人工智能 自然语言处理
RAG 调优指南:Spring AI Alibaba 模块化 RAG 原理与使用
通过遵循以上最佳实践,可以构建一个高效、可靠的 RAG 系统,为用户提供准确和专业的回答。这些实践涵盖了从文档处理到系统配置的各个方面,能够帮助开发者构建更好的 RAG 应用。
833 114
|
2月前
|
算法 测试技术 C语言
深入理解HTTP/2:nghttp2库源码解析及客户端实现示例
通过解析nghttp2库的源码和实现一个简单的HTTP/2客户端示例,本文详细介绍了HTTP/2的关键特性和nghttp2的核心实现。了解这些内容可以帮助开发者更好地理解HTTP/2协议,提高Web应用的性能和用户体验。对于实际开发中的应用,可以根据需要进一步优化和扩展代码,以满足具体需求。
203 29
|
1月前
|
安全 前端开发 Java
Spring Boot 项目中触发 Circular View Path 错误的原理与解决方案
在Spring Boot开发中,**Circular View Path**错误常因视图解析与Controller路径重名引发。当视图名称(如`login`)与请求路径相同,Spring MVC无法区分,导致无限循环调用。解决方法包括:1) 明确指定视图路径,避免重名;2) 将视图文件移至子目录;3) 确保Spring Security配置与Controller路径一致。通过合理设定视图和路径,可有效避免该问题,确保系统稳定运行。
80 0
|
2月前
|
前端开发 数据安全/隐私保护 CDN
二次元聚合短视频解析去水印系统源码
二次元聚合短视频解析去水印系统源码
69 3
|
2月前
|
JavaScript 算法 前端开发
JS数组操作方法全景图,全网最全构建完整知识网络!js数组操作方法全集(实现筛选转换、随机排序洗牌算法、复杂数据处理统计等情景详解,附大量源码和易错点解析)
这些方法提供了对数组的全面操作,包括搜索、遍历、转换和聚合等。通过分为原地操作方法、非原地操作方法和其他方法便于您理解和记忆,并熟悉他们各自的使用方法与使用范围。详细的案例与进阶使用,方便您理解数组操作的底层原理。链式调用的几个案例,让您玩转数组操作。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
2月前
|
移动开发 前端开发 JavaScript
从入门到精通:H5游戏源码开发技术全解析与未来趋势洞察
H5游戏凭借其跨平台、易传播和开发成本低的优势,近年来发展迅猛。接下来,让我们深入了解 H5 游戏源码开发的技术教程以及未来的发展趋势。
|
2月前
|
负载均衡 JavaScript 前端开发
分片上传技术全解析:原理、优势与应用(含简单实现源码)
分片上传通过将大文件分割成多个小的片段或块,然后并行或顺序地上传这些片段,从而提高上传效率和可靠性,特别适用于大文件的上传场景,尤其是在网络环境不佳时,分片上传能有效提高上传体验。 博客不应该只有代码和解决方案,重点应该在于给出解决方案的同时分享思维模式,只有思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
2月前
|
存储 前端开发 JavaScript
在线教育网课系统源码开发指南:功能设计与技术实现深度解析
在线教育网课系统是近年来发展迅猛的教育形式的核心载体,具备用户管理、课程管理、教学互动、学习评估等功能。本文从功能和技术两方面解析其源码开发,涵盖前端(HTML5、CSS3、JavaScript等)、后端(Java、Python等)、流媒体及云计算技术,并强调安全性、稳定性和用户体验的重要性。
|
3月前
|
机器学习/深度学习 自然语言处理 算法
生成式 AI 大语言模型(LLMs)核心算法及源码解析:预训练篇
生成式 AI 大语言模型(LLMs)核心算法及源码解析:预训练篇
281 0
|
5月前
|
设计模式 存储 安全
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
创建型模式的主要关注点是“怎样创建对象?”,它的主要特点是"将对象的创建与使用分离”。这样可以降低系统的耦合度,使用者不需要关注对象的创建细节。创建型模式分为5种:单例模式、工厂方法模式抽象工厂式、原型模式、建造者模式。
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析

推荐镜像

更多