AOP(Spring知识)

SpringAOP

AOP(Aspect Oriented Programming)

概述

面向切面编程,一种编程范式,指导开发者如何组织程序结构

作用

在不惊动原始设计的基础上为其进行功能增强

核心概念

连接点(JoinPoint):程序执行过程中的任意位置,力度为执行方法、抛出异常、设置 变量等
在SpringAOP中,理解为方法的执行
切入点(PointCut):匹配连接点的式子
在SpringAOP中,一个切入点可以描述一个具体方法,也可匹配多个方法
一个具体方法:如com.itheima.dao包下的BookDao接口中的无形参无返回值的save方法
匹配多个方法:所有的save方法,所有的get开头的方法,所有以Dao结尾的接口中的任意方法,所有带有一个参数的方法
通知(Advice):在切入点处执行的操作,也就是共性功能
在SpringAOP中,功能最终以方法的形式呈现
通知类:定义通知的类
切面(Aspect):描述通知与切入点的对应关系

实现步骤

1.导入依赖
pom.xml

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.4</version>
</dependency>

2.定义接口与实现类
3.定义通知类和通知
4.定义切入点
使用@PointCut注解,基本上是固定格式
5.制作切面
使用@Before注解
6.将通知类配给容器并标识其为切面类
使用@Component,@Aspect注解

//类名和方法名可以任意
@Component
@Aspect
public class MyAdvice {

    @Pointcut("execution(void com.itheima.dao.BookDao.update())")
    private void pt(){}
    
    @Before("pt()")
    public void method(){
        System.out.println(System.currentTimeMillis());
    }
}

7.开启注解格式AOP功能

@Configuration
@ComponentScan("com.itheima")
@EnableAspectJAutoProxy
public class SpringConfig {
}

8.运行程序

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

AOP工作流程

1.Spring容器启动
2.读取所有切面配置中的切入点
3.初始化bean
4.获取bean执行方法

验证容器中是否为代理对象?
如果目标对象中的方法会被增强,那么容器中将存入的是目标对象的代理对象
如果目标对象中的方法不被增强,那么容器中将存入的是目标对象本身
思路:

1.要执行的方法,不被定义的切入点包含,即不要增强,打印当前类的getClass()方法
2.要执行的方法,被定义的切入点包含,即要增强,打印出当前类的getClass()方法
3.观察两次打印的结果

AOP核心概念

  • 目标对象(Target):原始功能去掉共性功能对应的类产生的对象,这种对象是无法直接完成最终工作的
  • 代理(Proxy):目标对象无法直接完成工作,需要对其进行功能回填,通过原始对象的代理对象实现

SpingAOP底层实现是通过代理模式

AOP配置管理

AOP切入点表达式

格式
动作关键字(访问修饰符 返回值 包名.类/接口名.方法名(参数) 异常名)

//方式一
// 执行com.itheima.dao包下的BookDao接口中的无参数update方法
execution(void com.itheima.dao.BookDao.update())

//方法二
//执行com.itheima.dao.impl包下的BookDaoImpl类中的无参数update方法
execution(void com.itheima.dao.impl.BookDaoImpl.update())

通配符

// *:单个独立的任意符号,可以独立出现,也可以作为前缀或者后缀的匹配符出现
//匹配com.itheima包下的任意包中的UserService类或接口中所有find开头的带有一个参数的方法
execution(public * com.itheima.*.UserService.find*(*))

// ..:多个连续的任意符号,可以独立出现,常用于简化包名与参数的书写
//匹配com包下的任意包中的UserService类或接口中所有名称为findById的方法
execution(public User com..UserService.findById(..))

// +:专用于匹配子类类型 不常用
// *Service+,表示所有以Service结尾的接口的子类
execution(* *..*Service+.*(..))

书写技巧

  • 所有代码按照标准规范开发,否则以下技巧全部失效
  • 描述切入点通常描述接口,而不描述实现类,如果描述到实现类,就出现紧耦合了
  • 访问控制修饰符针对接口开发均采用public描述(可省略访问控制修饰符描述
  • 返回值类型对于增删改类使用精准类型加速匹配,对于查询类使用*通配快速描述
  • 包名书写尽量不使用…匹配,效率过低,常用*做单个包描述匹配,或精准匹配
  • 接口名/类名书写名称与模块相关的采用*匹配,例如UserService书写成*Service,绑定业务层接口名
  • 方法名书写以动词进行精准匹配,名词采用匹配,例如getById书写成getBy,selectAll书写成selectAll
  • 参数规则较为复杂,根据业务方法灵活调整
  • 通常不使用异常作为匹配规则

AOP通知类型

//表示通知添加到切入点方法执行的前面
@Before("pt()")

类型总共有5种
前置通知 @Before
后置通知 @After
环绕通知(重点) @Around
返回后通知 @AfterReturning
抛出异常后通知 @AfterReturning

@Component
@Aspect
public class MyAdvice {
    @Pointcut("execution(void com.itheima.dao.BookDao.update())")
    private void pt(){}
    
    @Pointcut("execution(int com.itheima.dao.BookDao.select())")
    private void pt2(){}
    
    @Around("pt2()")
    public Object aroundSelect(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("around before advice ...");
        //表示对原始操作的调用
        Object ret = pjp.proceed();
        System.out.println("around after advice ...");
        return ret;
    }
}

案例:业务层接口执行效率

AOP获取数据

  • JoinPoint:适用于前置、后置、返回后、抛出异常后通知
  • ProceedingJoinPoint:适用于环绕通知

获取参数

1.非环绕方式通知获取方式
在方法上添加JoinPoint形参,通过它的getArgs()方法
返回的是一个数组

@Component
@Aspect
public class MyAdvice {

    @Pointcut("execution(* com.itheima.dao.BookDao.selectName(..))")
    private void pt() {
    }

    @Before("pt()")
    public void before(JoinPoint jp) {
        Object[] args = jp.getArgs();
        System.out.println(Arrays.toString(args));
        System.out.println("before advice...");
    }
  }

2.环绕方式通知获取方式
在方法上添加ProceedingJoinPoint形参,通过它的getArgs()方法

@Component
@Aspect
public class MyAdvice {

    @Pointcut("execution(* com.itheima.dao.BookDao.selectName(..))")
    private void pt() {
    }

    @Around("pt()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        Object[] args = pjp.getArgs();
        System.out.println(Arrays.toString(args));
        //  Object ret = pjp.proceed(); 这里可以调用无参的
        // 如需要修改参数,用下面的有参方法pjp.proceed(Object[] objects)
        args[0] = 666;
        Object ret = pjp.proceed(args);
        return ret;

    }

获取返回值

1.环绕通知获取返回值
如上面的代码中, ret就是方法的返回值
2.返回后通知获取返回值

@Component
@Aspect
public class MyAdvice {
    @Pointcut("execution(* com.itheima.dao.BookDao.selectName(..))")
    private void pt() {
    }
    
    @AfterReturning(value = "pt()", returning = "ret")
    public void afterReturning(Object ret) {
        System.out.println("afterReturning advice..." + ret);
    }

形参ret必须和returning="ret"保持一致
说明:如有JoinPoint参数,必须要放第一位

获取异常(了解)

1.环绕通知获取异常
2.抛出异常获取异常

案例:百度网盘密码数据兼容处理

AOP事务

事务作用:在数据层保障一系列的数据库操作同成功同失败

事务管理

步骤:
1.在需要被事务管理的方法上添加@Transactional注解
2.在jdbcConfig类中配置事务管理器

    //配置事务管理器,mybatis使用的是jdbc事务
    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource){
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
        transactionManager.setDataSource(dataSource);
        return transactionManager;
    }
}

3.开启事务注解 @EnableTransactionManagement
4.运行测试类

事务角色

  • 事务管理员:发起事务方,在Spring中通常指代业务层开启事务的方法
  • 事务协调员:加入事务方,在Spring中通常指代数据层方法,也可以是业务层方法

事务属性

事务配置、事务传播行为

事务配置

以下这些属性可以在@Transactional注解的参数上进行设置

属性作用
readOnly设置是否为只读事务
timeout设置事务超时时间
rollbackFor设置事务回滚异常
rollbackForClassName设置事务回滚异常
noRollbackFor设置事务不回滚异常
noRollbackForClassName设置事务不回滚异常
isolation设置事务隔离级别
propagation设置事务传播行为

说明:

  • readOnly:true只读事务,false读写事务,增删改要设为false,查询设为true。
  • timeout:设置超时时间单位秒,在多长时间之内事务没有提交成功就自动回滚,-1表示不设置超时时间。
  • rollbackFor:当出现指定异常进行事务回滚
  • isolation设置事务的隔离级别
    DEFAULT :默认隔离级别, 会采用数据库的隔离级别
    READ_UNCOMMITTED : 读未提交
    READ_COMMITTED : 读已提交
    REPEATABLE_READ : 重复读取
    SERIALIZABLE: 串行化
readOnly=true
timeout=-1
TrollbackFor={NullPointException.class}
isolation=Isolation.DEFAULT

案例:转账业务追加日志

事务传播行为

事务协调员对事物管理员所携带事务的态度
需要用到propagation属性

	//propagation设置事务属性:传播行为设置为当前操作需要新事务
    @Transactional(propagation = Propagation.REQUIRES_NEW)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring AOPAspect Oriented Programming)是 Spring 框架中的一个重要组成部分,用于实现面向切面编程。以下是 Spring AOP 知识体系的主要内容: 1. AOP 的概念:介绍 AOP 的基本概念、作用和优势。 2. AOP实现方式:主要有基于代理的 AOP 和基于字节码操作的 AOP 两种实现方式。 3. 切面(Aspect)的概念:切面是 AOP 中的一个重要概念,用于描述一组横切关注点(cross-cutting concern)。 4. 切入点(Pointcut)的概念:切入点是 AOP 中一个非常重要的概念,用于描述在哪些连接点(Join point)上执行切面。 5. 通知(Advice)的概念:通知是切面中的具体行为,用于描述在连接点上执行的具体操作。 6. 引入(Introduction)的概念:引入是 AOP 中一个非常重要的概念,用于在不修改现有类代码的情况下为一个对象增加新的属性或方法。 7. 织入(Weaving)的概念:织入是 AOP 中一个非常重要的概念,用于将切面应用到目标对象上,生成代理对象。 8. AOP 的实践应用:介绍 AOP 在实际开发中的应用场景,如日志记录、性能监控、事务管理等。 9. Spring AOP 的具体实现:介绍 Spring AOP 的具体实现方式,如基于代理的 AOP 和基于注解的 AOP 等。 10. AOP 的局限性和注意事项:介绍 AOP 在实践中可能存在的问题和需要注意的事项,如对 final 方法的限制、对静态方法的限制等。 以上是 Spring AOP 知识体系的主要内容,熟悉这些内容可以帮助开发人员更好地理解和应用 AOP 技术。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值