前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >Spring-AOP

Spring-AOP

作者头像
ma布
发布2024-10-21 18:33:44
发布2024-10-21 18:33:44
10900
代码可运行
举报
文章被收录于专栏:Java开发Java开发
运行总次数:0
代码可运行

一:什么是AOP

在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。 总的来说,AOP就是一编程规范,目的是在不改变方法源代码的基础上对方法进行功能增强。

二:AOP快速入门

2.1导入AOP坐标

在pom.xml中导入aop坐标

代码语言:javascript
代码运行次数:0
运行
复制
<dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>5.3.31</version>
        </dependency>
代码语言:javascript
代码运行次数:0
运行
复制
  <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.7</version>
        </dependency>

2.2定义dao接口和实现类

BookDao接口中:

代码语言:javascript
代码运行次数:0
运行
复制
package com.lcyy.dao;

public interface BookDao {
    void read();
}

实现类:注意要在BookDaoImpl方法上加入注解@Repository表示为交给Spring的一个组件

代码语言:javascript
代码运行次数:0
运行
复制
import com.lcyy.dao.BookDao;
import org.springframework.stereotype.Repository;

@Repository
public class BookDaoImpl implements BookDao {
    @Override
    public void read() {
        System.out.println("BookDao AOP 实现");
    }
}

2.3定义通知类

建立一个aop的包,在包下建立一个MyAdvice的通知类

代码语言:javascript
代码运行次数:0
运行
复制
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Component//放在普通类上,交给ioc容器管理
@Aspect//定义这个类为切面类
public class MyAdvice {
    //定义连接点
    @Pointcut("execution(void com.lcyy.dao.BookDao.read())")
    private void pc(){}

    //定义通知
    @Before("pc()")
    public void testAdvice(){
        System.out.println("执行前的时间毫秒值:");
        System.out.println(System.currentTimeMillis());
    }
}

注意: 1.在MyAdvice类上要加入注解:@Aspect表示定义为一个切面类,@Component表示交给ioc容器管理。 2.@Pointcut注解要求配置在方法上方,("execution(void com.lcyy.dao.BookDao.read())")为切入点表达式,execution为关键字, void为返回值 com.lcyy.dao.BookDao.read()为com.lcyy包下的dao包下的BookDao接口中的read()方法。 3.@Before("pc()") 其中@Before为前置通知,表示为在方法执行前执行。关于通知的几种类型,下面会详细讲解。其中 "pc()" 表示将连接点和通知捆绑起来,注意方法名必须相同。

2.4在配置类中进行Spring注解包扫描和开启AOP功能

代码语言:javascript
代码运行次数:0
运行
复制
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@ComponentScan("com.lcyy")
@EnableAspectJAutoProxy//开启aop
public class SpringConfig {
}

其中注解 @EnableAspectJAutoProxy 表示开启AOP通知,交给IOC容器管理

2.5定义测试类测试结果

代码语言:javascript
代码运行次数:0
运行
复制
import com.lcyy.config.SpringConfig;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class BookDaoTest {
    public static void main(String[] args) {
        //获取容器
        ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
        BookDao bookDao = ctx.getBean(BookDao.class);
        bookDao.read();
    }
}

测试结果:

三:AOP工作流程

  1. Spring容器启动
  2. 读取所有切面配置中的切入点
  3. 初始化bean,判定bean对应的类中的方法是否匹配到任意切入点
    • 匹配失败,创建原始对象
    • 匹配成功,创建原始对象(目标对象)的代理对象
  4. 获取bean执行方法
    • 获取的bean是原始对象时,调用方法并执行,完成操作
    • 获取的bean是代理对象时,根据代理对象的运行模式运行原始方法与增强的内容,完成操作

验证AOP的代理对象,在刚才测试类中获取

代码语言:javascript
代码运行次数:0
运行
复制
import com.lcyy.config.SpringConfig;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class BookDaoTest {
    public static void main(String[] args) {
        //获取容器
        ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
        BookDao bookDao = ctx.getBean(BookDao.class);
        bookDao.read();
        System.out.println(bookDao.getClass());
    }
}

测试结果:

其中:class com.sun.proxy.$Proxy19 就是为代理对象的类名

四:AOP的切入点表达式

4.1语法格式

描述方法一:执行com.lcyy.dao包下的BookDao接口中的无参数read方法

execution(void com.lcyy.dao.BookDao.read())

描述方法二:执行com.lcyy.dao包下的BookDaoImpl实现类中的无参数read方法

execution(void com.lcyy.dao.BookDaoImpl.read())

切入点表达式标准格式:动作关键字(访问修饰符 返回值 包名.类/接口名.方法名(参数) 异常名)

execution(public User com.itheima.service.UserService.findById(int)) 其中:

  • 动作关键字:描述切入点的行为动作,例如execution表示执行到指定切入点
  • 访问修饰符:public,private等,可以省略
  • 返回值:写返回值类型
  • 包名:多级包使用点连接
  • 类/接口名:
  • 方法名:
  • 参数:直接写参数的类型,多个类型用逗号隔开
  • 异常名:方法定义中抛出指定异常,可以省略

4.2通配符

(*):单个独立的任意符号,可以独立出现,也可以作为前缀或者后缀的匹配符出现

(..):多个连续的任意符号,可以独立出现,常用于简化包名与参数的书写

常用的通配符表达式为:

@Pointcut("execution(* com.lcyy.dao.*.*(..)") 表示任意的访问修饰符 在com.lcyy.dao包下的任意类中的所有方法(..)表示可以有形参也可以没有形参。

五:AOP通知类型

  • AOP通知共分为5种类型
    • 前置通知:在切入点方法执行之前执行
    • 后置通知:在切入点方法执行之后执行,无论切入点方法内部是否出现异常,后置通知都会执行。
    • 环绕通知(重点):手动调用切入点方法并对其进行增强的通知方式。
    • 最终通知:在切入点方法执行之后执行,如果切入点方法内部出现异常将不会执行。
    • 异常通知:在切入点方法执行之后执行,只有当切入点方法内部出现异常之后才执行。

5.1前置通知

  • 名称:@Before
  • 类型:==方法注解==
  • 位置:通知方法定义上方
  • 作用:设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法前运行
  • 案例代码:
代码语言:javascript
代码运行次数:0
运行
复制
 //前置通知
    @Before("execution(void com.lcyy.dao.BookDao.write())")
    public void testAdvice2(){
        System.out.println("我是前置通知");
    }

5.2后置通知

名称:@AfterReturning

类型:==方法注解==

位置:通知方法定义上方

作用:设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法后运行

案例代码:

代码语言:javascript
代码运行次数:0
运行
复制
@AfterReturning("execution(void com.lcyy.dao.BookDao.write())")
    public void testAdvice4(){
        System.out.println("我是后置通知");
    }

5.3最终通知

  • 名称:@After
  • 类型:==方法注解==
  • 位置:通知方法定义上方
  • 作用:设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法后运行
  • 案例代码:
代码语言:javascript
代码运行次数:0
运行
复制
@After("execution(void com.lcyy.dao.BookDao.write())")
    public void testAdvice3(){
        System.out.println("我是最终通知");
    }

5.4异常通知

  • 名称:@AfterThrowing
  • 类型:==方法注解==
  • 位置:通知方法定义上方
  • 作用:设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法运行抛出异常后执行
代码语言:javascript
代码运行次数:0
运行
复制
@AfterThrowing("execution(void com.lcyy.dao.BookDao.write())")
    public void testAdvice5(){
        System.out.println("我是异常通知");
    }

在以上四个通知后,我们在测试类中测试以上通知

注意:书写了异常通知,却没有执行,是因为简单的代码实现没有异常。

测试异常通知:

在BookDaoImpl 的实现类中书写测试异常的代码

代码语言:javascript
代码运行次数:0
运行
复制
@Override
    public void write() {
        System.out.println("BookDao AOP 实现");
        int a = 10/0;
        System.out.println("a = " + a);
    }

测试结果:

5.5环绕通知

  • 名称:@Around(重点,常用)
  • 类型:==方法注解==
  • 位置:通知方法定义上方
  • 作用:设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法前后运行
  • 案例代码:
代码语言:javascript
代码运行次数:0
运行
复制
  @Around("execution(void com.lcyy.dao.BookDao.jiyi())")
    public Object testJiyi(ProceedingJoinPoint PJ) throws Throwable {
        System.out.println("我时前置通知");
        Object Oj = PJ.proceed();
        System.out.println("我是后置通知");
        return Oj;
    }

测试结果:

注意:

  1. 环绕通知方法形参必须是ProceedingJoinPoint,表示正在执行的连接点,使用该对象的proceed()方法表示对原始对象方法进行调用,返回值为原始对象方法的返回值。
  2. 环绕通知方法的返回值建议写成Object类型,用于将原始对象方法的返回值进行返回,哪里使用代理对象就返回到哪里。

六:总结

AOP在spring中是一个很重要的概念,理解和运用AOP在以后的业务开发中有很重要的作用!

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-05-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一:什么是AOP
  • 二:AOP快速入门
    • 2.1导入AOP坐标
    • 2.2定义dao接口和实现类
    • 2.3定义通知类
    • 2.4在配置类中进行Spring注解包扫描和开启AOP功能
    • 2.5定义测试类测试结果
  • 三:AOP工作流程
  • 四:AOP的切入点表达式
    • 4.1语法格式
    • 4.2通配符
  • 五:AOP通知类型
    • 5.1前置通知
    • 5.2后置通知
    • 5.3最终通知
    • 5.4异常通知
    • 5.5环绕通知
  • 六:总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档