1.AOP的简单理解
AOP全称:Aspect Oriented Programming;
面向切面编程是通过预编译方式和运行期动态代理的方式实现程序功能的统一维护的一种技术;
面向切面编程其实是凌驾于系统之上的一种设计思想,该思想不影响原始方法的调用;
能够进入切面中的必须与切入点表达式匹配,只有匹配才能进入切面中执行通知方法(特定的业务逻辑);
面向切面编程的思想是弥补了面向对象的不足;扩展性特别的好;并且多个切面互不影响。
2.AOP和OOP两种思想
OOP(面向对象编程)是将一段业务处理过程的实体及其行为和属性进行抽象封装。从而可以实现单元划分和代码重用,以便提高效率。
AOP(面向切面编程)是针对某一个特定的业务处理过程进行抽取形成一个切面。也就是说AOP处理的是某个阶段或者说业务处理时的某个步骤。可以极大的降低模块之间的耦合性和达到了很好的隔离效果。
通俗点理解,OOP思想面对的是一个对象,而AOP面对的是业务的某个阶段或步骤;如果几个或更多个的业务逻辑过程中,有重复的操作行为,就可以使用AOP提取出来,运用动态代理(JDK或CGLib),实现程序功能的统一维护。就比如说需要事务,日志,权限等这些共有的特定业务逻辑时,需要将这些特定的业务逻辑进行抽取形成切面。若使用OOP思想解决这类的问题就得在哪需要这段特定的业务逻辑就得掺杂到处理普通业务逻辑的代码中。这样就会有很多代码重复,并不高效,而AOP在这一点上高效的帮OOP解决了繁琐的代码重复问题。
3.AOP 专业用词:
切面(Aspect):一个关注点的模块化,这个关注点可能会横切多个对象。--------- > 共用的一个类
切入点(PointCut):匹配连接点的断言。 -------> 一种匹配规则,只有满足规则就可执行切面中的通知
通知(Advice):在切面的某个特定的连接点上执行的动作。------> 切面中的方法(主要实现额外操作)
连接点(JoinPoint):在程序执行过程中某个特定的点。-------> 由代理对象调用的那个方法
目标对象(Target Object):被一个或者多个切面所通知的对象。----->真实对象
Spring 中 AOP 代理由 Spring 的 IOC 容器负责生成、管理,其依赖关系也由 IOC 容器负责管理。
4.SpringAOP的实现原理
1)生成代理对象规则
如果被代理者实现了接口,则使用JDK的动态代理为其创建代理对象,如果没有实现接口,则使用cglib为其创建代理对象。
2)人为修改使用CGLib创建代理对象
<aop:config poxy-target-class="true" >
.....
</aop:config>
3)AOP调用过程
当用户获取对象时,首先会与切入点做匹配,如果匹配成功则为其创建代理对象;当代理对象调用方法时即连接点,就会调用与切入点绑定的通知方法。
5.五大通知
- 前置通知(Before advice):在目标方法执行之前执行
- 后置通知(After returning advice):在目标方法执行之后 执行
- 异常通知(After throwing advice):目标方法抛出异常后 执行
- 最终通知(After (finally) advice):在目标方法执行之后都会执行
- 环绕通知(Around Advice):在目标方法执行之前执行,执行之后执行
前四大通知都不能管理目标方法是否执行。也就是说跟目标方法不搭边
ProceedingJoinPoint 只能环绕通知中
注意: 若多个环绕通知同时执行时,会形成一种嵌套关系。反正最终代理对象调用的连接点只能执行一次不可能执行多次。
6.Spring中使用AOP通知时参数的使用
这是一个后置通知的配置:
throwing="...." 传的是一个异常对象
<aop:after-throwing method="afterThrow" pointcut-ref="pc" throwing="throwable"/>
其它几大通知类似配置即可。
在执行环绕通知中,若传入了一个对象,这时环绕通知有两个参数,ProceedingJoinPoint 参数必须是放置第一位,不然报错。切记切记!!
1 //环绕通知 2 public Object around(ProceedingJoinPoint point,Transcation tx){ 3 4 //执行连接点 5 point.proceed(); 6 7 8 }
7.AOP注解形式
@Aspect 标识在类上,标识该类是个切面类
@Before 前置通知
@AfterReturning 后置通知
@AfterThrowing 异常通知
@After 最终通知
@Around 环绕通知
1)开启注解
1 <!-- 开启AOP切面注解 --> 2 <aop:aspectj-autoproxy/>
2)配置切面
1 @Component 2 @Aspect 3 public class aspectclass { 4 //定义一个切入点 5 @Pointcut("execution(* Servlet..*.*(..))") 6 public void pointCut(){} 7 8 @Around("pointCut()")//或者直接写@Around("execution(* Servlet..*.*(..))") 9 public Object around(ProceedingJoinPoint point) throws Throwable{ 10 System.out.println("我是环绕通知。。。"); 11 System.out.println("目标类的名称:"+point.getTarget().getClass()); 12 System.out.println("方法的名称:"+point.getSignature().getName()); 13 long c1 = System.currentTimeMillis(); 14 Object proceed = point.proceed(); 15 long c2 = System.currentTimeMillis(); 16 System.out.println("方法执行的时间:"+(c2-c1)); 17 return proceed; 18 } 19 20 @AfterThrowing(value="pointCut()",throwing="e")//或者直接写@AfterThrowing(value="execution(* Servlet..*.*(..))",thowing="e") 21 public void exce(JoinPoint point,Throwable e){ 22 System.out.println("异常类:"+e.getClass()); 23 System.out.println("异常信息:"+e.getMessage()); 24 System.out.println("出现异常的方法:"+point.getSignature().getName()); 25 System.out.println("目标类:"+point.getTarget().getClass()); 26 } 27 28 }
8.切入点表达式匹配注解
1 @Around("execution(* Servlet..*.*(..)) && @annotation(ann)") 2 public Object aroundTx(ProceedingJoinPoint point,Transactional ann) throws Throwable{ 3 tx.begin(); 4 Object proceed = point.proceed(); 5 tx.commit(); 6 return proceed; 7 }
在进行匹配时,首先会匹配满足切入点表达式且同时含有注解的方法,之后再将注解类型进行匹配,如果匹配成功则执行通知方法,如果注解类型不匹配,则整个切入点不匹配,不执行通知方法。