什么是AOP
Aspect Oriented Programming:面向切面编程
什么时候会出现面向切面编程的需求?
按照软件重构的思想,如果多个类中出现重复的代码,就应该考虑定义一个共同的抽象类,将这些共同的代码提取到抽象类中,比如Teacher,Student都有username,那么就可以把username及相关的get、set方法抽取到SysUser中,这种情况,我们称为纵向抽取。
但是如果,我们的情况是以下情况,又该怎么办?
给所有的类方法添加性能检测,事务控制,该怎么抽取?
PerformanceMonitor
TransactionManager
AOP就是希望将这些分散在各个业务逻辑代码中的相同代码,通过横向切割的方式抽取到一个独立的模块中,让业务逻辑类依然保存最初的单纯。
抽取出来简单,难点就是如何将这些独立的逻辑融合到业务逻辑中,完成跟原来一样的业务逻辑,这就是AOP解决的主要问题。
AOP术语
l连接点(Joinpoint)
程序执行的某个特定位置,如某个方法调用前,调用后,方法抛出异常后,这些代码中的特定点称为连接点。简单来说,就是在哪加入你的逻辑增强
l切点(PointCut)
每个程序的连接点有多个,如何定位到某个感兴趣的连接点,就需要通过切点来定位。
比如,连接点--数据库的记录,切点--查询条件
l增强(Advice)
增强是织入到目标类连接点上的一段程序代码。
在Spring中,像BeforeAdvice等还带有方位信息
l目标对象(Target)
需要被加强的业务对象
l织入(Weaving)
织入就是将增强添加到对目标类具体连接点上的过程。
l代理类(Proxy)
一个类被AOP织入增强后,就产生了一个代理类。
l切面(Aspect)
切面由切点和增强组成,它既包括了横切逻辑的定义,也包括了连接点的定义,SpringAOP就是将切面所定义的横切逻辑织入到切面所制定的连接点中。
userBiz add update del
couseBiz add update del
定义一个范围给哪些类的哪些方法
增强:加事务的控制
AOP实现者
lAspectJ
AspectJ是语言级的AOP实现,2001发布,扩展了Java语言,定义了AOP语法,能够在编译期通过提供横切代码的织入,所以它有一个专门的编译器用来生成遵守Java字节码规范的class文件
lSpringAOP
SpringAOP使用纯Java实现,在运行期通过代理的方式向目标类织入增强代码。
SpringAOP代理机制
基于JDK的动态代理,接口
基于CGlib的动态代理,类
问题
现有一个UserDao的实现,想在原有的实现基础上添加新的业务处理,怎么办?
假如有多个实现
什么是代理模式
我们假设有一个UserDao和一个TransactionManager类
静态代理的问题:
1,假如还有其他的dao,则还需要再创建新的proxy
2,如果目标对象的方法发生改变,比如方法名做了修改,则代理类也要做修改
基于JDK的动态代理
关键类:
InvocationHandler:其作用就是将横切逻辑代码和业务逻辑代码编织到一起的编织器。
Proxy则通过newProxyInstance根据handler来创建一个符合第三方接口的代理类
基于接口的模式
基于CGlib的动态代理
CGlib采用非常底层的字节码技术,可以为一个类创建子类,并在子类中采用方法拦截技术拦截所有父类方法的调用,并织入横切逻辑
扩展
如果要多层怎么办?
AOP小结
这种实现方式存在三个明显需要改进的地方:
1.目标类的所有方法都添加了性能监视横切逻辑,而有时,这并不是我们所期望的,我们可能只希望对业务类中的某些特定方法添加横切逻辑;
2.我们通过硬编码的方式指定了织入横切逻辑的织入点,即在目标类业务方法的开始和结束前织入代码;
3.我们手工编写代理实例的创建过程,为不同类创建代理时,需要分别编写相应的创建代码,无法做到通用。
Spring AOP的主要工作就是围绕以上三点展开:Spring
AOP通过Pointcut(切点)指定在哪些类的哪些方法上织入横切逻辑,通过Advice(增强)描述横切逻辑和方法的具体织入点(方法前、方法后、方法的两端等)。此外,Spring通过Advisor(切面)将Pointcut和Advice两者组装起来。有了Advisor的信息,Spring就可以利用JDK或CGLib的动态代理技术采用统一的方式为目标Bean创建织入切面的代理对象了。
创建增强
Spring使用增强类定义横切逻辑,同时由于Spring只支持方法连接点,增强还包括了在方法的哪一点加入横切代码的方位信息,所以增强既包含横切逻辑,还包含部分连接点的信息
增强类型
AOP联盟为增强定义了org.aopalliance.aop.Advice接口,Spring支持5种类型的增强
前置增强:org.springframework.aop.BeforeAdvice代表前置增强,因为Spring只支持方法级的增强,所以MethodBeforeAdvice是目前可用的前置增强,表示在目标方法执行前实施增强,而BeforeAdvice是为了将来版本扩展需要而定
后置增强:org.springframework.aop.AfterReturningAdvice代表后增强,表示在目标方法执行后实施增强;
环绕增强:org.aopalliance.intercept.MethodInterceptor代表环绕增强,表示在目标方法执行前后实施增强;
异常抛出增强:org.springframework.aop.ThrowsAdvice代表抛出异常增强,表示在目标方法抛出异常后实施增强;
案例:
创建切面
我们可能注意到一个问题:增强被织入到目标类的所有方法中,假设我们希望有选择地织入到目标类某些特定的方法中,就需要使用切点进行目标连接点的定位了。描述连接点是进行AOP编程最主要的工作
增强提供了连接点方位信息:如织入到方法前面、后面等,而切点进一步描述织入到哪些类的哪些方法上。
Spring通过org.springframework.aop.Pointcut接口描述切点,Pointcut由ClassFilter和MethodMatcher构成,它通过ClassFilter定位到某些特定类上,通过MethodMatcher定位到某些特定方法上,这样Pointcut就拥有了描述某些类的某些特定方法的能力。
Spring使用org.springframework.aop.Advisor接口表示切面的概念,一个切面同时包含横切代码和连接点信息
从广义上说,增强其实就是一种最简单的切面,它既包括横切代码也包括切点信息,只不过它的切点只是简单的方法相对位置的信息。所以增强一般需要和切点联合才可以表示一个更具实用性的切面。
AspectJ
Spring AOP,它包括基于XML配置的AOP和基于@AspcetJ注解的AOP,这两种方法虽然在配置切面时的表现方式不同,但底层都是采用动态代理技术(JDK代理或CGLib代理)。Spring可以集成AspectJ,但AspectJ本身并不属于Spring AOP的范畴。
Spring在处理@Aspect注解表达式时,需要将Spring的asm模块添加到类路径中。asm是轻量级的字节码处理框架,因为Java的反射机制无法获取入参名,Spring就利用asm处理@AspectJ中所描述的方法入参名。
也可以通过配置的方式来生成代理对象
工作原理:
首先,在配置文件中引入aop命名空间,然后通过aop命名空间的自动为Spring容器中那些匹配@AspectJ切面的Bean创建代理,完成切面织入。