SpringAOP的理解和底层原理 - AspectJ静态代理

什么是AOP

AOP(Aspect-OrientedProgramming):面向切面编程。利用AOP可以对业务逻辑的各个部分进行隔离,从而是的业务逻辑各部分之间的耦合度降低,提高程序的可用性,同时提高了开发的效率。

为什么需要 AOP

想象下面的场景,开发中在多个模块间有某段重复的代码,我们通常是怎么处理的?显然,没有人会靠“复制粘贴”吧。在传统的面向过程编程中,我们也会将这段代码,抽象成一个方法,然后在需要的地方分别调用这个方法,这样当这段代码需要修改时,我们只需要改变这个方法就可以了。然而需求总是变化的,有一天,新增了一个需求,需要再多出做修改,我们需要再抽象出一个方法,然后再在需要的地方分别调用这个方法,又或者我们不需要这个方法了,我们还是得删除掉每一处调用该方法的地方。实际上涉及到多个地方具有相同的修改的问题我们都可以通过 AOP 来解决。

AOP 实现分类

AOP 要达到的效果是,保证开发者不修改源代码的前提下,去为系统中的业务组件添加某种通用功能。AOP 的本质是由 AOP 框架修改业务组件的多个方法的源代码,看到这其实应该明白了,AOP 其实就是代理模式的典型应用。
按照 AOP 框架修改源代码的时机,可以将其分为两类:

  • 静态 AOP 实现, AOP 框架在编译阶段对程序源代码进行修改,生成了静态的 AOP 代理类(生成的 *.class 文件已经被改掉了,需要使用特定的编译器),比如 AspectJ
  • 动态 AOP 实现, AOP 框架在运行阶段对动态生成代理对象(在内存中以 JDK 动态代理,或 CGlib 动态地生成 AOP 代理类),如 SpringAOP。


AOP基本概念
public class test {
    add()

    select()

    update()

    delete()

}
  • 连接点(JointPoint):类中可以被增强的方法。如以上代码中的四组方法都是连接点。
  • 切入点(Pointcut):带有通知的连接点,实际被真正增强的方法。比如以上代码,我只增强了add(), 则add()就是切入点
  • 通知(增强):在特定的切入点上,实际增加的通知(增强)处理。

通知类型
前置通知(@Before):在我们执行目标方法之前运行。
后置通知(@After ):在我们目标方法运行结束之后,不管有没有异常。(类似发finally)
返回通知(@AfterReturnning):在我们目标方法正常返回运行。
异常通知(@AfterThrowing) :在我们目标方法出现异常后运行。
环绕异常(@Around):动态代理,需要手动执行JoinPoint.procced() ,其实就是:执行我们的目标方法之前相当于前置通知, 执行之后就相当于我们后置通知。

  • 切面(Aspect):可以理解为动作,把通知(增强)应用到切点。
  • AOP代理:SpringAOP有两种代理模式:JDK动态代理,CGLIB代理。

AOP操作(基于注解方式实现)
前置知识

Spring框架一般基于AspectJ,实现AOP

AspectJ不是Spring组成部分,而是独立的AOP框架,一般AapectJ和Spring框架一起使用,实现AOP操作。


切入点表达式:知道对哪个类里面的哪个方法进行通知(增强)
语法结构:execution([权限修饰符][返回类型][全限定类名][方法名称] ([参数列表]) )

举例:对com.simon.TestAop类中的add方法进行增强

execution(* com.simon.TestAop.add(..))

举例:对com.simon.TestAop类中的所有方法进行增强

execution(* com.simon.TestAop.*(..))

注意:默认 权限修饰符public 可以省略。参数列表可以使用 … 表示有无参数均可,有参数可以是任意类型。使用…来表示当前包及其子包。

导入依赖

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.6</version>
            <scope>runtime</scope>
        </dependency>

创建增强的类

package simon.TestAop;

//增强的类
public class UserProxy {
    //前置通知
    public void before() {
        System.out.println("before----");
    }
}

创建被增强的类

package simon.TestAop;

//被增强的类
public class User {
    public void add() {
        System.out.println("User add----");
    }
}

进行通知的配置
1.开启注解扫描

  • 可使用xm配置(本次采用)
  • 可使用配置类代替xml
<!--开启注解扫描-->
    <context:component-scan base-package="simon.TestAop"/>

2.使用注解创建User 对象 ,可以被Spring扫描到。

package simon.TestAop;

import org.springframework.stereotype.Component;

//被增强的类
@Component
public class User {
    public void add() {
        System.out.println("User add----");
    }
}


3.使用注解创建UserProxy。

  • @Component可以Spring扫描到
  • @Aspect 生成代理对象
//增强的类
@Component
@Aspect  
public class UserProxy {
    //前置通知
    public void before() {
        System.out.println("before----");
    }
}

4.开启生成代理对象

  <!--开启Aspect  生成代理对象-->
    <aop:aspectj-autoproxy/>

5.在UserProxy代理类创建通知

package aopstudy.demo.TestAop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;


//增强的类
@Component
@Aspect  //生成代理对象
public class UserProxy {
    //前置通知
    @Before(value = "execution(* aopstudy.demo.TestAop.User.*(..))")
    public void before() {
        System.out.println("before----");
    }

    //后置通知
    @After(value = "execution(* aopstudy.demo.TestAop.User.*(..))")
    public void After() {
        System.out.println("After----");
    }

    //返回通知
    @AfterReturning(value = "execution(* aopstudy.demo.TestAop.User.*(..))")
    public void Return() {
        System.out.println("Return----");
    }

    //异常通知
    @AfterThrowing(value = "execution(* aopstudy.demo.TestAop.User.*(..))")
    public void AfterThrowing() {
        System.out.println("AfterThrowing----");
    }

    //环绕异常
    @Around(value = "execution(* aopstudy.demo.TestAop.User.*(..))")
    public void Around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("Around before----");

        //被增强的方法执行
        proceedingJoinPoint.proceed();

        System.out.println("Around after----");
    }
}

使用junit测试

import aopstudy.demo.TestAop.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;


public class testAop {

    @Test
    public void testAop() {
        ApplicationContext context = new ClassPathXmlApplicationContext("applications.xml");
        User user = context.getBean("user", User.class);
        user.add();
    }
}

**********运行记录************
Around before----
before----
User add----
Around after----
After----
Return----

由于没有异常,所以@AfterThrowing 没有执行。
接下来修改user,让他出现错误

package aopstudy.demo.TestAop;

import org.springframework.stereotype.Component;

//被增强的类
@Component
public class User {
    public void add() {
        System.out.println(10 / 0);
        System.out.println("User add----");
    }
}


继续执行junit

Around before----
before----
After----
AfterThrowing----

java.lang.ArithmeticException: / by zero
at aopstudy.demo.TestAop.User.add(User.java:9)
	at aopstudy.demo.TestAop.User$$FastClassBySpringCGLIB$$2315042d.invoke(<generated>)
	at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~

观察发现,UserProxy代理类中切入点重复使用了多次,如进行修改的话不是很方便,因此我们优化代码如下:

package aopstudy.demo.TestAop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;


//增强的类
@Component
@Aspect  //生成代理对象
public class UserProxy {

    //相同切入点抽取
    @Pointcut(value = "execution(* aopstudy.demo.TestAop.User.*(..))")
    private void point() {

    }

    //前置通知
    @Before(value = "point()")
    public void before() {
        System.out.println("before----");
    }

    //后置通知
    @After(value = "point()")
    public void After() {
        System.out.println("After----");
    }

    //返回通知
    @AfterReturning(value = "point()")
    public void Return() {
        System.out.println("Return----");
    }

    //异常通知
    @AfterThrowing(value = "point()")
    public void AfterThrowing() {
        System.out.println("AfterThrowing----");
    }

    //环绕异常
    @Around(value = "point()")
    public void Around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("Around before----");

        //被增强的方法执行
        proceedingJoinPoint.proceed();

        System.out.println("Around after----");
    }
}

若多个增强类对同一个方法进行增强。可以在增强类上用@Order(int )设置优先级,数字越小优先级越高

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值