Spring中的AOP原理

在学习了Springboot的启动源码之后,了解了Spring中IOC容器创建和初始化的过程,IOC和AOP是Spring框架的两大特性,现在开始学习AOP的原理,揭开AOP的神秘面纱。

众所周知,Spring的IOC和AOP特性节省了很多程序员的开发时间,使企业级项目的开发变得更加容易。IOC的思想主要是利用反射的技术加载那些在XML配置文件中定义或者利用注解修饰的配置类和一些单例的Bean对象,并且对这些对象的依赖提供自动注入的功能,将这些对象的管理由代码转移到Spring框架,减少了程序员new对象的次数。

而AOP主要是提供了一种面向切面的编程,可以将一些与业务无关的代码与业务代码分离开来(如日志记录、权限认证、事务处理等),使程序员在编写代码的时候可以专注于业务代码的开发,并且可以将这些多个业务模块公共的方法封装成一个可重用的模块,提高代码的复用性,降低多个业务模块之间的耦合。由于这个可重用模块中的某个方法会被用于协助多个业务模块,将每个方法都想象成一个点,多个点就可以组成一个面,因此AOP又称为面向切面编程。

AOP思想使OOP(面向对象编程)思想的一种补充,传统的OOP允许定义从上到下的关系(可以在父类中编写多个子类需要的公共方法供这些子类调用),但不适合定义从左到右的关系(如果来了一个新类,这个类中的方法也是子类所需要的公共方法,如何在不修改子类代码的情况下让子类能够使用新类中的方法(闭包原则))。

实现AOP的技术,主要分为两大类:一是采用动态代理技术,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行;二是采用静态织入的方式,引入特定的语法创建“切面”,从而使得编译器可以在编译期间织入有关“切面”的代码。

Springboot使用AOP的实例

第一步:导入相关依赖

<!-- aop依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
</dependency>

第二部:创建一个业务类

@Component
public class ServiceTest {

    public void voidNoneArgsPublicFunction(){
        System.out.println("voidNoneArgsPublicFunction方法执行");
    }

    public Object objectNoneArgsPublicFunction(){
        System.out.println("objectNoneArgsPublicFunction方法执行");
        return null;
    }

    public void voidPublicFunction(String arg){
        System.out.println("voidPublicFunction方法执行");
    }

    public Object objectPublicFunction(String arg){
        System.out.println("objectPublicFunction方法执行");
        return null;
    }

    public void voidThrowPublicFunction(String arg){
        System.out.println("voidThrowPublicFunction方法执行");
        throw new RuntimeException("voidThrowPublicFunction方法执行");
    }

}

第三部:创建一个切面

// 切面注解
@Aspect
// 将切面注册到IOC容器中
@Component
public class AOPTest {

    // 选择一个切入点,表示org.dsmserver.springtest.test.ServiceTest类下面的任意函数都为切入点,具体可以参考Pointcut语法
    @Pointcut("execution(* org.dsmserver.springtest.test.ServiceTest.*(..))")
    public void LogAspect(){}

    // 前置通知
    @Before("LogAspect()")
    public void doBefore(JoinPoint joinPoint){
        System.out.println(joinPoint.toShortString() + "的前置通知");
    }

    // 不带返回值函数的后置通知
    @After("LogAspect()")
    public void doAfter(JoinPoint joinPoint){
        System.out.println(joinPoint.toShortString() + "的无返回后置通知");
    }

    // 带返回值函数的后置通知
    @AfterReturning("LogAspect()")
    public void doAfterReturning(JoinPoint joinPoint){
        System.out.println(joinPoint.toShortString() + "的带返回后置通知");
    }

    // 抛出异常后的后置通知
    @AfterThrowing("LogAspect()")
    public void deAfterThrowing(JoinPoint joinPoint){
        System.out.println(joinPoint.toShortString() + "的异常后置通知");
    }

    // 环绕通知
    @Around("LogAspect()")
    public Object deAround(ProceedingJoinPoint joinPoint) throws Throwable{
        System.out.println(joinPoint.toString() + "的环绕通知");
        return joinPoint.proceed();
    }
}

第四步:调用ServiceTest类中的方法

@SpringBootTest
class SpringtestApplicationTests {

    @Autowired
    ServiceTest serviceTest;

    @Test
    void contextLoads() {
        System.out.println("----------------------------------------------");
        serviceTest.voidNoneArgsPublicFunction();
        System.out.println("----------------------------------------------");
        serviceTest.objectNoneArgsPublicFunction();
        System.out.println("----------------------------------------------");
        serviceTest.objectPublicFunction(null);
        System.out.println("----------------------------------------------");
        serviceTest.voidPublicFunction(null);
        System.out.println("----------------------------------------------");
        serviceTest.voidThrowPublicFunction(null);
        System.out.println("----------------------------------------------");
    }

}

运行结果如下图所示:

----------------------------------------------
execution(ServiceTest.voidNoneArgsPublicFunction())的环绕通知
execution(ServiceTest.voidNoneArgsPublicFunction())的前置通知
voidNoneArgsPublicFunction方法执行
execution(ServiceTest.voidNoneArgsPublicFunction())的带返回后置通知
execution(ServiceTest.voidNoneArgsPublicFunction())的无返回后置通知
----------------------------------------------
execution(ServiceTest.objectNoneArgsPublicFunction())的环绕通知
execution(ServiceTest.objectNoneArgsPublicFunction())的前置通知
objectNoneArgsPublicFunction方法执行
execution(ServiceTest.objectNoneArgsPublicFunction())的带返回后置通知
execution(ServiceTest.objectNoneArgsPublicFunction())的无返回后置通知
----------------------------------------------
execution(ServiceTest.objectPublicFunction(..))的环绕通知
execution(ServiceTest.objectPublicFunction(..))的前置通知
objectPublicFunction方法执行
execution(ServiceTest.objectPublicFunction(..))的带返回后置通知
execution(ServiceTest.objectPublicFunction(..))的无返回后置通知
----------------------------------------------
execution(ServiceTest.voidPublicFunction(..))的环绕通知
execution(ServiceTest.voidPublicFunction(..))的前置通知
voidPublicFunction方法执行
execution(ServiceTest.voidPublicFunction(..))的带返回后置通知
execution(ServiceTest.voidPublicFunction(..))的无返回后置通知
----------------------------------------------
execution(ServiceTest.voidThrowPublicFunction(..))的环绕通知
execution(ServiceTest.voidThrowPublicFunction(..))的前置通知
voidThrowPublicFunction方法执行
execution(ServiceTest.voidThrowPublicFunction(..))的异常后置通知
execution(ServiceTest.voidThrowPublicFunction(..))的无返回后置通知

java.lang.RuntimeException: voidThrowPublicFunction方法执行

从结果中可以看出,最先执行的是环绕通知,环绕通知中必须执行返回joinPoint.proceed();的执行结果,若将joinPoint.proceed();换成null;则会出现下面的情况:

----------------------------------------------
execution(ServiceTest.voidNoneArgsPublicFunction())的环绕通知
----------------------------------------------
execution(ServiceTest.objectNoneArgsPublicFunction())的环绕通知
----------------------------------------------
execution(ServiceTest.objectPublicFunction(..))的环绕通知
----------------------------------------------
execution(ServiceTest.voidPublicFunction(..))的环绕通知
----------------------------------------------
execution(ServiceTest.voidThrowPublicFunction(..))的环绕通知
----------------------------------------------

从结果中可以看出不仅函数没有执行,前置通知也没有执行。因此环绕通知可以控制目标方法的执行,这也是环绕通知和前置、后置通知最大的区别。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值