Spring中的AOP的特性详解。

动态代理的实现原理

要了解Spring的AOP就必须先了解动态代理的原理,因为AOP就是基于动态代理实现的。动态代理要从JDK本身说起。

在JDK的java.lang.reflect包下有个Proxy类,他正是构造代理类的入口。

该类有一个方法newProxyInstance就是创建代理对象的方法。

这个方法需要3个参数:ClassLoader,用来加载代理类的Loader类,通常这个Loader和被代理的类是同一个Loader类;Interfaces,是要被代理的那些接口;InvocationHandler,用于执行除了被代理接口中方法之外的用户自定义的操作,他也是用户需要代理的最终目的。用户调用目标方法都被代理到在InvocationHandler类中定义的唯一方法invoke()中。

下面还是看看Proxy产生代理类的过程,他构造的代理到底是什么样子?

其实从上图中可以发现,构造代理类是在ProxyGenerator的generateProxyClass方法中进行的。ProxyGenerator类在sun.misc包下。


Spring AOP如何实现

从代理的原理我们知道,代理的目的是调用目标方法时可以转而执行InvocationHandler类的invoke方法,所以如何在InvocationHandler上做文章就是Spring实现AOP的关键所在。

Spring的AOP实现遵守AOP联盟的约定,同时Spring又扩展了他,增加了如Pointcut、Advisor等一些接口使得其更加灵活。

Spring引用了Aop Alliance定义的接口。暂且不讨论Spring如何扩展Aop Alliance,先看看Spring是如何实现代理类的。要实现代理类,在Spring的配置文件中通常是这样定义一个Bean的:

<bean id="testBeanSingleton" class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="proxyInterfaces" >
        <value>org.springframework.aop.framework.PrototypeTargetTest$TestBean</value>
    </property>
    <property name="target">
        <ref local="testBeanTarget"></ref>
    </property>
    <property name="singleton">
        <value>true</value>
    </property>
    <property name="interceptorNames">
        <list>
            <value>testInterceptor</value>
            <value>testInterceptor2</value>
        </list>
    </property>
</bean>

可以看到,要设置被代理的接口和接口的实现类(也就是目标类),以及拦截器(在执行目标方法之前被调用)。这里可以选择使用在Spring中定义的各种各样的拦截器。

下面看看Spring是如何完成代理的。

前面提到Spring AOP是实现其自身的扩展点来完成这个特性的,从这个代理类可以看出他继承了FactoryBean的ProxyFactoryBean,FactoryBean之所以特别就在于他可以让你自定义对象的创建方法。当然处理对象要通过Proxy类来动态生成。

下图是Spring创建代理对象的时序图。

在Spring创建了代理对象后,当你调用目标对象上的方法时,都会被代理到InvocationHandler类的invoke方法中执行,这在钱买你已经解释了。在这里JdkDynamicAopProxy类实现了InvocationHandler接口。

下面再看看Spring时如何调用拦截器的,下图是这个过程的时序图。

以上所说的都是JDK动态代理,Spring还支持一种CGLIB类代理。

### Spring AOP 面向切面编程详解与图解 Spring AOP(Aspect-Oriented Programming,面向切面编程)是一种编程范式,旨在通过分离横切关注点(cross-cutting concerns)来增强代码的模块化[^1]。AOP 的核心思想是将业务逻辑与系统服务(如日志记录、事务管理等)分离,从而提高代码的可维护性和复用性。 #### 核心概念 以下是 Spring AOP 中的核心概念及其解释: 1. **连接点(JoinPoint)** 连接点是指程序执行过程中的某个位置,例如方法调用或异常抛出。在 Spring AOP 中,连接点通常表示为方法的执行[^3]。 2. **切入点(Pointcut)** 切入点是一个描述符,用于匹配特定的连接点。它定义了哪些连接点需要应用通知。例如,可以定义一个切入点来匹配所有以 `save` 开头的方法[^3]。 3. **通知(Advice)** 通知是在特定连接点处执行的操作,即共性功能。Spring AOP 提供了五种类型的通知: - **Before Advice**:在方法执行前运行。 - **After Returning Advice**:在方法成功返回后运行。 - **After Throwing Advice**:在方法抛出异常时运行。 - **After (Finally) Advice**:无论方法是否抛出异常,都会运行。 - **Around Advice**:围绕方法执行,可以在方法前后运行自定义逻辑[^3]。 4. **切面(Aspect)** 切面是通知和切入点的结合体,描述了在哪执行什么操作。切面通常是通过注解或 XML 配置实现的[^3]。 5. **目标对象(Target Object)** 被代理的对象,即包含业务逻辑的类实例。 6. **代理对象(Proxy Object)** AOP 框架生成的对象,用于拦截对目标对象的方法调用,并在适当时候应用通知。 --- #### Spring AOP 实现方式 Spring AOP 支持两种主要的实现方式:基于注解和基于 XML 的配置。 1. **基于注解的实现** 使用 `@Aspect` 注解定义切面类,并使用以下注解定义通知: ```java @Before("execution(* com.example.service.*.*(..))") public void beforeAdvice() { System.out.println("Before advice executed"); } ``` 2. **基于 XML 的实现** 在 XML 配置文件中定义切面和通知。以下是一个简单的示例: ```xml <aop:config> <aop:aspect id="logging" ref="loggingBean"> <aop:before method="beforeAdvice" pointcut="execution(* com.example.service.*.*(..))"/> </aop:aspect> </aop:config> ``` --- #### 图解 Spring AOP 工作流程 以下是 Spring AOP 的工作流程图解: ``` +-------------------+ | 客户端调用 | | 方法 | +-------------------+ | v +-------------------+ | 代理对象 | | (拦截方法调用)| +-------------------+ | v +-------------------+ | 应用通知 | | (Before/After)| +-------------------+ | v +-------------------+ | 目标对象方法 | | 执行业务逻辑 | +-------------------+ ``` - **客户端调用方法**:应用程序发起对目标对象方法的调用。 - **代理对象拦截**:代理对象捕获方法调用并决定是否应用通知。 - **应用通知**:根据切入点规则,执行相应的通知逻辑。 - **目标对象执行**:最终调用目标对象的方法以完成业务逻辑。 --- #### Spring AOP 与 AspectJ 对比 | 特性 | Spring AOP | AspectJ | |------------------|------------------------------------|----------------------------------| | 实现方式 | 运行时动态代理 | 编译时/类加载时织入 | | 性能 | 较慢(代理调用) | 接近原生性能 | | 连接点支持 | 仅方法级别 | 字段、构造器、静态初始化块等 | | 织入时机 | 运行时 | 编译期或类加载期 | | 依赖 | 轻量(仅需 Spring) | 需要 AspectJ 编译器或 LTW | | 适用场景 | 简单切面(事务、日志等) | 复杂切面(性能监控等) | --- ### 示例代码 以下是一个基于注解的 Spring AOP 示例: ```java // 定义切面类 @Aspect @Component public class LoggingAspect { @Before("execution(* com.example.service.*.*(..))") public void logBefore(JoinPoint joinPoint) { System.out.println("Method " + joinPoint.getSignature().getName() + " is called."); } @AfterReturning(pointcut = "execution(* com.example.service.*.*(..))", returning = "result") public void logAfterReturning(Object result) { System.out.println("Method returned with result: " + result); } } ``` ```java // 目标类 @Service public class MyService { public String performTask() { return "Task completed"; } } ``` ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值