Aop常见概念

2018, Dec 10    

AOP常见概念

面向切面编程(Aspect Oriented Programming,AOP)其实就是一种关注点分离的技术,是OOP的重要补充。

软件开发时经常提一个词叫做“业务逻辑”或者“业务功能”,我们的代码主要就是实现某种特定的业务逻辑。但是我们往往不能专注于业务逻辑,比如我们写业务逻辑代码的同时,还要写事务管理、缓存、日志等等通用化的功能,而且每个业务功能都要和这些业务功能混在一起,痛苦!所以,为了将业务功能的关注点和通用化功能的关注点分离开来,就出现了AOP技术。这些通用化功能的代码实现,对应的就是我们说的切面(Aspect)。

代码分开的同时,我们如何保证功能的完整性呢? 你的业务功能依然需要有事务和日志等特性,即切面最终需要合并 ,这个就叫织入

里就涉及AOP的底层技术啦,有三种方式:

  1. 编译时织入:在代码编译时,把切面代码融合进来,生成完整功能的Java字节码,这就需要特殊的Java编译器了,AspectJ属于这一类
  2. 类加载时织入:在Java字节码加载时,把切面的字节码融合进来,这就需要特殊的类加载器,AspectJ和AspectWerkz实现了类加载时织入
  3. 运行时织入:在运行时,通过动态代理的方式,调用切面代码增强业务功能,Spring采用的正是这种方式。动态代理会有性能上的开销,但是好处就是不需要神马特殊的编译器和类加载器啦,按照写普通Java程序的方式来就行了!

目标对象 :切面的织入发生在的地方

切面:通常是一个类,里面可以定义切入点和通知

@Aspect
@Component
public class WebLogAspect {
     Logger logger = LoggerFactory.getLogger(this.getClass());

     // xx的接口不使用AOP,独立返回格式
    @Pointcut("execution(public * cn.bookcycle.scusmsplatform.department.controller..*.*(..)) && !execution(public * cn.bookcycle.scusmsplatform.department.controller.DeveloperLoginController.*(..))")
    public void webLog(){}

    @Around("webLog()")
    public ResultBean doAround(ProceedingJoinPoint pjp) {
        ResultBean<?> result;
        try {
            result = (ResultBean<?>) pjp.proceed();
        } catch (Throwable e) {
            result = handlerException(pjp, e);
        }
        return result;
    }
 }

如何指定每个 aspect 的执行顺序呢

给aspect添加@Order注解,该注解全称为:org.springframework.core.annotation.Order

给aspect添加@Order注解,该注解全称为:org.springframework.core.annotation.Order

@Order(5)
@Component
@Aspect
public class Aspect1 {
    // ...
}

@Order(6)
@Component
@Aspect
public class Aspect2 {
    // ...
}

切入点 :定义在调用什么方法的时候的时候应用切面

@Pointcut("execution(public * cn.bookcycle.scusmsplatform.department.controller..*.*(..)) && !execution(public * cn.bookcycle.scusmsplatform.department.controller.DeveloperLoginController.*(..))")
    public void webLog(){}

切入点的定义和表达式

切入点表达式的定义算是整个AOP中的核心,有一套自己的规范

Spring AOP支持的切入点指示符:

(1)execution:用来匹配执行方法的连接点

A:@Pointcut(“execution(* com.aijava.springcode.service...(..))”)

第一个表示匹配任意的方法返回值,..(两个点)表示零个或多个,上面的第一个..表示service包及其子包,第二个表示所有类,第三个*表示所有方法,第二个..表示

方法的任意参数个数

B:@Pointcut(“within(com.aijava.springcode.service.*)”)

within限定匹配方法的连接点,上面的就是表示匹配service包下的任意连接点

C:@Pointcut(“this(com.aijava.springcode.service.UserService)”)

this用来限定AOP代理必须是指定类型的实例,如上,指定了一个特定的实例,就是UserService

D:@Pointcut(“bean(userService)”)

bean也是非常常用的,bean可以指定IOC容器中的bean的名称

通知 :定义什么时候进行织入

@Around("webLog()")
    public ResultBean doAround(ProceedingJoinPoint pjp) {
        ResultBean<?> result;
        try {
            result = (ResultBean<?>) pjp.proceed();
        } catch (Throwable e) {
            result = handlerException(pjp, e);
        }
        return result;
    }

通知类型介绍

(1)Before:在目标方法被调用之前做增强处理,@Before只需要指定切入点表达式即可

(2)AfterReturning:在目标方法正常完成后做增强,@AfterReturning除了指定切入点表达式后,还可以指定一个返回值形参名returning,代表目标方法的返回值

(3)AfterThrowing:主要用来处理程序中未处理的异常,@AfterThrowing除了指定切入点表达式后,还可以指定一个throwing的返回值形参名,可以通过该形参名

来访问目标方法中所抛出的异常对象

(4)After:在目标方法完成之后做增强,无论目标方法时候成功完成。@After可以指定一个切入点表达式

(5)Around:环绕通知,在目标方法完成前后做增强处理,环绕通知是最重要的通知类型,像事务,日志等都是环绕通知,注意编程中核心是一个ProceedingJoinPoint