面向切面编程
aop 全称 Aspect Oriented Programming ,面向切面,AOP主要实现的目的是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果。其与设计模式完成的任务差不多,是提供另一种角度来思考程序的结构,来弥补面向对象编程的不足。
1.构建项目,pom如下:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2.新建WebLogAspect类
在完成了引入AOP依赖包后,一般来说并不需要去做其他配置。也许在Spring中使用过注解配置方式的人会问是否需要在程序主类中增加@EnableAspectJAutoProxy来启用,实际并不需要。
@Component
@Aspect
public class WebLogAspect {
@Pointcut("execution(public * com.gwd.web..*.*(..))")
public void weblog() {}
@Before("weblog()")
public void webBefore() {
System.out.println("在更新之前");
System.out.println("stop");
return;
}
@After("weblog()")
public void webAfter() {
System.out.println("在更新之后");
}
@Around("weblog()")
public void webAround(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("环绕通知前……");
pjp.proceed();
System.out.println("环绕通知后……");
}
@AfterReturning("weblog()")
public void webAfterReturn() {
System.out.println("afterReturning ……");
}
}
实现AOP的切面主要有以下几个要素:
使用@Aspect注解将一个java类定义为切面类
使用@Pointcut定义一个切入点,可以是一个规则表达式,比如下例中某个package下的所有函数,也可以是一个注解等。
根据需要在切入点不同位置的切入内容
使用@Before在切入点开始处切入内容
使用@After在切入点结尾处切入内容
使用@AfterReturning在切入点return内容之后切入内容(可以用来对处理返回值做一些加工处理)
使用@Around在切入点前后切入内容,并自己控制何时执行切入点自身的内容
使用@AfterThrowing用来处理当切入内容部分抛出异常之后的处理逻辑
3.Controller
@RestController
public class TestController {
@RequestMapping("/testAop")
public void testAop() {
System.out.println("test进行中");
}
}
4.测试结果如下
环绕通知前……
在更新之前
stop
test进行中
环绕通知后……
在更新之后
afterReturning ……
5.流程
在一个方法只被一个aspect类拦截时,aspect类内部的 advice 将按照以下的顺序进行执行。
正常情况下:
异常情况下:
6.切面执行顺序
由于通过AOP实现,程序得到了很好的解耦,但是也会带来一些问题,比如:我们可能会对Web层做多个切面,校验用户,校验头信息等等,这个时候经常会碰到切面的处理顺序问题。
所以,我们需要定义每个切面的优先级,我们需要@Order(i)注解来标识切面的优先级。i的值越小,优先级越高。假设我们还有一个切面是CheckNameAspect用来校验name必须为didi,我们为其设置@Order(10),而上文中WebLogAspect设置为@Order(5),所以WebLogAspect有更高的优先级,这个时候执行顺序是这样的:
• 在@Before中优先执行@Order(5)的内容,再执行@Order(10)的内容
• 在@After和@AfterReturning中优先执行@Order(10)的内容,再执行@Order(5)的内容
所以我们可以这样子总结:
• 在切入点前的操作,按order的值由小到大执行
• 在切入点后的操作,按order的值由大到小执行
7.Aop常用术语
•切面(Aspect):
一个关注点的模块化,这个关注点可能会横切多个对象。事务管理是J2EE应用中一个关于横切关注点的很好的例子。在Spring AOP中,切面可以使用基于模式或者基于@Aspect注解的方式来实现。
•连接点(Joinpoint):
在程序执行过程中某个特定的点,比如某方法调用的时候或者处理异常的时候。在Spring AOP中,一个连接点总是表示一个方法的执行。
•通知(Advice):
在切面的某个特定的连接点上执行的动作。其中包括了“around”、“before”和“after”等不同类型的通知(通知的类型将在后面部分进行讨论)。许多AOP框架(包括Spring)都是以拦截器做通知模型,并维护一个以连接点为中心的拦截器链。
•切入点(Pointcut):
匹配连接点的断言。通知和一个切入点表达式关联,并在满足这个切入点的连接点上运行(例如,当执行某个特定名称的方法时)。切入点表达式如何和连接点匹配是AOP的核心:Spring缺省使用AspectJ切入点语法。
•引入(Introduction):
用来给一个类型声明额外的方法或属性(也被称为连接类型声明(inter-type declaration))。Spring允许引入新的接口(以及一个对应的实现)到任何被代理的对象。例如,你可以使用引入来使一个bean实现IsModified接口,以便简化缓存机制。
•目标对象(Target Object):
被一个或者多个切面所通知的对象。也被称做被通知(advised)对象。既然Spring AOP是通过运行时代理实现的,这个对象永远是一个被代理(proxied)对象。
•AOP代理(AOP Proxy):
AOP框架创建的对象,用来实现切面契约(例如通知方法执行等等)。在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。
•织入(Weaving):
把切面连接到其它的应用程序类型或者对象上,并创建一个被通知的对象。这些可以在编译时(例如使用AspectJ编译器),类加载时和运行时完成。Spring和其他纯Java AOP框架一样,在运行时完成织入。
8.切入点表达式
切入点表达式的格式:
execution([可见性] 返回类型 [声明类型].方法名(参数) [异常])
其中【】中的为可选,其他的还支持通配符的使用:
•*:匹配所有字符
•..:一般用于匹配多个包,多个参数
•+:表示类及其子类
运算符有:&&、||、!
常见表达式:
•包名切面
对 com.app.controller 包中所有的类的所有方法切面
@Pointcut("execution(public * com.app.controller.*.*(..))")
•包名及子包切面
对 com.app.controller 及其子包中所有的类的所有方法切面
@Pointcut("execution(public * com.app.controller..*.*(..))")
•类名切面
只针对 StudentController 类切面
@Pointcut("execution(public * com.app.controller.StudentController.*(..))")