返回

SSM框架03-AOP和声明式事务控制

Spring的AOP简介

什么是AOP

AOP(Aspect Oriented Programming)意思为面向切面编程,是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。

动态代理:在不修改源码的情况下对目标方法进行增强,可以实现程序功能之间的松耦合。

AOP 是 OOP 的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

AOP的作用及其优势

  • 作用:在程序运行期间,在不修改源码的情况下对方法进行功能增强
  • 优势:减少重复代码,提高开发效率,并且便于维护

AOP的底层实现

实际上,AOP 的底层是通过 Spring 提供的的动态代理技术实现的。在运行期间,Spring通过动态代理技术动态的生成代理对象,代理对象方法执行时进行增强功能的介入,在去调用目标对象的方法,从而完成功能的增强

AOP的动态代理技术

  • JDK代理 : 基于接口的动态代理技术
  • cglib代理:基于父类的动态代理技术

AOP相关术语

Spring 的 AOP 底层实现原理就是对上面的动态代理的代码进行了封装,封装后我们只需要对需要关注的部分进行代码编写,并通过配置的方式完成目标方法的增强。

  • Target(目标对象):代理的目标对象
  • Proxy(代理):一个类被 AOP 织入增强后,就产生一个结果代理类
  • Joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方 法类型的连接点
  • Pointcut(切入点):所谓切入点是指我们要对哪些 Joinpoint 进行拦截的定义
  • Advice(通知/ 增强):所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知
  • Aspect(切面):是切入点和通知(引介)的结合
  • Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程。spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入

AOP 开发明确的事项

需要编写的内容

  • 编写核心业务代码(目标类的目标方法)
  • 编写切面类,切面类中有通知(增强功能方法)
  • 在配置文件中,配置织入关系,即将哪些通知与哪些连接点进行结合

AOP 技术实现的内容

Spring 框架监控切入点方法的执行。一旦监控到切入点方法被运行,使用代理机制,动态创建目标对象的代理对象,根据通知类别,在代理对象的对应位置,将通知对应的功能织入,完成完整的代码逻辑运行。

AOP 底层的代理方式

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

基于XML的AOP开发

  • 导入 AOP 相关坐标
  • 创建目标接口和目标类(内部有切点)
  • 创建切面类(内部有增强方法)
  • 将目标类和切面类的对象创建权交给 spring
  • 在 applicationContext.xml 中配置织入关系
  • 测试代码

通知的类型

通知的配置语法:

1
<aop:通知类型 method=切面类中方法名 pointcut=切点表达式"></aop:通知类型>
名称 标签 说明
前置通知 <aop:before> 用于配置前置通知。指定增强的方法在切入点方法之前执行
后置通知 <aop:after-returning> 用于配置后置通知。指定增强的方法在切入点方法之后执行
环绕通知 <aop:around> 用于配置环绕通知。指定增强的方法在切入点方法之前和之后都执行
异常抛出通知 <aop:throwing> 用于配置异常抛出通知。指定增强的方法在出现异常时执行
最终通知 <aop:after> 用于配置最终通知。无论增强方式执行是否有异常都会执行

切点表达式的写法

表达式语法:

1
execution([修饰符] 返回值类型 包名.类名.方法名(参数))
  • 访问修饰符可以省略
  • 返回值类型、包名、类名、方法名可以使用星号* 代表任意
  • 包名与类名之间一个点 . 代表当前包下的类,两个点 .. 表示当前包及其子包下的类
  • 参数列表可以使用两个点 .. 表示任意个数,任意类型的参数列表

当多个增强的切点表达式相同时,可以将切点表达式进行抽取,在增强中使用 pointcut-ref 属性代替 pointcut 属性来引用抽取后的切点表达式。

基于注解的AOP开发

  • 创建目标接口和目标类(内部有切点)
  • 创建切面类(内部有增强方法)
  • 将目标类和切面类的对象创建权交给 spring
  • 在切面类中使用注解配置织入关系
    • 使用@Aspect标注切面类
    • 使用@通知注解标注通知方法
  • 在配置文件中开启组件扫描和 AOP 的自动代理
    • 在配置文件中配置aop自动代理<aop:aspectj-autoproxy/>
  • 测试代码

注解通知的类型

通知的配置语法:

1
@通知注解("切点表达式")
名称 标签 说明
前置通知 @Before 用于配置前置通知。指定增强的方法在切入点方法之前执行
后置通知 @AfterReturning 用于配置后置通知。指定增强的方法在切入点方法之后执行
环绕通知 @Around 用于配置环绕通知。指定增强的方法在切入点方法之前和之后都执行
异常抛出通知 @AfterThrowing 用于配置异常抛出通知。指定增强的方法在出现异常时执行
最终通知 @After 用于配置最终通知。无论增强方式执行是否有异常都会执行

切点表达式的写法

同 xml 配置 aop 一样,我们可以将切点表达式抽取。抽取方式是在切面内定义方法,在该方法上使用@Pointcut 注解定义切点表达式,然后在在增强注解中进行引用。

基于XML的声明式事务控制

什么是声明式事务控制

Spring 的声明式事务顾名思义就是采用声明的方式来处理事务。这里所说的声明,是指在配置文件中声明。用在 Spring 配置文件中声明式的处理事务来代替代码式的处理事务。

声明式事务处理的作用

  • 事务管理不侵入开发的组件。具体来说,业务逻辑对象就不会意识到正在事务管理之中,事实上也应该如此,因为事务管理是属于系统层面的服务,而不是业务逻辑的一部分,如果想要改变事务管理策划的话,也只需要在定义文件中重新配置即可
  • 在不需要事务管理的时候,只要在设定文件上修改一下,即可移去事务管理服务,无需改变代码重新编译,这样维护起来极其方便

注意:Spring 声明式事务控制底层就是AOP

声明式事务控制的实现

声明式事务控制明确事项:

  • 谁是切点?——业务方法
  • 谁是通知?——事务控制
  • 配置切面

声明式事务控制的配置要点

平台事务管理器配置

1
2
3
4
<!--配置平台事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>

事务通知的配置

1
2
3
4
5
6
<!--事务增强配置-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
	<tx:attributes>
		<tx:method name="*"/>
	</tx:attributes>
</tx:advice>

其中,<tx:method> 代表切点方法的事务参数的配置,例如:

1
<tx:method name="transfer" isolation="REPEATABLE_READ" propagation="REQUIRED" timeout="-1" read-only="false"/>
  • name:切点方法名称
  • isolation:事务的隔离级别
  • propogation:事务的传播行为
  • timeout:超时时间
  • read-only:是否只读

事务aop织入的配置

1
2
3
4
5
<!--配置事务的aop织入-->
<aop:config>
    <aop:pointcut id="txPointcut" expression="execution(* o.service.impl.*.*(..))"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>

基于注解的声明式事务控制

  • 使用 @Transactional 在需要进行事务控制的类或是方法上修饰,注解可用的属性同 xml 配置方式,例如隔离 级别、传播行为等。
  • 注解使用在类上,那么该类下的所有方法都使用同一套注解参数配置。
  • 使用在方法上,不同的方法可以采用不同的事务参数配置。
  • xml配置文件中要开启事务的注解驱动<tx:annotation-driven />

声明式事务控制的配置要点

平台事务管理器配置(xml方式)

1
2
3
4
<!--配置平台事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>

事务通知的配置(@Transactional注解配置)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// 转账事务
@Transactional
public void transfer(String outMan, String inMan, double money) {
    // 出账
    accountDao.out(outMan,money);
    // 人为制造错误(测试用)
    int i = 1/0;
    // 入帐
    accountDao.in(inMan,money);
}

事务注解驱动的配置

1
2
<!--事物的注解驱动-->
<tx:annotation-driven transaction-manager="transactionManager"/>
最后更新于 Mar 26, 2022 18:14 UTC
Built with Hugo
Theme Stack designed by Jimmy