AOP介绍
参考博文
1.什么是AOP思想?
AOP (Aspect Orient Programming),直译过来就是 面向切面编程。AOP 是一种编程思想,是面向对象编程(OOP)的一种补充。面向对象编程将程序抽象成各个层次的对象,而面向切面编程是将程序抽象成各个切面。
从《Spring实战(第4版)》图书中扒了一张图:
2.AOP一般用来干什么
- 事务处理:执行方法前,开启事务,执行完成后关闭事务,出现异常后回滚事务
- 权限判断:在执行方法前,判断是否具有权限
- 日志:在执行前进行日志处理
举个例子,你想给你的网站记录用户访问日志:
对某些url,是不用记录的,而有些是需要记录的
如果你依然使用OOP,面向对象,
那你只能在那些url对应的Controller代码里面,一个一个写上日志记录的代码
而如果你使用了AOP呢?
无需在控制类添加代码,直接添加一个日志类,来进行无侵入式通过切面来记录日志。
3.AOP的优点
- 减少重复代码
- 提高开发效率
- 维护方便
4.AOP相关术语
术语 | 意义 |
---|---|
Joinpoint(连接点) | 所谓连接点是指那些被拦截到的点。在 spring 中,这些点指的是方法,因为 spring 只支持方法类型的连接点。 |
Pointcut(切入点) | 所谓切入点是指我们要对哪些 Joinpoint 进行拦截的定义。 |
Advice(通知) | 所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知。通知的类型:前置通知,后置通知,异常通知,最终通知,环绕通知。 |
Proxy(代理 | 一个类被 AOP 织入增强后,就产生一个结果代理类。 |
Aspect(切面) | 是切入点和通知(引介)的结合。 |
Introduction(引介) | 引介是一种特殊的通知在不修改类代码的前提下, Introduction 可以在运行期为类动态地添加一些方法或 Field。 |
Target(目标对象) | 代理的目标对象。 |
以注解的方式配置AOP
1.切点语法
我们使用execution指示器选择Instrument的play方法,方法表达式以 * 号开始,标识我们不关心方法的返回值类型。然后我们指定了全限定类名和方法名。对于方法参数列表,我们使用 … 标识切点选择任意的play方法,无论该方法的入参是什么。
多个匹配之间我们可以使用链接符 &&
、||
、!
来表示 “且”、“或”、“非”的关系。但是在使用 XML 文件配置时,这些符号有特殊的含义,所以我们使用 “and
”、“or
”、“not”
来表示。
举例:
限定该切点仅匹配的包是 com.sharpcj.aopdemo.test1
,可以使用
execution(* com.sharpcj.aopdemo.test1.IBuy.buy(..)) && within(com.sharpcj.aopdemo.test1.*)
在切点中选择 bean,可以使用
execution(* com.sharpcj.aopdemo.test1.IBuy.buy(..)) && bean(girl)
2.五种通知类型
注解(名称) | 作用 |
---|---|
@Before | 该通知方法会在目标方法调用之前执行 |
@After | 该通知方法会在目标方法返回或者异常后调用 |
@AfterReturning | 该通知方法会目标方法返回后调用,如果出现异常则不会调用 |
@AfterThrowing | 该通知方法会在目标方法抛出异常后调用 |
@Around(环绕通知) | 该通知法会将目标封装起来 |
3.简单的实例
User类
package test_aop.pojo;
import org.springframework.stereotype.Component;
@Component
public class User {
public String point(boolean s) {
System.err.println("User.point() arg s is==" + s);
return "message";
}
}
AdviceConfig类
package test_aop.advice;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;
// 一定要是用aspect注解
// true代表使用cglib代理方式(使用注解) false代表使用jdk动态代理模式
@EnableAspectJAutoProxy(proxyTargetClass = true)
@Component
@Aspect
public class UserAdvice {
// 方法路径
final static String FUNTION_NAME = "execution(* test_aop.pojo.User.point(..))";
// 在point调用之前执行
// point参数代表当前的目标方法,我们可以通过这个参数获取目标方法的 参数,返回值等相关信息
@Before(value = FUNTION_NAME)
public void before(JoinPoint point) {
System.out.println("UserAdvice.before()");
}
// 在point调用之后执行
// point参数代表当前的目标方法,我们可以通过这个参数获取目标方法的 参数,返回值等相关信息
@After(value = FUNTION_NAME)
public void after(JoinPoint point) {
System.out.println("UserAdvice.after()");
}
// 该通知方法会目标方法返回后调用,如果出现异常则不会调用
// returning参数指定用什么参数名接受返回的值
@AfterReturning(pointcut = FUNTION_NAME, returning = "re")
public void afterReturning(Object re) {
System.out.println("UserAdvice.afterReturning() re==" + re);
}
// 该通知方法会在目标方法抛出异常后调用
// throwing参数指定用什么名称的参数接受异常
@AfterThrowing(pointcut = FUNTION_NAME, throwing = "e")
public void afterThrowing(Exception e) {
System.err.println(e);
}
// 该通知法会将目标封装起来,必须执行proceed方法,相当与放行的操作
@Around(value = FUNTION_NAME)
public void arround(ProceedingJoinPoint p) throws Throwable {
System.out.println("UserAdvice.arround() before");
p.proceed();
System.out.println("UserAdvice.arround() after");
}
}
main类
package test_aop;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import test_aop.pojo.User;
@SpringBootApplication
// 扫描注解
@ComponentScan(basePackages = { "test_aop.pojo", "test_aop.advice" })
public class App {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(App.class, args);
User user = context.getBean(User.class);
user.point(true);
}
}
结果