diff --git a/README.md b/README.md index fc0bde2..b90c19d 100644 --- a/README.md +++ b/README.md @@ -199,6 +199,7 @@ - [JDK动态代理](spring-aop/spring-aop-jdkProxy/README.md):接口实现,动态生成代理类,处理方法调用,统一横切关注点。 - [Cglib动态代理](spring-aop/spring-aop-cglibProxy/README.md):基于字节码生成的库,无需接口,可拦截类方法并进行增强。 - [AopProxy](spring-aop/spring-aop-aopProxy/README.md):创建和管理AOP代理对象。 + - [AopProxyFactory](spring-aop/spring-aop-aopProxyFactory/README.md):创建AOP代理工厂,支持JDK和CGLIB。 - [ClassFilter](spring-aop/spring-aop-classFilter/README.md):确定类是否匹配拦截条件。 - [MethodMatcher](spring-aop/spring-aop-methodMatcher/README.md):确定方法是否匹配拦截条件。 - [Pointcut](spring-aop/spring-aop-pointcut/README.md):定义切入点,匹配被拦截的方法。 diff --git a/spring-aop/pom.xml b/spring-aop/pom.xml index 1255037..b4a40c9 100644 --- a/spring-aop/pom.xml +++ b/spring-aop/pom.xml @@ -33,6 +33,7 @@ spring-aop-advice-afterReturningAdvice spring-aop-advice-throwsAdvice spring-aop-advice-introductionInterceptor + spring-aop-aopProxyFactory 4.0.0 diff --git a/spring-aop/spring-aop-aopProxyFactory/README.md b/spring-aop/spring-aop-aopProxyFactory/README.md new file mode 100644 index 0000000..d26c8de --- /dev/null +++ b/spring-aop/spring-aop-aopProxyFactory/README.md @@ -0,0 +1,206 @@ +## AopProxyFactory + +- [AopProxyFactory](#AopProxyFactory) + - [一、基本信息](#一基本信息) + - [二、基本描述](#二基本描述) + - [三、主要功能](#三主要功能) + - [四、接口源码](#四接口源码) + - [五、主要实现](#五主要实现) + - [六、最佳实践](#六最佳实践) + - [七、源码分析](#七源码分析) + - [八、常见问题](#八常见问题) + +### 一、基本信息 + +✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) + +### 二、基本描述 + +`AopProxyFactory` 接口是 Spring AOP 框架的关键组件之一,负责根据配置信息创建 AOP 代理对象,支持根据目标对象的类型和配置选择合适的代理方式(JDK 动态代理或 CGLIB 代理)。 + +### 三、主要功能 + +1. **创建 AOP 代理对象** + + 根据传入的 `AdvisedSupport` 对象,利用指定的代理方式(JDK 动态代理或 CGLIB 代理)生成 AOP 代理对象。 + +2. **决定代理方式** + + 根据目标类的类型和配置信息,确定是否使用 JDK 动态代理或 CGLIB 代理。这个决定通常是基于配置中的一些条件,例如是否需要代理接口或者是否允许使用 CGLIB 代理。 + +3. **支持灵活配置** ++ 通过实现该接口,可以灵活地定制 AOP 代理的生成方式,以满足不同场景下的需求。 + +### 四、接口源码 + +`AopProxyFactory`接口 ,其功能是创建基于 `AdvisedSupport` 配置对象的 AOP 代理。代理对象需要满足一系列约定,包括实现被配置指示的所有接口、实现 `Advised` 接口、实现 `equals` 方法用于比较被代理的接口、通知和目标、并且在通知者和目标都是可序列化的情况下应该是可序列化的,以及在通知者和目标都是线程安全的情况下应该是线程安全的。该接口的实现应该能够根据给定的 AOP 配置创建相应的 AOP 代理对象,并在配置无效时抛出 `AopConfigException` 异常。 + +```java +/** + * 接口,由能够基于 {@link AdvisedSupport} 配置对象创建 AOP 代理的工厂实现。 + * + *

代理对象应遵守以下约定: + *

+ * + *

代理可能允许或不允许更改通知。如果它们不允许更改通知(例如,因为配置已被冻结),则代理应在尝试更改通知时抛出 {@link AopConfigException}。 + * + * @author Rod Johnson + * @author Juergen Hoeller + */ +public interface AopProxyFactory { + + /** + * 根据给定的 AOP 配置创建一个 {@link AopProxy}。 + * @param config 以 AdvisedSupport 对象形式表示的 AOP 配置 + * @return 相应的 AOP 代理 + * @throws AopConfigException 如果配置无效 + */ + AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException; + +} + +``` + +### 五、主要实现 + +1. **DefaultAopProxyFactory** + + `DefaultAopProxyFactory` 是 `AopProxyFactory` 接口的默认实现类,它负责根据给定的 `AdvisedSupport` 配置对象创建 AOP 代理,根据配置信息选择合适的代理方式(JDK 动态代理或 CGLIB 代理),并处理可能出现的异常情况,使得 Spring AOP 能够灵活地生成并使用 AOP 代理对象,实现切面编程的功能。 + +### 六、最佳实践 + +何使用 Spring AOP 创建 JDK 动态代理和 CGLIB 代理。在 `jdkProxy()` 方法中,首先创建了一个 `AdvisedSupport` 对象用于配置 AOP 代理,然后通过 `DefaultAopProxyFactory` 实例创建 JDK 动态代理对象,并打印生成的代理类。而在 `cglibProxy()` 方法中,同样创建了一个 `AdvisedSupport` 对象用于配置 AOP 代理,然后通过 `DefaultAopProxyFactory` 实例创建 CGLIB 代理对象,并打印生成的代理类。 + +```java +public class AopProxyFactoryDemo { + + public static void main(String[] args) { + // 分别演示 JDK 动态代理和 CGLIB 代理 + jdkProxy(); + cglibProxy(); + } + + /** + * JDK 动态代理示例 + */ + private static void jdkProxy() { + // 创建 AdvisedSupport 对象,用于配置 AOP 代理 + AdvisedSupport advisedSupport = new AdvisedSupport(); + // 设置目标对象 + advisedSupport.setTarget(new MyServiceImpl()); + // 设置目标对象的类 + advisedSupport.setTargetClass(MyService.class); + + // 创建 DefaultAopProxyFactory 实例 + AopProxyFactory aopProxyFactory = new DefaultAopProxyFactory(); + // 创建 JDK 动态代理对象 + MyService myService = (MyService) aopProxyFactory.createAopProxy(advisedSupport).getProxy(); + // 打印生成的代理类 + System.out.println("jdkProxy = " + myService.getClass()); + } + + /** + * CGLIB 代理示例 + */ + private static void cglibProxy() { + // 创建 AdvisedSupport 对象,用于配置 AOP 代理 + AdvisedSupport advisedSupport = new AdvisedSupport(); + // 设置目标对象 + advisedSupport.setTarget(new MyServiceImpl()); + // 创建 DefaultAopProxyFactory 实例 + AopProxyFactory aopProxyFactory = new DefaultAopProxyFactory(); + // 创建 CGLIB 代理对象 + MyService myService = (MyService) aopProxyFactory.createAopProxy(advisedSupport).getProxy(); + // 打印生成的代理类 + System.out.println("cglibProxy = " + myService.getClass()); + } +} +``` + +运行结果,显示了两种不同的代理类。`jdkProxy` 是使用 JDK 动态代理生成的代理类,它由 `com.sun.proxy.$Proxy0` 表示。而 `cglibProxy` 是使用 CGLIB 生成的代理类,它由 `com.xcs.spring.MyServiceImpl$$EnhancerBySpringCGLIB$$3c109cf5` 表示。这两种代理类分别对应了不同的代理方式,分别由 Spring AOP 根据配置信息生成。 + +```java +jdkProxy = class com.sun.proxy.$Proxy0 +cglibProxy = class com.xcs.spring.MyServiceImpl$$EnhancerBySpringCGLIB$$3c109cf5 +``` + +### 七、源码分析 + +`DefaultAopProxyFactory` 是 `AopProxyFactory` 接口的默认实现,根据给定的 `AdvisedSupport` 配置对象,可以创建 CGLIB 代理或 JDK 动态代理。如果对于给定的 `AdvisedSupport` 实例满足以下条件之一,则会创建 CGLIB 代理:优化标志被设置、代理目标类标志被设置,或者未指定代理接口。通常情况下,可以通过指定 `proxyTargetClass` 来强制使用 CGLIB 代理,或者通过指定一个或多个接口来使用 JDK 动态代理。 + +```java +/** + * 默认的 {@link AopProxyFactory} 实现,根据条件创建 CGLIB 代理或 JDK 动态代理。 + * + *

如果对于给定的 {@link AdvisedSupport} 实例满足以下条件之一,则创建 CGLIB 代理: + *

+ * + *

通常情况下,可以通过指定 {@code proxyTargetClass} 来强制使用 CGLIB 代理,或者通过指定一个或多个接口来使用 JDK 动态代理。 + * + * @author Rod Johnson + * @author Juergen Hoeller + * @author Sebastien Deleuze + * @since 2004-03-12 + * @see AdvisedSupport#setOptimize + * @see AdvisedSupport#setProxyTargetClass + * @see AdvisedSupport#setInterfaces + */ +@SuppressWarnings("serial") +public class DefaultAopProxyFactory implements AopProxyFactory, Serializable { + + @Override + public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException { + // 检查是否支持CGLIB代理,如果是,则创建CGLIB代理 + if (!NativeDetector.inNativeImage() && + (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config))) { + // 获取目标类 + Class targetClass = config.getTargetClass(); + if (targetClass == null) { + // 如果目标类为空,则抛出AopConfigException异常 + throw new AopConfigException("TargetSource cannot determine target class: " + + "Either an interface or a target is required for proxy creation."); + } + // 如果目标类是接口或者是代理类,则创建JDK动态代理 + if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) { + return new JdkDynamicAopProxy(config); + } + // 否则,创建CGLIB代理 + return new ObjenesisCglibAopProxy(config); + } else { + // 否则,创建JDK动态代理 + return new JdkDynamicAopProxy(config); + } + } + + /** + * 确定提供的 {@link AdvisedSupport} 是否仅指定了 {@link org.springframework.aop.SpringProxy} 接口 + * (或者根本没有指定代理接口)。 + */ + private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) { + Class[] ifcs = config.getProxiedInterfaces(); + return (ifcs.length == 0 || (ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0]))); + } + +} +``` + +### 八、常见问题 + +1. **代理选择问题** + + + 我们可能会困惑于何时使用 CGLIB 代理,何时使用 JDK 动态代理。这可能涉及到代理目标类是否实现了接口、是否需要强制使用 CGLIB 代理等因素。 + +2. **代理配置错误** + + + 配置不当可能导致代理对象的创建失败或不符合预期。例如,未正确设置目标对象、未指定要代理的接口、优化标志或代理目标类标志设置错误等。 + +3. **异常处理** + + + 在代理对象创建过程中可能会发生各种异常,如配置错误、代理对象无法生成等。我们需要正确处理这些异常,并根据具体情况进行调试和修复。 \ No newline at end of file diff --git a/spring-aop/spring-aop-aopProxyFactory/pom.xml b/spring-aop/spring-aop-aopProxyFactory/pom.xml new file mode 100644 index 0000000..c1b15a8 --- /dev/null +++ b/spring-aop/spring-aop-aopProxyFactory/pom.xml @@ -0,0 +1,14 @@ + + + + com.xcs.spring + spring-aop + 0.0.1-SNAPSHOT + + + 4.0.0 + spring-aop-aopProxyFactory + + \ No newline at end of file diff --git a/spring-aop/spring-aop-aopProxyFactory/src/main/java/com/xcs/spring/AopProxyFactoryDemo.java b/spring-aop/spring-aop-aopProxyFactory/src/main/java/com/xcs/spring/AopProxyFactoryDemo.java new file mode 100644 index 0000000..dcf58bc --- /dev/null +++ b/spring-aop/spring-aop-aopProxyFactory/src/main/java/com/xcs/spring/AopProxyFactoryDemo.java @@ -0,0 +1,50 @@ +package com.xcs.spring; + +import org.springframework.aop.framework.AdvisedSupport; +import org.springframework.aop.framework.AopProxyFactory; +import org.springframework.aop.framework.DefaultAopProxyFactory; + +public class AopProxyFactoryDemo { + + public static void main(String[] args) { + // 分别演示 JDK 动态代理和 CGLIB 代理 + jdkProxy(); + cglibProxy(); + } + + /** + * JDK 动态代理示例 + */ + private static void jdkProxy() { + // 创建 AdvisedSupport 对象,用于配置 AOP 代理 + AdvisedSupport advisedSupport = new AdvisedSupport(); + // 设置目标对象 + advisedSupport.setTarget(new MyServiceImpl()); + // 设置目标对象的类 + advisedSupport.setTargetClass(MyService.class); + + // 创建 DefaultAopProxyFactory 实例 + AopProxyFactory aopProxyFactory = new DefaultAopProxyFactory(); + // 创建 JDK 动态代理对象 + MyService myService = (MyService) aopProxyFactory.createAopProxy(advisedSupport).getProxy(); + // 打印生成的代理类 + System.out.println("jdkProxy = " + myService.getClass()); + } + + /** + * CGLIB 代理示例 + */ + private static void cglibProxy() { + // 创建 AdvisedSupport 对象,用于配置 AOP 代理 + AdvisedSupport advisedSupport = new AdvisedSupport(); + // 设置目标对象 + advisedSupport.setTarget(new MyServiceImpl()); + // 创建 DefaultAopProxyFactory 实例 + AopProxyFactory aopProxyFactory = new DefaultAopProxyFactory(); + // 创建 CGLIB 代理对象 + MyService myService = (MyService) aopProxyFactory.createAopProxy(advisedSupport).getProxy(); + // 打印生成的代理类 + System.out.println("cglibProxy = " + myService.getClass()); + } +} + diff --git a/spring-aop/spring-aop-aopProxyFactory/src/main/java/com/xcs/spring/MyService.java b/spring-aop/spring-aop-aopProxyFactory/src/main/java/com/xcs/spring/MyService.java new file mode 100644 index 0000000..0cf16d5 --- /dev/null +++ b/spring-aop/spring-aop-aopProxyFactory/src/main/java/com/xcs/spring/MyService.java @@ -0,0 +1,7 @@ +package com.xcs.spring; + +public interface MyService { + + String doSomething(); + +} diff --git a/spring-aop/spring-aop-aopProxyFactory/src/main/java/com/xcs/spring/MyServiceImpl.java b/spring-aop/spring-aop-aopProxyFactory/src/main/java/com/xcs/spring/MyServiceImpl.java new file mode 100644 index 0000000..9e3938e --- /dev/null +++ b/spring-aop/spring-aop-aopProxyFactory/src/main/java/com/xcs/spring/MyServiceImpl.java @@ -0,0 +1,9 @@ +package com.xcs.spring; + +public class MyServiceImpl implements MyService { + + @Override + public String doSomething() { + return "hello world"; + } +} diff --git a/spring-aop/spring-aop-aspectj-aspect/src/main/java/com/xcs/spring/AspectDemo.java b/spring-aop/spring-aop-aspectj-aspect/src/main/java/com/xcs/spring/AspectDemo.java index b812ae2..5d723ab 100644 --- a/spring-aop/spring-aop-aspectj-aspect/src/main/java/com/xcs/spring/AspectDemo.java +++ b/spring-aop/spring-aop-aspectj-aspect/src/main/java/com/xcs/spring/AspectDemo.java @@ -1,6 +1,5 @@ package com.xcs.spring; -import com.xcs.spring.service.MyService; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class AspectDemo { diff --git a/spring-aop/spring-aop-aspectj-aspect/src/main/java/com/xcs/spring/LoggingAspect.java b/spring-aop/spring-aop-aspectj-aspect/src/main/java/com/xcs/spring/LoggingAspect.java index 109fa4a..97bf06a 100644 --- a/spring-aop/spring-aop-aspectj-aspect/src/main/java/com/xcs/spring/LoggingAspect.java +++ b/spring-aop/spring-aop-aspectj-aspect/src/main/java/com/xcs/spring/LoggingAspect.java @@ -8,7 +8,7 @@ import org.springframework.stereotype.Component; @Component class LoggingAspect { - @Before("execution(* com.xcs.spring.service.MyService.doSomething(..))") + @Before("execution(* com.xcs.spring.MyService.doSomething(..))") public void beforeAdvice() { System.out.println("Before executing the method..." ); } diff --git a/spring-aop/spring-aop-aspectj-aspect/src/main/java/com/xcs/spring/service/MyService.java b/spring-aop/spring-aop-aspectj-aspect/src/main/java/com/xcs/spring/MyService.java similarity index 84% rename from spring-aop/spring-aop-aspectj-aspect/src/main/java/com/xcs/spring/service/MyService.java rename to spring-aop/spring-aop-aspectj-aspect/src/main/java/com/xcs/spring/MyService.java index 928bc15..809682b 100644 --- a/spring-aop/spring-aop-aspectj-aspect/src/main/java/com/xcs/spring/service/MyService.java +++ b/spring-aop/spring-aop-aspectj-aspect/src/main/java/com/xcs/spring/MyService.java @@ -1,4 +1,4 @@ -package com.xcs.spring.service; +package com.xcs.spring; import org.springframework.stereotype.Service;