Spring AOP面向切面编程(Aspect Oriented Programming)

面向切面编程(Aspect-Oriented Programming,AOP)是一种编程思想,它可以帮助我们更好地实现代码复用和模块化。AOP的核心概念是“切面”,它是一种横向的关注点,可以跨越多个不同的对象和模块,提供额外的功能和服务。可以在不修改原始代码的情况下对功能做增强

AOP中一些常用的术语及其定义

切面(Aspect):跨越一个或多个对象的横切关注点,即某些需要进行统一处理的代码片段的集合。

连接点(Join Point):程序执行中可以插入切面的特定点,比如方法调用、对象初始化和异常处理等。

切点(Pointcut):用于定义一组连接点的规则,指定切面代码应该插入到哪些连接点中。

通知(Advice):在连接点处执行的代码,可以在连接点之前、之后或者之后抛出异常时执行。

引入(Introduction):用于向现有的类中添加新方法或属性的方式,比如向现有类中添加一个新的接口。

织入(Weaving):将切面代码插入到目标对象中,生成新的代理对象的过程。

目标对象(Target Object):包含连接点的对象,即需要被代理的对象。

AOP代理(AOP Proxy):在目标对象上应用切面之后生成的代理对象,它将请求转发给目标对象并在必要时执行切面代码。

AOP 入门案例

案例设定:在接口执行前输出当前的系统时间

实现思路

1、导入坐标(pom.xml)
2、制作连接点(原始操作,Dao接口与实现类)
3、制作共性功能(通知类与通知)
4、定义切入点
5、绑定切入点与通知关系

具体代码

1、导入坐标

<dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
</dependency>
<dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
</dependency>

2、实现原始功能
定义一个bookDao类里面有两个方法
save方法执行会输出当前的系统时间,update执行不会输出当前系统时间
之后我们要把输出系统时间这个代码抽离出去,定义成一个单独的类

package com.example.demo.dao;

import org.springframework.stereotype.Component;

@Component
public class BookDao {
    public void save(){
        System.out.println(System.currentTimeMillis());
        System.out.println("book dao save");
    }
    public void update(){
        System.out.println("book dao update...");
    }
}

3、定义通知类,绑定切入点与通知类的关系

package com.example.demo.aop;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class MyAdvice {
    @Pointcut("execution( void com.example.demo.dao.BookDao.update())")
    private void pt(){}

    @Before("pt()")
    public void method(){
        System.out.println(System.currentTimeMillis());
    }
}

在这个类中我们定义输出当前时间的方法
@Aspect 告诉spring这个类是一个切面
@Pointcut 定义切点,在上面的代码中,我们定义了当代码执行到void 没有返回值,且是com.example.demo.dao.BookDao.update()这个方法时才绑定切点与通知的关系
@Before 在方法执行前执行通知类中的方法
4、spring配置文件

package com.example.demo.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@ComponentScan("com.example.demo")
@EnableAspectJAutoProxy		
public class SpringConfig {

}

@EnableAspectJAutoProxy 这个注解的意思是告诉程序我们要用注解开发程序

5、在App类中测试

package com.example.demo;

import com.example.demo.config.SpringConfig;
import com.example.demo.dao.BookDao;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

@SpringBootApplication
public class App {

    public static void main(String[] args) {
        ApplicationContext app = new AnnotationConfigApplicationContext(SpringConfig.class);
        BookDao bookDao = app.getBean(BookDao.class);
        bookDao.save();
        bookDao.update();
    }
}

右键运行输出的内容如下
在这里插入图片描述
可以看到两个方法前面都输出了当前的时间

AOP 切点表达式Pointcut Expression)

execution( void com.example.demo.dao.BookDao.update())

切点表达式包含的主要内容:

关键词:描述切入点的行为动作,execution表示指定切入点
访问修饰符:public,private等,可以省略
返回值
包名
类/接口名
方法名
参数

使用通配符描述切入点

execution( public * com.example.demo.dao.BookDao.*(*))

“ * “单个独立的任意符号,可以作为前缀或者后缀出现,表示匹配任意

execution( public void com.example..dao.BookDao..())

" … "连续的任意符号,可以独立出现,常用于简化包名

execution( public void com.example..dao+.*(..))

" + " 用于匹配子类类型
我们把案列中的切点表达式改成以下

@Pointcut("execution( * com.example..dao.BookDao.*())")

结果一样,不变

AOP工作流程及常见通知类型

在AOP配置中,定义切点和切面。切点是一个或多个方法或类的集合,上面的案列中就是一个方法,输出当前时间

当程序运行到某个切点时,AOP框架会拦截这个切点,并根据配置的切面,执行一些预先定义好的方法

在切面中,可以使用不同类型的通知(Advice),例如:

前置通知(Before Advice):在切点之前执行的通知 ,@Before。

后置通知(After Advice):在切点之后执行的通知,@After。

环绕通知(Around Advice):在切点前后都可以执行的通知,可以控制方法执行的流程,@Around。

异常通知(After Throwing Advice):在切点抛出异常时执行的通知,@AfterThrowing。

返回通知(After Returning Advice):在切点返回结果时执行的通知,@AfterReturning。

在执行完切面逻辑后,AOP框架会继续执行原来的程序流程。

其中@Before、@After、@AfterThrowing和@AfterReturning注解需要指定切点表达式(Pointcut Expression)在上面的案列中execution( void com.example.demo.dao.BookDao.update()) 这句代码就表示切点表达式,表示哪些方法需要被切面拦截;
@Around注解除了需要指定切点表达式,还需要在方法中手动调用ProceedingJoinPoint对象的proceed()方法,以继续执行原方法。
在上面的案例中我们可以在通知类中添加一个Around通知

package com.example.demo.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class MyAdvice {
    @Pointcut("execution( * com.example..dao.BookDao.*())")
    private void pt(){}
    @Pointcut("execution( * com.example..dao.BookDao.*())")
    private void pt1(){}

    @Before("pt()")
    public void method(){
        System.out.println(System.currentTimeMillis());
    }

    @Around("pt1()")
    public void method1(ProceedingJoinPoint pjp){
        System.out.println("环绕通知开始");
        try {
            pjp.proceed();
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
        System.out.println("环绕通知结束");
    }
}

代码运行结果如下
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

亦简_yz

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值