面向切面编程(AOP)是经过另外一种思考方式来对面向对象编程(OOP)的补充。在抽象的结构中,OOP模块的基本单元是类,而AOP的基本单元是面。AOP的面可以跨越多个类型和对象来达成模块化。java
下面是根据个人理解画的图:spring
AOP提供了一个不一样的编程思路,不过springIoc并无依赖AOP,对于Ioc来讲,AOP能够提供支持但不是必须。编程
其中切入点能够和链接点合并,直接在通知中表示:
@Before("execution(com.dust.controller..(..))")
public void before() {/* 方法体 /}
该写法等价于:
@Pointcut("execution(com.dust.controller..(..))")
public void point(){}
@Befor("point()")
public void before(){/ 方法体 */}api
在AOP中,链接点与切入点的关联关系以及相应的判断规则是AOP的核心。切入点肯定了AOP入口,可是具体要执行哪部分则由链接点决定。链接点是方法执行的入口。数组
@Before("execution(* com.dust.controller.*.*(..))")
public void beforController() {
//在Controller被调用前
}
复制代码
这里能够知道,对AOP来讲,最小的单元是方法。AOP只能在方法和方法之间切入,而不能切入方法自己缓存
@AfterReturning("execution(* com.dust.controller.*.*(..))")
public void afterController() {
//在Controller执行完以后
}
复制代码
对方法返回值的获取:并发
@AfterReturning(
pointcut = "execution(* com.dust.controller.*.*(..))",
returning = "retVal")
public void afterController(Object retVal) {
//对方法返回值的获取
System.out.println(retVal.toString());
}
复制代码
其中returning中的参数名称必需要和advice方法的参数名称相同,当方法执行返回时,返回值将做为相应的参数值传递给advice方法。若是方法没有返回值,则该参数为null。app
@AfterThrowing("execution(* com.dust.controller.*.*(..))")
public void afterController() {
//在Controller抛出异常后执行
}
复制代码
也能够设置到抛出给定异常时才执行advice。框架
@AfterThrowing(
pointcut = "execution(* com.dust.controller.*.*(..))",
throwing = "ex")
public void afterController(NullPointerException ex) {
//抛出空指针异常时执行advice
}
复制代码
@After("execution(* com.dust.controller.*.*(..))")
public void afterController() {
//最终执行通知
}
复制代码
全部通知方法均可以声明一个类行为org.aspectj.lang.JoinPoint的参数。模块化
环绕通知声明的参数为ProceedingJoinPoint,由于须要执行ProceedingJoinPoint的proceed()方法。而其余的通知则不须要。
JoinPoint提供了不少有用的方法:
一般通知方法获取方法参数除了上述经过JoinPoint获取外还能够经过pointcut获取
@Pointcut("execution(* com.example.springdemo.controller.*.*(..)) && args(address, text, ..)")
public void inController(String address, String text) {}
@Before("inController(address, text)")
public void beforController(String address, String text) {
System.out.println("在执行控制器以前,获取参数{address:" + address + ",text:" + text + "}");
}
复制代码
args(address, text, ..)切入点表达式匹配切入点方法的参数,然后传入给advice方法。 这是要求全部和该切入点匹配的链接点都须要接收参数,还能够单单在链接点接收参数:
@Pointcut("execution(* com.example.springdemo.controller.*.*(..))")
public void inController() {}
@Before("inController() && args(address, text, ..)")
public void beforController(String address, String text) {
System.out.println("在执行控制器以前,获取参数{address:" + address + ",text:" + text + "}");
}
复制代码
其余参数:代理对象(this),目标对象(target)和注释(@within, @target, @annotation, @args)均可以以相似的方式绑定。
因为并发问题:死锁。可能致使业务执行失败。下次执行又有可能执行成功,所以对于这种操做不但愿将重试交给用户来执行,这个能够交由系统来执行,这样对于用户来讲他仍是一次就执行成功了。
因为要尝试执行屡次process(),所以使用@Around环绕通知
@Aspect
@Configuration
public class AOPRedo {
//默认最大重试次数
private static final int DEFAULT_MAX_RETRIES = 2;
private int maxRetries = DEFAULT_MAX_RETRIES;
public void setMaxRetries(int maxRetries) {
this.maxRetries = maxRetries;
}
@Around("execution(* com.example.springdemo.controller.RestAPIController.*(..))")
public Object apiAroundController(ProceedingJoinPoint pjp) {
int num = 0;
Throwable throwable;
do {
num++;
try {
return pjp.proceed();
} catch (Throwable th) {
System.out.println("尝试捕获");
throwable = th;
}
} while (num <= maxRetries);
return null;
}
}
复制代码
其中重试次数能够交给配置文件,经过@PropertySource来导入配置信息。
@RestController
@RequestMapping("api")
public class RestAPIController {
@Autowired
EmailService emailService;
private int count = 0;
@RequestMapping("email")
public String email(String address, String info) throws NullPointerException {
if (count++ < 1) {
throw new NullPointerException();
}
return emailService.setEmail(address,info) + "执行次数:" + count;
}
}
复制代码
执行结果