面向切面编程

面向切面编程(Aspect Oriented Programming,AOP)是什么?

  1. AOP是一种编程范式,不是编程语言;
  2. AOP解决特定问题,但不能解决所有问题;
  3. OOP的补充,而不是其替代。

AOP为什么出现?

  1. 提高代码重用性;
  2. 概念分离:分离功能性需求和非功能性需求。将功能性需求从非功能性需求中分离出来。

应用场景

  1. 权限控制
  2. 缓存控制
  3. 事务控制
  4. 审计日志
  5. 性能监控
  6. 分布式追踪
  7. 异常处理

Spring AOP的通过代理实现

通过DefaultAopProxyFactory.java源码可以看到AOP由jdk和cglib两种方式实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}

Spring AOP的使用

在Spring中主要使用注解@Aspect、@Pointcut、@Before、@After、@AfterReturning、@AfterThrowing以及@Around进行面向切面编程。

1
2
3
4
5
6
7
8
9
10
11
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Aspect {

/**
* Per clause expression, defaults to singleton aspect
* <p/>
* Valid values are "" (singleton), "perthis(...)", etc
*/
public String value() default "";
}

如上代码是@Aspect注解的定义,ElementType.TYPE可以看出该注解作用目标是接口、类、枚举、注解,@Aspect注解,Spring通过@Aspect注解切面并把它应用到目标对象上。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Pointcut {

/**
* The pointcut expression
* We allow "" as default for abstract pointcut
*/
String value() default "";

/**
* When compiling without debug info, or when interpreting pointcuts at runtime,
* the names of any arguments used in the pointcut are not available.
* Under these circumstances only, it is necessary to provide the arg names in
* the annotation - these MUST duplicate the names used in the annotated method.
* Format is a simple comma-separated list.
*/
String argNames() default "";
}

如上代码是@Pointcut注解的定义,value()用来定义切面所在的位置,定义方式有以下几种方式:

1
2
3
4
5
6
7
8
9
// execution定义切面,匹配符合表达式的所有方法
@Pointcut("execution(* com.xjtu.springbootstudy.aop.bymyself.service.ProgrammerService.work())")
// within用于匹配类,对应类下的所有方法都执行切面方法;
@Pointcut("within(com.xjtu.springbootstudy.aop.bymyself.service.*)")
// @annotation用于匹配自定义注解,如下面的@SignLog注解,再将@SignLog放在想定义切面的方法
@Pointcut("@annotation(com.xjtu.springbootstudy.aop.bymyself.annotation.SignLog)")
// @within用于匹配自定义注解,如下面的@SignLog注解,再将@SignLog放在想定义切面的类上
@Pointcut("@within(com.xjtu.springbootstudy.aop.bymyself.annotation.SignLog))")
public void log(){ }

如上对log()添加注解,@Pointcut注解中value定义切面位置,使用execution、within、@annotation、@within等方式设置切面。

  • @before在目标方法开始执行时执行;
  • @after在目标方法执行结束前执行;
  • @AfterReturning在目标方法执行正确返回前执行;
  • @AfterThrowing在目标方法执行异常时执行,
  • @Around环绕执行,一般是前4个无法实现期望功能时,才使用这个注解。

我写了一个简单示意切面程序,如下所示,对应执行结果也展示下下方。有个疑问就是@after和@AfterReturning注解的方法谁先执行?根据执行结果也可以看出
@after注解的方法先执行,@AfterReturning注解的方法后执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
@Before(value = "log()")
public void signIn(JoinPoint joinPoint){
logger.info("***********the people signs in******");
}

@After(value = "log()")
public void leaveWorkPlace(JoinPoint joinPoint){
logger.info("***********the people leaves workplace******");
}

@AfterReturning(value = "log()")
public void signOut(JoinPoint joinPoint){
logger.info("***********the people signs out successfully******");
}

@AfterThrowing(value = "log()",throwing = "throwable")
public void happenAccidentWhenWorking(JoinPoint joinPoint,Throwable throwable){
logger.info("***********the people happens accident when working******");
logger.info("***********"+throwable.getMessage()+"***********");
}

@Around(value = "log()")
public void happenAround(ProceedingJoinPoint joinPoint){
try {
joinPoint.proceed(joinPoint.getArgs());
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}

执行结果:
***********the people signs in****** // @before
I'm working in workplace! // 这是目标方法的执行结果
***********the people leaves workplace****** // @after
***********the people signs out successfully****** // @afterReturning
-------------本文结束感谢您的阅读-------------