Spring企业级程序设计 • 【第3章 面向切面编程】
全部章节 >>>>
本章目录
3.3.1 使用@Pointcut和@Around完成环绕增强
3.1 AOP基本概念和术语
3.1.1 AOP概念
面向切面编程(Aspect Oriented Programming,AOP),通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。
AOP是面向对象编程(Object Oriented Programming,OOP)的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生泛型。
利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。常用于日志记录、性能统计、安全控制、事务处理以及异常处理等等。
3.1.2 AOP的术语解释
AOP的关键术语:
切面(Aspect):是共有功能的实现。
连接点(Join Point):是程序在运行过程中能够插入切面的地点。
通知(Advice):即增强,是切面的具体实现。分为前置通知(Before)、后置通知(AfterReturning)、异常通知(AfterThrowing)、环绕通知(Around)和最终通知(After)
切入点(Pointcut):用于定义通知应该切入到哪些连接点上。
目标对象(Target):是即将切入切面的对象,即那些被通知的对象。
代理对象(Proxy):将通知应用到目标对象之后被动态创建的对象。
织入(Weaving):将切面应用到目标对象从而创建一个新的代理对象的过程。
3.1.3 通知类型介绍
AOP为通知定义了org.asoalliance.Advice接口,Spring支持5种类型的通知:
前置通知:表示在连接点被调用前执行的通知。
后置通知:表示在某个连接点成功执行之后执行的通知。
环绕通知:表示包围一个连接点通知,在被通知的方法调用之前和之后执行自定义的方法
异常通知:表示在方法抛出异常后执行的通知。
最终通知:表示在某个连接点执行之后执行的通知。
3.1.4 通过AOP模拟事务操作
使用AOP中的环绕通知来模拟事务操作
引入相关jar包

引入aop约束
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.2.xsd ">
<!—此处输入内容-->
</beans>
在com.mhys.demo.service包下创建OrderService类,声明insertOrder()方法
在com.mhys.demo.advice包下创建TransactionAdvice类,声明around()方法和after()方法
在applicationContext.xml配置文件中配置目标对象、通知对象和切入点
<!-- 配置目标对象 -->
<bean id="orderService" class="com.mhys.demo.service.OrderService"></bean>
<!-- 配置通知对象 -->
<bean id="transactionAdvice" class="com.mhys.demo.advice.TransactionAdvice"></bean>
<aop:config>
<!-- 配置切入点 -->
<aop:pointcut expression="execution(* com.mhys.demo.service.*Service.*(..))" id="pointCut"/>
<aop:aspect ref="transactionAdvice">
<!-- 环绕通知 -->
<aop:around method="around" pointcut-ref="pointCut"/>
<!-- 最终通知 -->
<aop:after method="after" pointcut-ref="pointCut"/>
</aop:aspect>
</aop:config>
在com.mhys.demo.test包下编写测试类

3.1.5 实践练习
3.2 基于XML配置的AOP开发
3.2.1 使用<aop:pointcut />标签定义切入点
切入点是指哪些方法需要被执行“AOP”,是由“Pointcut Expression”来描述的。
Expression常用方法有方法参数匹配、方法描述匹配、目标类匹配等,其中最常用的是方法描述匹配。
语法:
execution(<修饰符模式>?<返回类型模式><声明类型模式>?<方法名模式>(<参数模式>)<异常模式>?)
3.2.1 使用<aop:pointcut />标签定义切入点
6种使用execution()表达式实例。:
execution(public void com.mhys.demo.UserServiceImpl.save())
execution(void com.mhys.demo.UserServiceImpl.save())表达式
execution(* com.mhys.demo.UserServiceImpl.save())表达式
execution(* com.mhys.demo.UserServiceImpl.*())表达式
execution(* com.mhys.demo.*ServiceImpl.*(..))表达式
execution(* com.mhys.demo..*ServiceImpl.*(..))表达式
示例:定义一个切入点配置某个包以及子包下的以Service结尾的类中任意返回值、任意参数的以add开头的所有方法,然后在织入最终通知
<!-- 配置目标对象 -->
<bean id="userService" class="com.mhys.demo.user.UserService"></bean>
<bean id="orderService" class="com.mhys.demo.order.OrderService"></bean>
<!-- 配置通知对象 -->
<bean id="myAdvice" class="com.mhys.demo.advice.MyAdvice"></bean>
<aop:config>
<!-- 配置切入点 -->
<aop:pointcut expression="execution(* com.mhys.demo..*Service.add*(..))" id="pointCut"/>
<aop:aspect ref="myAdvice">
<!—最终通知 -->
<aop:after method="afterAdvice" pointcut-ref="pointCut"/>
</aop:aspect>
</aop:config>

3.2.2 使用<aop:before />标签来前置增强
前置通知是在目标方法之前执行。常见的应用场景是使用前置通知可以在目标方法执行之前执行,插入系统日志。
语法:
<aop:before method="切面类的方法名" pointcut-ref="切入点表达式是引用"/>
示例:在MyAdvice通知类中,声明一个beforeAdvice()前置增强方法,然后在applicationContext.xml配置文件中使用<aop:before />标签织入前置通知
<aop:config>
<!-- 配置切入点 -->
<aop:pointcut expression="execution(* com.mhys.demo..*Service.add*(..))" id="pointCut"/>
<aop:aspect ref="myAdvice">
<!-- 最终通知 -->
<aop:after method="afterAdvice" pointcut-ref="pointCut"/>
<!-- 前置通知 -->
<aop:before method="beforeAdvice" pointcut-ref="pointCut"/>
</aop:aspect>
</aop:config>

3.2.3 使用<aop:after-returning />标签来后置增强
后置通知是在目标方法执行成功之后执行。其使用方法和前置通知类似。通过后置通知可以为目标方法添加新的逻辑代码,使业务方法增强。
语法:
<aop:after-runturning method="切面类的方法名" pointcut-ref="切入点表达式是引用"/>
示例:在MyAdvice通知类中,声明一个afterReturningAdvice()后置增强方法,然后在applicationContext.xml配置文件中使用<aop:after-returning />标签织入后置通知。
<aop:config>
<!-- 配置切入点 -->
<aop:pointcut expression="execution(* com.mhys.demo..*Service.add*(..))" id="pointCut"/>
<aop:aspect ref="myAdvice">
<!-- 最终通知 -->
<aop:after method="afterAdvice" pointcut-ref="pointCut"/>
<!-- 前置通知 -->
<aop:before method="beforeAdvice" pointcut-ref="pointCut"/>
<!-- 后置通知 -->
<aop:after-returning method="afterReturningAdvice" pointcut-ref="pointCut"/>
</aop:aspect>
</aop:config>

3.2.4 使用<aop:after-throwing />标签处理异常
异常通知是在方法抛出异常后执行的通知,它最适合的应用场景是在事务管理中。当参与事务的某个Dao发生异常时,事务管理器就必须回滚事务。
语法:
<aop:after-throwing method="切面类的方法名" pointcut-ref="切入点表达式是引用"/>
示例:
在MyAdvice通知类中,声明一个afterthrowingAdvice()异常增强方法;然后在applicationContext.xml配置文件中使用<aop:after-throwing />标签织入异常通知。
<aop:config>
<!-- 配置切入点 -->
<aop:pointcut expression="execution(* com.mhys.demo..*Service.add*(..))" id="pointCut"/>
<aop:aspect ref="myAdvice">
<!-- 最终通知 -->
<aop:after method="afterAdvice" pointcut-ref="pointCut"/>
<!-- 前置通知 -->
<aop:before method="beforeAdvice" pointcut-ref="pointCut"/>
<!-- 后置通知 -->
<aop:after-returning method="afterReturningAdvice" pointcut-ref="pointCut"/>
<!-- 异常通知 -->
<aop:after-throwing method="afterThrowingAdvice" pointcut-ref="pointCut"/>
</aop:aspect>
</aop:config>

3.2.5 实践练习
3.3 基于注解的AOP开发
3.3.1 使用@Pointcut和@Around完成环绕增强
示例:使用@Pointcut定义切入点,@Round定义环绕增强方法。模拟实现事务的开启和提交操作。
<!-- 开启注解扫描 -->
<context:component-scan base-package="com.mhys.demo"></context:component-scan>
<!-- 启动AOP注解 -->
<aop:aspectj-autoproxy ></aop:aspectj-autoproxy>

3.3.2 使用@After完成最终增强
示例:在TransactionAdvice通知类中声明一个afterAdvice()最终通知方法,然后使用@After定义最终通知。
@Component
@Aspect
// 表示该类是一个通知类
public class TransactionAdvice {
// 原有代码省略
@After("TransactionAdvice.pc()")
public void afterAdvice(){
System.out.println("关闭事务");
}
}
新增afterAdvice()最终增强方法

3.3.3 使用@Before完成前置增强
示例:在TransactionAdvice通知类中声明一个beforeAdvice()前置增强方法,然后使用@Before定义前置通知。
@Component
@Aspect
// 表示该类是一个通知类
public class TransactionAdvice {
// 代码省略
@Before("TransactionAdvice.pc()")
public void beforeAdvice(){
System.out.println("执行前置通知方法!");
}
}
新增beforeAdvice()前置增强方法

3.3.4 使用@AfterReturning完成后置增强
示例:在TransactionAdvice通知类中声明一个afterReturningAdvice()后置增强方法,然后使用@AfterReturning定义后置通知。
@Component
@Aspect
// 表示该类是一个通知类
public class TransactionAdvice {
// 代码省略
@AfterReturning("TransactionAdvice.pc()")
public void afterReturningAdvice(){
System.out.println("执行后置通知方法!");
}
}
新增afterReturningAdvice()后置增强方法

3.3.5 使用@AfterThrowing异常拦截
示例:在TransactionAdvice通知类中声明一个afterThrowingAdvice()异常增强方法,然后使用@AfterThrowingg定义异常通知。
@Component
@Aspect
// 表示该类是一个通知类
public class TransactionAdvice {
// 代码省略
@AfterThrowing("TransactionAdvice.pc()")
public void afterThrowingAdvice(){
System.out.println("发现异常,执行异常通知方法!");
}
}
新增afterThrowingAdvice异常增强方法

3.3.6 实践练习
3.4 综合案例
3.4.1 需求说明
对于CRM的系统而言,现在有很多的Dao类,比如客户的Dao,联系人的Dao等等。
客户提出一个需求,需要开发人员实现一个功能,对所有的Dao的类中的以get、find开头的方法实现日志输出打印,检测方法的性能,对所有的Dao类中的以save、add、insert、modify、update、delete以及del开头的方法实现事务的开启、提交和关闭操作。要求如下:
- 使用前置通知和后置通知实现日志输出打印。
- 使用环绕通知实现事务的开启和提交操作。
- 使用最终通知实现事务的关闭操作。

3.4.2 实现思路
需求说明解决思路:
在applicationContext.ml配置文件中开启注解扫描功能和AOP注解功能。
在com.mhys.demo.dao包下创建CustomerDao接口,声明getCustomerAll()方法、addCustomer()方法、modifyCustomer()方法和deleteCustomer()方法。 在com.mhys.demo.dao包下创建CustomerDaoImpl类,实现CustomerDao接口,实现接口4个方法。
在com.mhys.demo.dao包下创建LinkmanDao接口,声明getLinkmanAll()方法、addLinkman()方法、modifyLinkman()方法和deleteLinkman()方法。
在com.mhys.demo.dao包下创建LinkmanDaoImpl类,实现LinkmanDao接口,实现接口4个方法。
在com.mhys.demo.service包下创建CustomerService接口,声明getCustomerAll()方法、addCustomer()方法、modifyCustomer()方法和deleteCustomer()方法。
在com.mhys.demo.service包下创建CustomerServiceImpl类,实现CustomerService接口,实现接口4个方法。
com.mhys.demo.service包下创建LinkmanService接口,声明getLinkmanAll()方法、addLinkman()方法、modifyLinkman()方法和deleteLinkman()方法。
在com.mhys.demo.service包下创建LinkmanServiceImpl类,实现LinkmanService接口,实现接口4个方法。
在com.mhys.demo.advice包下创建LoggerAdvice通知类,定义切入点,声明loggerBeforeAdvice()方法作为环绕增强。
在com.mhys.demo.advice包下创建TransactionAdvice通知类,定义切入点,声明transactionBefor()方法、transactionAfterReturning()方法和transactionAfter()方法。
在com.mhys.demo.test包下编写测试类。
3.4.3 实践练习
总结
Spring AOP是OOP的补充,它也提供了模块化。在面向对象编程中,关键的单元是对象,AOP的关键单元是切面。
通知(advice)是在程序中想要应用在其他模块中的横切关注点的实现。Advice主要有以下5种类型:前置通知、后置通知(After Retuning Advice)、异常拦截通知(After Throwing Advice)、最终通知(After Advice)、围绕通知(Around Advice)
Spring企业级程序设计 • 【第3章 面向切面编程】的更多相关文章
- Spring(三)--AOP【面向切面编程】、通知类型及使用、切入点表达式
1.概念:Aspect Oriented Programming 面向切面编程 在方法的前后添加方法 2.作用:本质上来说是一种简化代码的方式 继承机制 封装方法 动 ...
- Spring之控制反转——IoC、面向切面编程——AOP
控制反转——IoC 提出IoC的目的 为了解决对象之间的耦合度过高的问题,提出了IoC理论,用来实现对象之间的解耦. 什么是IoC IoC是Inversion of Control的缩写,译为控制 ...
- Spring详解(五)------面向切面编程
.AOP 什么? AOP(Aspect Oriented Programming),通常称为面向切面编程.它利用一种称为"横切"的技术,剖解开封装的对象内部,并将那些影响了多个类的 ...
- spring入门(四)【面向切面编程】
开发过程中很多时候会用到日志.事务等操作,这些操作如果要写在业务代码中会相当麻烦,这时就会用到面向切面编程(AOP),AOP作为一种编程思想,和OOP有着不同的侧重点,面向对象侧重于万事万物皆对象,而 ...
- Spring框架(四)AOP面向切面编程
一.前言 在以前的项目中,很少去关注spring aop的具体实现与理论,只是简单了解了一下什么是aop具体怎么用,看到了一篇博文写得还不错,就转载来学习一下,博文地址:http://www.cnbl ...
- Spring学习手札(二)面向切面编程AOP
AOP理解 Aspect Oriented Program面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术. 但是,这种说法有些片面,因为在软件工程中,AOP的价值体现的并 ...
- Spring详解篇之 AOP面向切面编程
一.概述 Aop(aspect oriented programming面向切面编程),是spring框架的另一个特征.AOP包括切面.连接点.通知(advice).切入点(pointCut) . 1 ...
- 04 Spring:01.Spring框架简介&&02.程序间耦合&&03.Spring的 IOC 和 DI&&08.面向切面编程 AOP&&10.Spring中事务控制
spring共四天 第一天:spring框架的概述以及spring中基于XML的IOC配置 第二天:spring中基于注解的IOC和ioc的案例 第三天:spring中的aop和基于XML以及注解的A ...
- Spring总结六:AOP(面向切面编程)
概述: AOP(Aspect-Oriented Programming,面向切面的编程),它是可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术.它是一种新的 ...
随机推荐
- 高效读取大文件,再也不用担心 OOM 了!
内存读取 第一个版本,采用内存读取的方式,所有的数据首先读读取到内存中,程序代码如下: Stopwatch stopwatch = Stopwatch.createStarted(); // 将全部行 ...
- Linux系统时钟与硬件时钟
linux系统有两个时钟:一个是由主板电池驱动的硬件时钟(Real Time Clock),也叫做RTC或者叫CMOS时钟.当操作系统关机的时候,用这个来记录时间,但是对于运行的系统是不用这个时间的: ...
- [学习总结]6、Android异步消息处理机制完全解析,带你从源码的角度彻底理解
开始进入正题,我们都知道,Android UI是线程不安全的,如果在子线程中尝试进行UI操作,程序就有可能会崩溃.相信大家在日常的工作当中都会经常遇到这个问题,解决的方案应该也是早已烂熟于心,即创建一 ...
- feign中开启熔断的书写步骤
/** 1.在pom.xml中引入依赖 2.在application.yaml中开启hystrix 3.在方法上配置熔断类 4.书写接口的实现类 **/ //1.在pom.xml中引 ...
- java上传图片或文件
转载至:http://www.xdx97.com/#/single?bid=8b351a73-922c-eadc-512e-9e248a3efde9 前端通过form表单用post方式提交文件,后台进 ...
- SQL Server 2014如何DATEDIFF()函数截取对应时间年月日
4.1 定义和用法: DATEDIFF()函数返回两个日期之间的时间 4.2 语法 DATEDIFF(datepart,startdate,enddate) datepart值: year | qua ...
- JS 双向数据绑定、单项数据绑定
简单的双向数据绑定 <!DOCTYPE html> <html lang="en"> <head> <meta charset=" ...
- Table.NestedJoin合并…Join(Power Query 之 M 语言)
数据源: "销量表"和"部门表"两个查找表,每个表中都有"姓名"列 目标: 根据"姓名列"将"部门表" ...
- LuoguB2133 我家的门牌号 题解
Update \(\texttt{2021.11.27}\) 修复了代码中的 \(10000\) 写成 \(n\) 的错误. Content 一个家庭住在一个胡同里面,门牌号从 \(1\) 开始编号. ...
- 使用ANTLR解析CSV和JSON
再续 ANTLR专题 ,有了前面的基础,下面开始用ANTLR写一些有趣且实用的程序. CSV和JSON这两种数据格式对软件开发人员来说最熟悉不过了,一般读写CSV或JSON格式的数据都会借助现成的.比 ...