1.了解什么是AOP
AOP(Aspect Orient Programming),面向切面编程,是面向对象编程 OOP 的一种补充。在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程。例如转账功能,在转账代码的前后需要一些非业务方面的处理,权限控制,记录日志,事务的开启与结束,这些代码就可以使用AOP将其切入到转账代码的前后,这样就可以很好地分离业务代码和非业务代码。
AOP的优点就是降低代码之间的耦合,提高代码的复用性。
JDK 的动态代理,如果被代理了实现了接口,会默认使用jdk的动态代理。底层通过反射方式创建代理类的对象
CGLIB的动态代理,如果类没有实现接口,会使用CGLIB动态代理。底层是对代理类生成的class文件加载进来,通过修改其字节码生成子类来创建代理对象
spring之所以会引入这两种方式是因为其各有优缺点,
从性能上讲,使用字节码处理的CGLIB要比使用反射的JDK动态代理好。
从耦合度上讲,jdk要好于额外需要依赖字节码处理框架ASM的CGLIB。
2.掌握AOP相关术语
(1)目标对象(Target)
目标对象指 将要被增强的对象。即包含主业务逻辑的类的对象。上例中的UserDaoImpl 的对象若被增强,则该类称为目标类,该类对象称为目标对象。当然,不被增强,也就无所谓目标不目标了。
(2)切面(Aspect)
切面泛指非业务逻辑。上例中的事务处理、日志处理就可以理解为切面。常用的切面有通知,实际就是对业务逻辑的一种增强。
(3)连接点(JoinPoint)
连接点指可以被切面织入的方法。通常业务接口中的方法均为连接点。
(4)切入点(Pointcut)
切入点指切面具体织入的方法。在 UserDaoImpl 类中,若 addUser()被增强,而doOther()不被增强,则 addUser()为切入点,而 doOther()仅为连接点。 被标记为 final 的方法是不能作为连接点与切入点的,因为是不能被修改的,不能被增强的。
(5)通知(Advice)
通知是切面的一种实现,可以完成简单织入功能(织入功能就是在这里完成的)。上例中的MyInvocationHandler 就可以理解为是一种通知。换个角度来说,通知定义了增强代码切入到目标代码的时间点,是目标方法执行之前执行,还是之后执行等。通知类型不同,切入时间不同。切入点定义切入的位置,通知定义切入的时间。Advice有下面几种,这里使用常用的AspectJ方式:
前置通知(Before advice):在连接点之前执行,即目标方法执行之前执行。
后置通知(After returning advice):在连接点正常结束之后执行,如果连接点抛出异常,则不执行。
异常通知(After throwing advice):在连接点抛出异常后执行
最终通知(After (finally) advice):在连接点结束之后执行,无论是否抛出异常,都会执行。
环绕通知(Around advice):在连接点之前和之后均执行。
(6)织入(Weaving)
织入是指将切面代码插入到目标对象的过程。上例中 MyInvocationHandler 类中的 invoke()
方法完成的工作,就可以称为织入。
(7)aop代理(AOP proxy)
spring中的aop代理有两种:jdk自带的动态代理和CGLIB代理。
3.了解AOP的几种实现方式
1.ProxyFactoryBean配置和实现接口
2.使用Spring来定义纯粹的POJO切面,在对应的SpringXML文件中通过aop:config标签实现
3.使用@AspectJ注解驱动切面来实现(最常使用)
引入依赖:
<!--AOP相关jar包-->
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.3</version>
</dependency>
1.选择连接点:
Spring是方法级别的AOP框架,所以我们也是已某个类的方法作为连接点
2.创建切面:
相对于动态代理的概念而言,在切面类定义自己要增强的功能
<!--手动配置-->
<bean id="xmlAspect" class="cn.gok.services.XMLAspect"/>
<!--配置AOP-->
<aop:config>
<!--引入切面-->
<aop:aspect ref="xmlAspect">
<!--定义切入点:要对哪些地方进行功能增强-->
<aop:pointcut id="poin" expression="execution(* cn.gok.services.UserImpl.model1())"/>
<aop:before method="before" pointcut-ref="poin"/>
<aop:after method="after" pointcut-ref="poin"/>
<!--沿用自己定的规则切入-->
<aop:around method="around" pointcut="execution(* cn.gok.services.UserImpl.model2(int))"/>
</aop:aspect>
</aop:config>
基于注解的切面
@Component// 加载到Spring容器
@Aspect//切面的注解
public class Aspect2 {
/*
* 定义切入点
* */
@Pointcut("execution(* cn.gok.services.UserImpl.*(..))")
public void point() {
}
@Before("point()")
public void before() {
System.out.println("自动注入AOP方法执行前。。。。");
}
@AfterThrowing("point()")
public void afterThrowing(){
System.out.println("抛出错误之后!");
}
}