Skip to content

概念

AOP:Aspect Oriented Programming:面向切面编程,AOP底层走的就是代理模式

使用场景:事务管理、日志管理、权限控制等,这些服务与业务无关,但是又与业务模块有直接的关系,这些被称为交差业务模块,这些交叉业务模块就需要使用AOP来实现


maven:

xml
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.5</version>
</dependency>

术语

  • 目标对象(target):需要被增强的对象
  • 连接点(JoinPoint):目标对象中定义的所有方法
  • 通知(Advice):增强的功能,例:事务管理中,开启事务、提交事务、···
  • 切面(Aspect):抽取功能增强的类就是切面类,例:事务管理器;在切面类中定义通知类(增强的功能)
  • 切点(Pointcut):连接点中需要被增强的方法
  • 织入(Weaving):将通知(增强的功能)动态添加到切点的过程就是织入
  • 代理对象(Proxy):通过动态代理生成的具有增强功能的对象

通知

名称标签注解说明
前置通知<aop:before>@Before在切入点方法之前执行
后置通知<aop:afterReturning>@AfterReturning在切入点方法正常运行之后执行
异常通知<aop:afterThrowing>@AfterThrowing在切点方法发生异常的时候执行
最终通知<aop:after>@After无论切入点方法执行时是否有异常,都会执行
环绕通知<aop:around>@Around可以灵活实现四大通知的所有效果
注意:通常情况下,环绕通知都是独立使用的

属性:

  • method :指定通知中方法的名称
  • pointct :定义切入点表达式
  • pointcut-ref :指定切入点表达式的引用

基于XML的AOP

在spring的配置文件(applicationContext.xml)中进行配置,首先切面类需要有注解 @component ,如果没有需要在配置文件中Bean一下配置

xml
<!--AOP-->
<aop:config>
    <!--切面-->
    <aop:aspect id="切面类名" ref="切面类名">
        <!--切点配置-->
        <aop:pointcut id="aop" expression="execution(public void service.Service.*(..))"/>
        <!--前置通知-->
        <aop:before method="begin" pointcut-ref="aop"/>
        <!--正常处理业务完成通知-->
        <aop:after-returning method="commit" pointcut-ref="aop"/>
        <!--异常通知-->
        <aop:after-throwing method="rollback" pointcut-ref="aop"/>
        <!--后置通知-->
        <aop:after method="close" pointcut-ref="aop"/>
    </aop:aspect>
</aop:config>

XML环绕通知

在切面中定义环绕方法

java
public Object around(ProceedingJoinPoint point) {
    Object o = null;
    begin();
    try {
        o = point.proceed();
        commit();
    } catch (Throwable e) {
        rollback();
    } finally {
        close();
    }
    return o;
}

xml进行配置

xml
<!--AOP-->
<aop:config>
    <!--切面-->
    <aop:aspect id="userTransactionManager" ref="userTransactionManager">
        <!--切点配置-->
        <aop:pointcut id="aop" expression="execution(public void service.Service.*(..))"/>
        <!--环绕增强-->
        <aop:around method="around" pointcut-ref="aop"/>
    </aop:aspect>
</aop:config>

配值环绕增强之后,可以代替上面配置的四项增强

切点表达式

execution([修饰符] 返回类型 [全限定类名].方法名(参数列表)[异常列表])

修饰符:不写就是4个全包括 public、protected、private、default

返回类型:必写项,*表示所有类型

全限定类名:选填项 ..表示当前包及子包下的所有类;*表示当前包及子包下的所有类;不写表示所有类

方法名:必写项,*表示所有方法;String * 表示以当前字符串开头的所有方法;如:set * 表示以set开头的所有方法

参数列表:必写项 ()表示没有参数的方法;(*)表示只有一个参数的方法;(*,string) 表示第一个参数随意,第二个参数是String类型的方法

基于注解的AOP

开启 spring 对注解 AOP 的支持:<aop:aspectj-autoproxy/>;也可以不加,使用注解也行

在开发当中尽量不要同时使用注解的四大通知,特殊场景推荐使用:环绕通知

切面类:

java
@Component
// 切面类
@Aspect
public class UserTransactionManager {
    // 前置通知
    @Before("execution(* service.Service.*(..))")
    public void begin() {
        System.out.println("我开启了事务");
    }

    // 后置通知
    @AfterReturning("execution(* service.Service.*(..))")
    public void commit() {
        System.out.println("我提交了事务");
    }

    // 异常通知
    @AfterThrowing("execution(* service.Service.*(..))")
    public void rollback() {
        System.out.println("可能出错了,所以我回滚了事务");
    }

    // 最终通知
    @After("execution(* service.Service.*(..))")
    public void close() {
        System.out.println("我关闭了事务");
    }

    /*
     * 环绕通知
     * 注意:如果配置了环绕通知那么上面的通知不会生效,所以两个配置其中的一种即可
     * */
    @Around("execution(* service.Service.*(..))")
    public Object around(ProceedingJoinPoint point) {
        Object o = null;
        begin();
        try {
            o = point.proceed();
            commit();
        } catch (Throwable e) {
            rollback();
        } finally {
            close();
        }
        return o;
    }
}

上面的切点表达式可以进行简化,只需要轻易一个空方法方法上面加入注解 @Pointcut("execution(* service.Service.*(..))") ,到时候注解内只需要引入空方法名即可