一.Aop简介
AOP(Aspect-Oriented Programming,面向切面编程)是Spring框架中的一个重要特性。AOP通过将与核心业务逻辑无关的横切关注点(如事务管理、日志记录、安全性检查等)独立出来,从而实现了代码的模块化和重用。
在传统的面向对象编程中,通常通过将功能逻辑嵌入到核心业务逻辑中来实现某些功能。然而,这种方式会导致代码的重复和耦合,使得应用程序的维护和扩展变得困难。
而AOP的思想是将这些横切关注点与核心业务逻辑进行解耦。在Spring框架中,AOP通过切面(Aspect)来实现。切面是一个跨越多个类的模块化单元,它定义了横切关注点的行为。
在Spring中,通过使用AOP,你可以将横切关注点抽象为一个切面,并将其应用到多个类或方法上。当程序执行到被切入的方法时,切面的代码将自动执行,完成预定义的功能。
二.AOP与OOP
AOP(Aspect Oriented Programming)面向切面编程是 Spring 框架最核心的组件之一,它通过对程序结构的另一种考虑,补充了 OOP(Object-Oriented Programming)面向对象编程。在 OOP 中模块化的关键单元是类,而在 AOP 中,模块化单元是切面。也就是说 AOP 关注的不再是类,而是一系列类里面需要共同能力的行为。
三.AOP中的概念和机制
在Spring框架中,AOP的主要概念和机制包括切面(Aspect)、切点(Pointcut)、通知(Advice)、织入(Weaving)和引入(Introduction)等。
- 1. 切面(Aspect):切面是横切关注点的模块化单元,它由切点和通知组成。切面定义了在特定切点插入的代码逻辑,即在何处和何时执行通知。
- 2.切点(Pointcut):切点用于定义切面将会生效的具体位置。通过切点表达式,可以选择需要被切入的方法或类。例如,通过表达式"execution(* com.example.service.*.*(..))"可以选择所有com.example.service包下的方法。
- 3.通知(Advice):通知是切面内部的代码逻辑,它决定了在切点何处执行切面的逻辑。Spring定义了多种通知类型,包括:
- - 前置通知(Before):在切点之前的代码逻辑执行。
- - 后置通知(After):在切点之后的代码逻辑执行(无论目标方法是否发生异常)。
- - 返回通知(After-returning):在切点之后的代码逻辑执行(只有目标方法正常返回时)。
- - 异常通知(After-throwing):在切点之后的代码逻辑执行(只有目标方法发生异常时)。
- - 环绕通知(Around):在切点之前和之后的代码逻辑执行,可以完全控制目标方法的执行。
- - 过滤通知(Filtering advice)是一种特殊类型的通知,它用于过滤切点上要执行的切面逻辑。过滤通知可以控制切面是否应该在特定的条件下执行。
- 4. 织入(Weaving):织入是将切面与目标对象的代码进行整合的过程。在Spring中,有两种织入方式:
- - 编译时织入(Compile-time weaving):在编译阶段,将切面织入目标类的字节码中。
- - 运行时织入(Runtime weaving):在运行时,动态地将切面织入目标对象中。
- 5. 引入(Introduction):引入是一种特殊的通知类型,它允许为目标对象添加新的方法或属性,而不需要修改目标对象的源码。通过引入,可以在不改变类继承关系的情况下,为目标对象添加额外的功能。
- 6.连接点(Joinpoint):程序执行过程中明确的点,如方法的调用,或者异常的抛出.
- 7.目标(Target):被通知(被代理)的对象
- 8.代理(Proxy):将通知应用到目标对象后创建的对象(代理=目标+通知)
- 9.切入点(Pointcut):多个连接点的集合,定义了通知应该应用到那些连接点。
- 10.适配器(Advisor):适配器=通知(Advice)+切入点(Pointcut)
四.日志通知
3.1. 前置通知
spring-context.xml:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" default-autowire="byType" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!--目标对象--> <bean class="com.aop.biz.impl.BookBizImpl" name="bookBiz"></bean> <!--通知--> <bean class="com.aop.biz.advice.MyMethodBeforeAdvice" id="methodBeforeAdvice"></bean> <!--代理--> <bean class="org.springframework.aop.framework.ProxyFactoryBean" id="bookProxy"> <!-- 配置目标对象--> <property name="target" ref="bookBiz"></property> <!-- 配置代理的接口--> <property name="proxyInterfaces"> <list> <value>com.aop.biz.IBookBiz</value> </list> </property> <!--配置通知--> <property name="interceptorNames"> <list> <value>methodBeforeAdvice</value> </list> </property> </bean> </beans>
package com.aop.biz.advice; import org.springframework.aop.MethodBeforeAdvice; import java.lang.reflect.Method; import java.util.Arrays; /** * 买书、评论前加系统日志 * @author Administrator * */ public class MyMethodBeforeAdvice implements MethodBeforeAdvice { @Override public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable { // 在这里,可以获取到目标类的全路径及方法及方法参数,然后就可以将他们写到日志表里去 String target = arg2.getClass().getName(); String methodName = arg0.getName(); String args = Arrays.toString(arg1); System.out.println("【前置通知:系统日志】:"+target+"."+methodName+"("+args+")被调用了"); } }
3.2.后置通知
package com.aop.biz.advice; import org.springframework.aop.AfterReturningAdvice; import java.lang.reflect.Method; import java.util.Arrays; /** * 买书返利 * @author Administrator * */ public class MyAfterReturningAdvice implements AfterReturningAdvice { @Override public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable { String target = arg3.getClass().getName(); String methodName = arg1.getName(); String args = Arrays.toString(arg2); System.out.println("【后置通知:买书返利】:"+target+"."+methodName+"("+args+")被调用了,"+"该方法被调用后的返回值为:"+arg0); } }
3.3. 环绕通知
package com.aop.advice; import java.util.Arrays; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; /** * 环绕通知 * 包含了前置和后置通知 * * @author Administrator * */ public class MyMethodInterceptor implements MethodInterceptor { @Override public Object invoke(MethodInvocation arg0) throws Throwable { String target = arg0.getThis().getClass().getName(); String methodName = arg0.getMethod().getName(); String args = Arrays.toString(arg0.getArguments()); System.out.println("【环绕通知调用前:】:"+target+"."+methodName+"("+args+")被调用了"); // arg0.proceed()就是目标对象的方法 Object proceed = arg0.proceed(); System.out.println("【环绕通知调用后:】:该方法被调用后的返回值为:"+proceed); return proceed; } }
3.4.异常通知
package com.aop.biz.advice; import org.springframework.aop.ThrowsAdvice; import com.aop.biz.Exception.PriceException; /** * 出现异常执行系统提示,然后进行处理。价格异常为例 * @author Administrator * */ public class MyThrowsAdvice implements ThrowsAdvice { public void afterThrowing(PriceException ex) { System.out.println("【异常通知】:当价格发生异常,那么执行此处代码块!!!"); } }
3.5.过滤通知
所有用到的bean:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" default-autowire="byType" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!--目标对象--> <bean class="com.aop.biz.impl.BookBizImpl" name="bookBiz"></bean> <!--通知--> <bean class="com.aop.biz.advice.MyMethodBeforeAdvice" id="methodBeforeAdvice"></bean> <bean class="com.aop.biz.advice.MyAfterReturningAdvice" id="myAfterReturningAdvice"></bean> <bean class="com.aop.biz.advice.MyMethodInterceptor" id="myMethodInterceptor"></bean> <bean class="com.aop.biz.advice.MyThrowsAdvice" id="myThrowsAdvice"></bean> <bean class="org.springframework.aop.support.RegexpMethodPointcutAdvisor" id="regexpMethodPointcutAdvisor"> <property name="advice" ref="myAfterReturningAdvice"></property> <property name="pattern" value=".*buy"></property> </bean> <!--代理--> <bean class="org.springframework.aop.framework.ProxyFactoryBean" id="bookProxy"> <!-- 配置目标对象--> <property name="target" ref="bookBiz"></property> <!-- 配置代理的接口--> <property name="proxyInterfaces"> <list> <value>com.aop.biz.IBookBiz</value> </list> </property> <!--配置通知--> <property name="interceptorNames"> <list> <value>methodBeforeAdvice</value> <!-- <value>myAfterReturningAdvice</value>--> <value>regexpMethodPointcutAdvisor</value> <value>myMethodInterceptor</value> <value>myThrowsAdvice</value> </list> </property> </bean> </beans>
五.AOP的使用场景
- Authentication 权限
- Caching 缓存
- Context passing 内容传递
- Error handling 错误处理
- Lazy loading 懒加载
- Debugging 调试
- logging, tracing, profiling and monitoring 记录跟踪 优化 校准
- Performance optimization 性能优化
- Persistence 持久化
- Resource pooling 资源池
- Synchronization 同步
- Transactions 事务