spring-reading./spring-aop/spring-aop-aopProxy/README.md

206 lines
8.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

## AopProxy
- [AopProxy](#aopproxy)
- [一、基本信息](#一基本信息)
- [二、基本描述](#二基本描述)
- [三、主要功能](#三主要功能)
- [四、接口源码](#四接口源码)
- [五、主要实现](#五主要实现)
- [六、最佳实践](#六最佳实践)
- [JDK动态代理](#jdk动态代理)
- [CGLIB代理](#cglib代理)
- [七、源码分析](#七源码分析)
- [七、常见问题](#七常见问题)
### 一、基本信息
✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading)
### 二、基本描述
`AopProxy` 接口是Spring框架中用于支持面向切面编程AOP的关键组件之一它定义了生成代理对象的标准接口允许在运行时动态地创建代理对象以实现对目标对象的方法调用进行拦截和增强。
### 三、主要功能
1. **代理对象的创建与管理**
+ `AopProxy` 接口定义了创建和管理代理对象的标准方法,可以通过这些方法在运行时动态地生成代理对象。
2. **对目标对象方法的拦截与增强**
+ AOP代理对象通过 `AopProxy` 接口实现对目标对象方法的拦截,允许在方法执行前、后或异常时执行额外的逻辑,从而实现对方法行为的增强。
3. **支持不同的代理方式**
+ `AopProxy` 接口支持多种代理方式包括JDK动态代理和CGLIB代理。这样可以根据目标对象是否实现接口来选择合适的代理方式。
4. **实现AOP的横切关注点**
+ 通过 `AopProxy` 接口可以将AOP的横切关注点与业务逻辑进行分离提高了代码的模块化和可维护性同时也使得横切关注点可以被重用在多个不同的业务逻辑中。
### 四、接口源码
`AopProxy` 接口是一个委托接口用于配置AOP代理并允许创建实际的代理对象。它提供了两个方法用于创建代理对象第一个方法使用默认的类加载器创建代理对象通常是线程上下文类加载器第二个方法允许指定类加载器创建代理对象。可以使用JDK动态代理或者CGLIB代理技术来生成代理对象。
```java
/**
* 配置AOP代理的委托接口允许创建实际的代理对象。
*
* <p>默认情况下可用于创建代理对象的实现包括JDK动态代理和CGLIB代理
* 这些代理实现由 {@link DefaultAopProxyFactory} 应用。
*
* @author Rod Johnson
* @author Juergen Hoeller
* @see DefaultAopProxyFactory
*/
public interface AopProxy {
/**
* 创建一个新的代理对象。
* <p>使用AopProxy的默认类加载器必要时用于代理创建
* 通常为线程上下文类加载器。
* @return 新的代理对象(永远不会是 {@code null}
* @see Thread#getContextClassLoader()
*/
Object getProxy();
/**
* 创建一个新的代理对象。
* <p>使用给定的类加载器(必要时用于代理创建)。
* 如果给定的类加载器为 {@code null},则简单地传递并因此导致低级代理工具的默认值,
* 这通常不同于AopProxy实现的 {@link #getProxy()} 方法选择的默认值。
* @param classLoader 用于创建代理的类加载器
* (或 {@code null} 表示使用低级代理工具的默认值)
* @return 新的代理对象(永远不会是 {@code null}
*/
Object getProxy(@Nullable ClassLoader classLoader);
}
```
### 五、主要实现
1. **JdkDynamicAopProxy**
+ 使用 JDK 动态代理实现的 `AopProxy` 实现类。当目标对象实现了至少一个接口时Spring 将使用该类创建代理对象。该类通过 Java 标准库中的 `java.lang.reflect.Proxy` 类来创建代理对象。
2. **CglibAopProxy**
+ 使用 CGLIBCode Generation Library动态代理实现的 `AopProxy` 实现类。当目标对象没有实现任何接口时Spring 将使用该类创建代理对象。该类通过生成目标类的子类来创建代理对象,实现了对目标对象方法的拦截和增强。
### 六、最佳实践
#### JDK动态代理
使用 JDK 动态代理来创建 AOP 代理对象。在 `jdkProxy` 方法中,通过配置 `AdvisedSupport` 对象,设置目标对象和接口,然后利用反射创建 `JdkDynamicAopProxy` 实例,并调用 `AopProxy` 接口的 `getProxy` 方法生成代理对象。最后,输出代理对象的信息和调用代理对象方法的结果。
```java
public class AopProxyDemo {
public static void main(String[] args) throws Exception {
jdkProxy();
}
/**
* Jdk代理
*
* @throws Exception
*/
private static void jdkProxy() throws Exception {
// 创建AdvisedSupport对象用于配置AOP代理
AdvisedSupport advisedSupport = new AdvisedSupport();
// 设置目标对象
advisedSupport.setTarget(new MyServiceImpl());
// 设置目标对象实现的接口
advisedSupport.setInterfaces(MyService.class);
// 获取JdkDynamicAopProxy的Class对象
Class jdkClass = Class.forName("org.springframework.aop.framework.JdkDynamicAopProxy");
// 获取JdkDynamicAopProxy的构造方法
Constructor constructor = jdkClass.getConstructor(AdvisedSupport.class);
constructor.setAccessible(true);
// 使用构造方法创建JdkDynamicAopProxy实例
AopProxy aopProxy = (AopProxy) constructor.newInstance(advisedSupport);
// 调用getProxy方法创建代理对象
MyService myService = (MyService) aopProxy.getProxy();
// 输出代理对象的信息
System.out.println("JDK Class = " + myService.getClass());
// 调用代理对象的方法
System.out.println("doSomething method result = " + myService.doSomething());
}
}
```
运行结果,代理对象的类为 `com.sun.proxy.$Proxy0`,调用代理对象的 `doSomething` 方法结果为 `"hello world"`
```java
JDK Class = class com.sun.proxy.$Proxy0
doSomething method result = hello world
```
#### CGLIB代理
使用 CGLIB 动态代理来创建 AOP 代理对象。在 `cglibProxy` 方法中,通过配置 `AdvisedSupport` 对象,设置目标对象,然后利用反射创建 `CglibAopProxy` 实例,并调用 `AopProxy` 接口的 `getProxy` 方法生成代理对象。最后,输出代理对象的信息和调用代理对象方法的结果。
```java
public class AopProxyDemo {
public static void main(String[] args) throws Exception {
cglibProxy();
}
/**
* cglib代理
*
* @throws Exception
*/
private static void cglibProxy() throws Exception {
// 创建AdvisedSupport对象用于配置AOP代理
AdvisedSupport advisedSupport = new AdvisedSupport();
// 设置目标对象
advisedSupport.setTarget(new MyServiceImpl());
// 获取CglibAopProxy的Class对象
Class cglibClass = Class.forName("org.springframework.aop.framework.CglibAopProxy");
// 获取CglibAopProxy的构造方法
Constructor constructor = cglibClass.getConstructor(AdvisedSupport.class);
constructor.setAccessible(true);
// 使用构造方法创建CglibAopProxy实例
AopProxy aopProxy = (AopProxy) constructor.newInstance(advisedSupport);
// 调用getProxy方法创建代理对象
MyService myService = (MyService) aopProxy.getProxy();
// 输出代理对象的信息
System.out.println("Cglib Class = " + myService.getClass());
// 调用代理对象的方法
System.out.println("doSomething method result = " + myService.doSomething());
}
}
```
运行结果,代理对象的类为 `com.xcs.spring.MyServiceImpl$$EnhancerBySpringCGLIB$$3c231008`,调用代理对象的 `doSomething` 方法结果为 `"hello world"`
```java
Cglib Class = class com.xcs.spring.MyServiceImpl$$EnhancerBySpringCGLIB$$3c231008
doSomething method result = hello world
```
### 七、源码分析
暂无
### 七、常见问题
1. **选择合适的代理方式**
+ 在使用 Spring AOP 时需要根据目标对象是否实现接口来选择合适的代理方式JDK 动态代理还是 CGLIB 动态代理)。如果目标对象实现了接口,则会使用 JDK 动态代理,否则会使用 CGLIB 动态代理。选择合适的代理方式可以影响到代理对象的性能和行为。
2. **代理对象的类型**
+ 生成的代理对象的类型可能与原始目标对象的类型不同。这可能会导致一些类型转换或 instanceof 判断出现问题。因此,在使用代理对象时,需要注意其类型和行为与原始对象可能存在的差异。