Spring05_AOP的引入(原理)、基于xml的AOP、基注解的AOP

本文深入探讨了Spring的AOP(面向切面编程),包括AOP的概念、作用、优势以及相关术语。介绍了如何在XML配置中设置AOP,详细讲解了切入点表达式、通知类型和代理选择。通过案例展示了基于XML和注解的AOP实现,强调了动态代理在AOP中的应用,以及如何通过注解简化配置。
摘要由CSDN通过智能技术生成

第七章: AOP

(1)AOP的引入和简介

1、回顾了解动态代理

 看以前的博客,基于接口实现动态代理,基于子类实现的动态代理

2、案例引入AOP(AOP的原理)

(使用动态代理的方式,把重复的代码在动态代理中增强到每一个切点(方法)上面)

在这里插入图片描述

3、AOP的简介

3.1 什么是AOP??
  • 全称是 Aspect Oriented Programming 即:面向切面编程。

  • 在这里插入图片描述

  • 简单来说:它就是把我们程序重复的代码抽取出来,在需要执行的时候,使用动态代理的技术,在不修改源码的基础上,对我们的已有方法进行增强。

3.2AOP的作用和优势
  • 作用:
    • 在程序运行期间,不修改源码对已有方法进行增强。(利用动态代理实现AOP)
  • 优势:
    • 减少重复代码
    • 提高开发效率
    • 维护方便

4、AOP的相关术语

  • Joinpoint( 连接点):
    • 所谓连接点是指那些被拦截到的点。在 spring 中,这些点指的是方法,因为 spring 只支持方法类型的
      连接点。
  • Pointcut( 切入点):
    • 所谓切入点是指我们要对哪些 Joinpoint 进行拦截的定义
  • Advice( 通知/ 增强):
    • 所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知。
    • 通知的类型:前置通知,后置通知,异常通知,最终通知,环绕通知。
  • Introduction( 引介):
    • 引介是一种特殊的通知在不修改类代码的前提下, Introduction 可以在运行期为类动态地添加一些方法或 Field。
  • Target( 目标对象):
    • 代理的目标对象。
  • Weaving( 织入):
    • 是指把增强应用到目标对象来创建新的代理对象的过程。
    • spring 采用动态代理织入,而 AspectJ 采用编译期织入和类装载期织入。
  • Proxy (代理):
    • 一个类被 AOP 织入增强后,就产生一个结果代理类。
  • Aspect( 切面):
    • 是切入点和通知(引介)的结合。

5、学习 spring 中的 AOP 要 明确的事

  • a 、开发阶段(我们做的)
    • 编写核心业务代码(开发主线):大部分程序员来做,要求熟悉业务需求。
    • 公用代码抽取出来,制作成通知。(开发阶段最后再做)。
    • 在配置文件中,声明切入点与通知间的关系,即切面。
  • b 、运行阶段(Spring 框架完成的)
    • Spring 框架监控切入点方法的执行。一旦监控到切入点方法被运行,使用代理机制,动态创建目标对
      象的代理对象,根据通知类别,在代理对象的对应位置,将通知对应的功能织入,完成完整的代码逻辑运行。

6、关于代理的选择

  • spring会根据目标是否实现类接口来决定采用哪种代理方式:
    1. 基于接口的AOP实现动态代理
    2. 基于子类的AOP实现动态代理




(2) 基于xml的AOP

1、使用步骤
  1. 导入相关的jar包(IOC的jar包、aspectjweaver的jar包)

  2. 创建spring的AOP配置文件,导入相关的约束(AOP基于xml的约束(在官网找))

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop.xsd">
    </beans>
    
  3. 配置 spring 的 ioc

  4. 配置切面的IOC + 抽取公共代码制作AOP

2、案例:

在这里插入图片描述

业务层的实体类(要增强的方法)
public class AccountService  implements IAccountService {
   public AccountService() {
   }

   public void saveAccount() {
//    int i= 1/0;
      System.out.println("保存方法执行了");
   }

   public void updateAccount(int i) {
      System.out.println("更新方法执行了"+i);
   }

   public int deleteAccount() {
      System.out.println("删除方法执行了");
      return 0;
   }
}
-----------------------------------------------------------------------------------------------------------------------------------------
切面类
public class Logger  {
	public Logger() {
	}

	//前置对象
	public void PrintLog(){
		System.out.println("前置方法执行了");
	}
	//后置对象
	public  void afterReturnPrintLog(int i){
		System.out.println("后置方法执行了"+i);
	}
	//异常
	public  int  afterThrowingPrintLog(){
		System.out.println("异常方法执行了");
		return 0;
	}
	//最终
	public  void  afterPrintLog(){
		System.out.println("最终通知执行了");
	}
	//环绕
	public Object aroundPrintLog(ProceedingJoinPoint pjp){
		//定义切点对象
		Object rtValue = null;
		try {
			//的到执行方法需要的参数
			Object[] ars = pjp.getArgs();
			System.out.println("前置增强");
			//执行切入点方法,传入相应的形参
			rtValue = pjp.proceed(ars);
			System.out.println("后置增强");
		} catch (Throwable throwable) {
			System.out.println("异常增强");
			throwable.printStackTrace();
		} finally {
			System.out.println("最终增强");
			System.out.println();
		}
		return rtValue;
	}
}
-----------------------------------------------------------------------------------------------------------------------------------------
 

配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!-- 配置spring中的IOC把service对象配置进来-->
    <bean id="accountService" class="com.zy.service.impl.AccountService"/>
    <!--配置AOP-->
    <!--先创建切面的Bean对象-->
    <bean id="logger" class="com.zy.utils.Logger"/>
    <!--AOP开始的标签,该标签里面可以配置多个切面-->
    <aop:config>
        <!--配置通用的切入点表达式标签 -->
        <aop:pointcut id="qrd" expression="execution(* com.zy.service.impl.*.*(..))"/>
        <!--配置切面-->
        <aop:aspect id="logAdvice" ref="logger">
            <!--配置切点-->
            <!--对saveAccount方法增强-->
            <!-- <aop:before method="PrintLog" pointcut="execution(public  void com.zy.service.impl.AccountService.saveAccount())"/>-->
            <aop:before method="PrintLog" pointcut="execution(* *..*.*(..))"/>
            <aop:after-returning method="afterReturnPrintLog" pointcut-ref="qrd"/>
            <aop:after-throwing method="afterThrowingPrintLog" pointcut-ref="qrd"/>
            <aop:after method="afterPrintLog" pointcut-ref="qrd"/>
            <!--环绕方法测试-->
            <!--<aop:around method="aroundPrintLog" pointcut-ref="qrd"/>-->
        </aop:aspect>
    </aop:config>
</beans>
3、AOP配置常见的标签及其属性
(1) < aop:config > AOP配置开始标签
  • 表示配置AOP的开始
(2) < aop:aspect > 切面标签
  • 写在 < aop:config >标签的内部,表示配置切面(把该类的方法增强到对应的切点
  • 属性
    • id属性: 切面的唯一标识
    • ref属性: 指明通知类Bean的ID(切面类的Bean的id)
  • 作用:在该标签内部指明需要在***什么类的方法(切点)使用该类(切面类)的方法进行增强
(3) 五种常用的通知类型(都是写在切面标签内部的)
  • < aop:before> 前置通知标签(切点)

    • 在***< aop:aspect> 切面标签***内部配置
    • 在执行方法前,先执行,切面类中对应的属性对应的方法
    • 属性:
      • method属性:指明使用***切面类中的什么方法***进行对切点的增强
      • printcut属性: 指定哪些要增强的方法(指定切点)(使用***切入点表达式进行指明切点***)
      • printcut-ref属性:指定使用的那个切入点表达式标签
  • < aop: after-returning> 后置通知

  • < aop:after-throwing > 异常通知

  • < aop:after > 最终通知

  • 注意:

    • 执行的顺序: 前置标签 —》 切点方法 ----》 后置标签 ----》最终通知
    • 当发生异常的时候,不会出现后置标签,会显示异常标签(也就说** **)
    • 以上四个标签是控制什么方法,放到切入点的什么位置执行
  • < aop:around>循环通知标签

    • 可以实现上面三个标签的功能

    • 通过代码控制的方式实现增强的功能

    • 属性: 和上面四个标签的属性一样

    • 切面指定的方法怎么写??

      • 会完全使用该方法,要想在里面使用切点指定的方法需要定义一个形参

      • 在环绕方法中定义一个形参: ProceedingJoinPoint接口,使用该接口就可以明确切入点方法

      • 在调用该环绕方法的时候,spring会自动给该方法传入参数(传入的参数就是我们xml中指定切点)

      • public Object aroundPrintLog(ProceedingJoinPoint pjp){
           //定义切点对象
           Object rtValue = null;
           try {
              //的到执行方法需要的参数
              Object[] ars = pjp.getArgs();
              System.out.println("前置增强");
              //执行切入点方法,传入相应的形参
              rtValue = pjp.proceed(ars);
              System.out.println("后置增强");
           } catch (Throwable throwable) {
              System.out.println("异常增强");
              throwable.printStackTrace();
           } finally {
              System.out.println("最终增强");
           }
           return rtValue;
        }
        
(4) (重点)切入点表达式的写法 及 < aop:pointcut >切入点表达式标签
1、切入点表达式
  • 关键字: execution(表达式)

  • 表达式的写法: 访问修饰符 返回值 包名.包名......包名.类名.方法名(参数列表)

    • 全通配写法: * * . . * . * ( . . ) 该项目下的所有方法都会被增强
    • 访问修饰符可以省略
    • 返回值 * 表示可以为任何返回值
    • 包名: * 表示可以为任何包名 但是有几级包就需要写几级的* . . 表示当前包及其子包
    • 类名: * 表示可以为任何类名
    • 方法名: * 表示可以为任何方法名
    • 参数列表:
      1. 基本类型直接写,
      2. 引用类型包名.类名(java.lang.String) 的方式
      3. 不写只匹配无参的方法
      4. 只写一个 * 匹配含有参数的方法,参数可以是任意类型
      5. . . 表示有参无参都匹配,参数可以是任意类型
  • 实际开发中切入点表达式的通常写法:

    • 切到业务层实现类下的方法全部增强: * com.zy.Service.impl.*(..)
  • 在创建项目的时候,导入了一个aspectjweaver的jar包,他的作用就是解析切入点表达式

2、< aop:pointcut > 通用化切入点表达式标签
  • 属性
    • id属性: 唯一标识
    • expression属性:指定切入点表达式
  • 通知标签的printcut-ref属性中引用该标签的ID,就是使用对应的切入点表达式
  • 注意:
    • 切面标签里面配置,只能当前切面使用
    • 如果在切面标签里面配置,该标签一定在通知类的标签之前定义
    • 可以在切面标签外面配置,但是要在切面标签之前配置,所有的切面都可以使用(常用)



(3)基于注解的AOP

1、案例

(1)配置Maven导入相应的坐标

(2)导入的配置文件的坐标和xml的不一样(在官网上去找)

​ 配置好IOC容器 (要扫描的包) + 开启AOP的注解

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <!--配置spring创建容器时要扫描的包(创建IOC要扫描的包)-->
    <context:component-scan base-package="com.zy"/>
    <!--开启spring注解AOP的支持-->
    <aop:aspectj-autoproxy/>

</beans>

(3)在Service和Logger相关的类似加入注解

//service相关的类
//创建Bean对象
@Service("accountService")
public class AccountService  implements IAccountService {
   public AccountService() {
   }

   public void saveAccount() {
//    int i= 1/0;
      System.out.println("保存方法执行了");
   }

   public void updateAccount(int i) {
      System.out.println("更新方法执行了"+i);
   }

   public int deleteAccount() {
      System.out.println("删除方法执行了");
      return 0;
   }
}
---------------------------------------------------------------------------------------------------------------------------------------
//切面类
 @Component("logger")
@Aspect//表示当前类是一个切面类
public class Logger  {
	public Logger() {
	}
	//相当于配置切入点表达式标签
	@Pointcut("execution(* com.zy.service.impl.*.*(..))")
	private  void pt1(){}
	//前置对象
	@Before("pt1()")
	public void PrintLog(){
		System.out.println("前置方法执行了");
	}
	//后置对象
    @AfterReturning("execution(* com.zy.service.impl.*.*(..))")
	public  void afterReturnPrintLog(int i){
		System.out.println("后置方法执行了"+i);
	}
	//异常
	@AfterThrowing("pt1()")
	public  int  afterThrowingPrintLog(){
		System.out.println("异常方法执行了");
		return 0;
	}
	//最终
	@After("pt1()")
	public  void  afterPrintLog(){
		System.out.println("最终通知执行了");
	}
	//环绕
    //@Around("pt1()")
	public Object aroundPrintLog(ProceedingJoinPoint pjp){
		//定义切点对象
		Object rtValue = null;
		try {
			//的到执行方法需要的参数
			Object[] ars = pjp.getArgs();
			System.out.println("前置增强");
			//执行切入点方法,传入相应的形参
			rtValue = pjp.proceed(ars);
			System.out.println("后置增强");
		} catch (Throwable throwable) {
			System.out.println("异常增强");
			throwable.printStackTrace();
		} finally {
			System.out.println("最终增强");
			System.out.println();
		}
		return rtValue;
	}
}
2、AOP相关的注解以及纯注解的AOP:
  • @Aspect 声明切面实体类
    • 放到切面的实体类上面
  • 五个常用通知标签:
    • @Before 前置
    • @AfterReturning 后置
    • @AfterThrowing 异常
    • @After 最终
    • @Around 环绕
    • 这五个标签都是放到 对应的方法上面
    • @Before("execution(切入点)") @Befoer("pt1()")
  • @Pointcut 切入点表达式
    • @Pointcut(“execution(切入点)”)
    • 放到相应的方法上面(该方法就相当于该切入点的id)
    • 引用切入点表达式的时候一定加括号
  • @EnableAspectJAutoProxy 纯注解配置时候,指明配置类开启支持AOP注解配置
    • 和上面IOC讲纯注解的注解标签一块使用
    • 放到配置类上
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小镇男孩~~

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

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

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

打赏作者

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

抵扣说明:

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

余额充值