JavaWeb进阶修炼手册29---核心框架:spring(三)---AOP

1. 今日内容

2. spring之AOP

2.1 AOP概述

1. AOP:全称是 Aspect Oriented Programming 即:面向切面编程。 通过预编译方式和运行期**动态代理**实现程序功能的统一维护的一种技术。
	*  简单来说:它就是把我们程序重复的代码抽取出来,在需要执行的时候,使用动态代理的技术,在不修改源码的基础上,对我们的已有方法进行增强。
2. AOP的核心(实现方式):动态代理。
3. AOP作用及优势:
	1. 作用:在程序运行的期间,不修改源码对已有的方法进行增强。
	2. 优点:
		* 减少重复代码
		* 提高开发效率
		* 维护方便

我的理解:AOP就是spring对动态代理的封装,使得我们在使用动态代理时更加的方便,我们只需要在配置文件中配置好,spring会在程序运行的过程中帮我们实现动态代理。

2.2 AOP相关术语

AOP相关术语在实际开发过程中对我们的帮助或许不大,但是在我们自学的过程中,翻阅资料时有很大的帮助。

  1. Joinpoint(连接点): 在 spring 中,这些点指的是方法,因为 spring 只支持方法类型的连接点。 连接点指的是被代理对象的所有方法
  2. Pointcut(切入点): 被代理对象中被增强的方法。我们可以选择被代理对象的部分方法增强,而被增强的方法叫切入点。
  3. Target(目标对象): 被代理对象。
  4. Proxy(代理): 代理对象。
  5. Advice(通知/增强): 所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知。 就是具体增强的代码。
    1. 前置通知:切入点中,增强方法时,调用被代理对象之前的增强方法。
    2. 后置通知:切入点中,增强方法时,调用被代理对象之后的增强方法。
    3. 异常通知:切入点中,增强方法时,调用被代理对象出现异常后执行的代码。
    4. 最终通知:切入点中,增强方法时,finally中执行的代码。
    5. 环绕通知:切入点中,整个增强方法的代码。
      在这里插入图片描述
  6. Weaving(织入): 是指把增强应用到目标对象来创建新的代理对象的过程
    1. spring 采用动态代理织入,而 AspectJ 采用编译期织入和类装载期织入。
  7. Aspect(切面):是切入点和通知(引介)的结合。

2.3 AOP动态代理的选择

在 spring 中,框架会根据目标类是否实现了接口来决定采用接口动态代理或者子类动态代理的方式。

2.5 基于xml配置AOP

2.5.1 导入jar包/坐标
<!--引入切入点表达式的jar包-->
  <dependency>
       <groupId>org.aspectj</groupId>
       <artifactId>aspectjweaver</artifactId>
       <version>1.9.5</version>
   </dependency>
2.5.2 创建配置文件bean.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>
2.5.3 在bean.xml中引用AOP
1. 把通知类用 bean 标签配置起来。即创建通知类对象。
	* 通知类中放的是增强的代码。通知类用来做前置通知、后置通知、异常通知、最终通知、环绕通知

2. 使用 aop:config标签声明 aop 配置
	* 位置:beans标签内
	* 作用:用于声明开始 aop 的配置

3. 使用 aop:aspect 配置切面(一个动态代理就是一个切面)
	* 位置:aop:config标签内
	* 作用:用于配置切面
	* 属性:
		1. id:给切面提供一个唯一标识,自己随便取,一般用XXAdvice
		2. ref:引用配置好的通知类 bean 的 id。

4. 使用 aop:pointcut 配置切入点表达式 
	* 位置:aop:aspect标签内或者aop:config标签内
	* 作用:用于配置切入点表达式。就是指定对哪些类的哪些方法进行增强。 
	* 属性:
		1. id:用于给切入点表达式提供一个唯一标识,自己随便取
		2. expression:用于定义切入点表达式。 (后面介绍切入点表达式)
	* 注意:如果aop:pointcut标签在aop:config标签内,则必须定义在用到他的切面前面,否则会报错

5. 使用 aop:xxx 配置对应的通知类型 
	1. aop:before:配置前置通知
		* 属性:
			1. method:用于指定通知类中的增强方法名称 
			2. ponitcut-ref(用的多):用于指定切入点的表达式的引用 
			3. poinitcut(用的少):用于指定切入点表达式 
	2. aop:after-returning:配置后置通知
		* 属性:
			1. method:指定通知中方法的名称。 
			2. pointct:定义切入点表达式 
			3. pointcut-ref:指定切入点表达式的引用 
	3. after-throwing:用于配置异常通知 
		* 属性:
			1. method:指定通知中方法的名称。   
			2. pointct:定义切入点表达式   
			3. pointcut-ref:指定切入点表达式的引用 
	4. aop:after:用于配置最终通知 
		* 属性:
		1. method:指定通知中方法的名称。   
		2. pointct:定义切入点表达式   
		3. pointcut-ref:指定切入点表达式的引用 

AOP使用案例代码:

<!-- 创建通知 --> 
<bean id="txManager" class="com.itheima.utils.TransactionManager">
	<property name="dbAssit" ref="dbAssit"></property> 
</bean> 

<!-- 配置切面们 --> 
<aop:config>
	<aop:aspect id="txAdvice" ref="txManager"> 
		<aop:before method="beginTransaction" pointcut="execution(public void com.itheima.service.impl.AccountServiceImpl.transfer(    java.lang.String, java.lang.String, java.lang.Float) )"/> 
		<aop:after-returning method="commit" pointcut="execution(public void com.itheima.service.impl.AccountServiceImpl.transfer(    java.lang.String, java.lang.String, java.lang.Float) )"/> 
		<aop:after-throwing method="rollback" pointcut="execution(public void com.itheima.service.impl.AccountServiceImpl.transfer(    java.lang.String, java.lang.String, java.lang.Float) )"/> 
		<aop:after method="release" pointcut="execution(public void com.itheima.service.impl.AccountServiceImpl.transfer(    java.lang.String, java.lang.String, java.lang.Float) )"/> 
	</aop:aspect> 
</aop:config> 

上述代码可以等价转换,将切入点抽取出来。一般,我们使用下面这个代码:

<!-- 创建通知 --> 
<bean id="txManager" class="com.itheima.utils.TransactionManager">
	<property name="dbAssit" ref="dbAssit"></property> 
</bean> 

<!-- 配置切面们 --> 
<aop:config>
	<aop:aspect id="txAdvice" ref="txManager"> 
		<aop:pointcut expression="execution(public void com.itheima.service.impl.AccountServiceImpl.transfer(    java.lang.String, java.lang.String, java.lang.Float) )" id="pt1"/> 
		<aop:before method="beginTransaction" pointcut-ref="pt1"/> 
		<aop:after-returning method="commit" pointcut-ref="pt1"/> 
		<aop:after-throwing method="rollback" pointcut-ref="pt1"/> 
		<aop:after method="release" pointcut-ref="pt1"/> 
	</aop:aspect> 
</aop:config> 

补充知识点:

1. 切入点表达式:execution(表达式) 
	* 表达式语法:execution([修饰符] 返回值类型 包名.类名.方法名(参数)) 
		* 注意:修饰符可以写也可以不写
		* 表达式写法:
			1. 全匹配方式: 
				void com.itheima.service.impl.AccountServiceImpl.saveAccount(com.itheima.domain.Account)
			2. 返回值可以使用*号,表示任意返回值 
				* com.itheima.service.impl.AccountServiceImpl.saveAccount(com.itheima.domain.Account)
			3. 包名可以使用*号,表示任意包,但是有几级包,需要写几个* 
				* * *.*.*.*.AccountServiceImpl.saveAccount(com.itheima.domain.Account) 
			4. 使用..来表示当前包,及其子包 
				* com..AccountServiceImpl.saveAccount(com.itheima.domain.Account) 
			5. 类名可以使用*号,表示任意类 
				* com..*.saveAccount(com.itheima.domain.Account) 
			6. 方法名可以使用*号,表示任意方法 
				* com..*.*( com.itheima.domain.Account) 
			7. 参数列表可以使用*,表示参数可以是任意数据类型,但是必须有参数 
				* com..*.*(*) 
			8. 参数列表可以使用..表示有无参数均可,有参数可以是任意类型 
				* com..*.*(..) 
			9. 全通配方式:
				 * *..*.*(..) 

2. 环绕通知
	* 环绕通知的方法有一个参数:ProceedingJoinPoint pjp,该参数代表 切入点
		* 说明:ProceedingJoinPoint是spring提供的一个接口,该接口作为环绕通知的方法参数,在程序执行时,spring框架会为我们提供该接口的实现类供我们使用。
		* 执行切入点:pjp.proceed(pjp.getArgs());

	* 例子:

	   <aop:config>  
			<!-- 切入点,也可以配置在<aop:aspect>标签内 -->   
			<aop:pointcut expression="execution(* com.itheima.service.impl.*.*(..))" id="pt1"/>   
			<aop:aspect id="txAdvice" ref="txManager"> 
				<!-- 配置环绕通知 -->   
				<aop:around method="transactionAround" pointcut-ref="pt1"/> 
		 	</aop:aspect>
	   </aop:config>


		/** 
		* 环绕通知  
		* @param pjp 
		*  spring 框架为我们提供了一个接口:ProceedingJoinPoint,它可以作为环绕通知的方法参数。  
		*  在环绕通知执行时,spring 框架会为我们提供该接口的实现类对象,我们直接使用就行。  
		* @return  
		*/ 
		public Object transactionAround(ProceedingJoinPoint pjp) {
	        //定义返回值
	        Object rtValue = null;
	        try {
	            //获取方法执行所需的参数
	            Object[] args = pjp.getArgs();
	            //前置通知:开启事务(自己编写)
	            beginTransaction();
	            //执行方法
	            rtValue = pjp.proceed(args);
	            //后置通知:提交事务(自己编写)
	            commit();
	        } catch (Throwable e) {
	            //异常通知:回滚事务(自己编写)
	            rollback();
	            e.printStackTrace();
	        }finally {
	            //最终通知:释放资源(自己编写)
	            release();
	        }
	        return rtValue;
	    }
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

ElegantCodingWH

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

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

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

打赏作者

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

抵扣说明:

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

余额充值