面向切面编程是对于面向对象的一种补充,是一种很先进的思想,技术实现倒不是很高深,关于 Spring 的 AOP 需要掌握的有以下这些。
AOP 并不是 Spring 框架独有的,Spring 只是支持 AOP 编程的框架之一而已。不同的框架对 AOP 的支持各有特点,有些框架支持对方法及其参数做拦截,有些则只能对方法进行拦截。Spring 就是后者,它只支持对方法进行拦截的 AOP 。
_Spring的AOP.pngAOP概述
AOP 全称是 Aspect Oriented Programming ,中文含义面向切面编程。
AOP 的概念
网上官方说法:
Spring 的 AOP 指的就是在程序运行时,动态的将代码切入到指定位置上的一种编程思想。
自己的理解:
Spring 会通过配置文件是否进行了 AOP 相关的配置,来动态决定到底应该采用目标对象还是代理对象。代理对象是对原有目标对象的方法进行了增强,所以一旦采用了代理对象,就会出现和原有不一样的效果。
为什么要使用AOP
比如在 Spring 中,只要编写一个切面类,然后进行相关配置,Spring 就会去创建代理对象。如此就实现了在不修改原有代码的情况下,达到了和修改原有代码相同的效果。Spring 中的 AOP 通常都是用来进行权限校验、日志记录、性能监控、事务控制等功能的。
AOP 的底层实现原理
底层实现无非就是,在通过依赖注入给 Bean 对象的属性进行赋值的时候,根据配置来决定是注入原有目标对象还是代理对象。Spring 中生成代理对象使用的技术有两种,分别是 JDK 动态代理,和 CGLib 动态代理。
JDK 动态代理
要求目标类必须实现接口,才能采用这种方式产生代理对象。
CGLib 动态代理
针对没有实现接口的类产生代理,它是通过继承的方式实现的代理。
AOP的相关术语
下面以CustomerDao
类为例,来讲解 AOP 相关术语。
//目标类
public class CustomerDao {
public void save() {
System.out.println("保存客户...");
}
public void find() {
System.out.println("查询客户...");
}
public void update() {
System.out.println("修改客户...");
}
public void delete() {织入
System.out.println("删除客户...");
}
}
//切面类,下面是两个方法就是通知
public class MyAspectXML {
public void checkPri(JoinPoint jointPoint) {
System.out.println("权限校验 "+jointPoint);
}
public void writeLog(Object result) {
System.out.println("日志记录 "+result);
}
}
前面已经提到,Spring 支持的是基于方法的 AOP ,也就是说它只能对方法进行增强。
_AOP相关术语.png
相关术语:
-
连接点(Joinpoint)
,目标类的所有方法,因为所有方法都可能被增强 -
切入点(Pointcut)
,就是会被增强的方法 -
通知(Advice)
,就是切面类中由程序员添加的用于在切入点之前或之后执行的方法,比如权限验证方法和日志记录方法。 -
目标类(Target)
,就是原有目标类啊 -
代理类(Proxy)
,由 Spring 框架集合目标类、切面类和配置文件生成的对切入点进行了增强的那个类 -
切面(Aspect)
,切入点和通知的组合,就是一个切面。比如save()
和writeLog()
两个方法就组成了一个切面。面向切面编程就是在原有的方法之前或之后添加方法,组成一个新的方法呗。 -
织入(Weaving)
,就是把通知应用到切入点的这个过程就叫做织入。也就是生成代理对象的过程呗!
通知的类型
通知就是切面类中的一个个方法,通知的类型指的是它和切入点的组成关系。
_通知的类型.png
-
前置通知
,在切入点之前执行的方法,比如权限验证方法。 -
后置通知
,在切入点之后执行的方法,比如日志记录方法。 -
环绕通知
,在切入点之后和之后都会执行的方法,比如记录当前时间的方法,通常用于测试系统性能。 -
异常抛出通知
,在切入点出现异常的时候会执行的方法,无非就是捕捉到异常后,先执行此方法,再抛出异常而已。 -
最终通知
,无论切入点是否出现异常,都会执行该方法,其实就是把通知加在了finally
代码块之内呗。
切入点表达式的写法
如何通过配置指定哪些类的哪些方法需要进行增强呢?就是通过切入点表达式来实现的。
切入点表达式的格式是:方法返回值
+包名.类名.方法名(方法的参数)
,其中除了参数之外,其他任何一个位置都可以用通配符*
表示。方法的参数用..
表示。
实际使用范例如下:
<aop:pointcut expression="execution(* packageName.ProductDaoImpl.save(..))" id="pointcut1"/>
XML 配置AOP
配置 AOP 首先要明确几个角色,分别是切面类,目标类,切入点、通知。以上面的目标类和切面类为例,他们的配置如下
<aop:config>
<!-- 配置切入点 -->
<aop:pointcut expression="execution(* com.itheima.spring.demo3.ProductDaoImpl.save(..))" id="pointcut1"/>
<aop:pointcut expression="execution(* com.itheima.spring.demo3.ProductDaoImpl.delete(..))" id="pointcut2"/>
<aop:pointcut expression="execution(* com.itheima.spring.demo3.ProductDaoImpl.update(..))" id="pointcut3"/>
<aop:pointcut expression="execution(* com.itheima.spring.demo3.ProductDaoImpl.find(..))" id="pointcut4"/>
<!-- 配置切面类,此标签内可以配置多个切面 -->
<aop:aspect ref="myAspect">
<!-- 前置通知 -->
<aop:before method="checkPri" pointcut-ref="pointcut1"/>
<!-- 后置通知 -->
<aop:after-returning method="writeLog" pointcut-ref="pointcut2" returning="result"/>
<!-- 环绕通知 -->
<aop:around method="around" pointcut-ref="pointcut3"/>
<!-- 异常抛出通知 -->
<aop:after-throwing method="afterThrowing" pointcut-ref="pointcut4" throwing="ex"/>
<!-- 最终通知 -->
<aop:after method="after" pointcut-ref="pointcut4"/>
</aop:aspect>
</aop:config>
AspectJ注解配置AOP
用 AspectJ 注解的方式配置 AOP ,其实主要是需要配置切面类和通知,还有切入点。
切面类的配置:
-
@Aspect
,用此注解给切面类进行配置。
@Aspect//表明是切面类
public class MyAspectAnno {}
通知的配置:
-
@Before
,配置前置通知
@Before(value="execution(* com.itheima.spring.demo1.OrderDao.save(..))")
public void before() {
System.out.println("save方法前置增强=====");
}//其他通知同理
-
@AfterReturning
,配置后置通知 -
@Around
,环绕通知 -
@AfterThrowing
,配置异常抛出通知 -
@After
,配置最终通知
切入点的配置:
-
@Pointcut
,
@Pointcut(value="execution(* packageName.ProductDaoImpl.save(..))")
public void save() {
System.out.println("保存订单...");
}
网友评论