Spring中AOP学习笔记
AOP
- 描述(摘抄百度百科)
- AOP开发中的相关操作术语
- 实例(xml方式)
-
- 通知的类型
- 切入点表达式的写法:
- 实例(注解方式)
描述(摘抄百度百科)
AOP(面向切面编程):Aspect Oriented Programming的缩写。意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
AOP开发中的相关操作术语
Joinpoint(连接点):所谓连接点是指那些被拦截到的点。其实就是类里面可以被增强的方法,这些方法称为连接点。
Pointcut(切入点):所谓切入点是指我们要对哪些 Joinpoint 进行拦截的定义。比如实际开发中只对saveAccount()方法增强,该方法就是切入点。
Advice(通知/增强):增强的逻辑,称为增强或通知
Aspect(切面):把增强应用到切入点上面的过程称为切面。
Introduction(引介):引介是一种特殊的通知在不修改类代码的前提下, Introduction 可以在运行期为类动态地添加一些方法或 Field。
Target(目标对象):代理的目标对象。
Proxy(代理):代理对象
Weaving(织入):将通知应用到目标的过程。例如:将日志记录应用到saveAccount()方法上面。
下图更直观:
实例(xml方式)
创建一个meavn工程,pom文件中添加如下依赖:
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.8.RELEASE</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.6</version> </dependency> </dependencies>
AccountService 接口:
public interface AccountService { /*** * 保存账户 */ void saveAccount(); /*** * 更新账户 */ void updateAccount(Integer i); /*** * 删除账户 */ void deleteAccount(Integer i); }
AccountServiceImpl实现类:
public class AccountServiceImpl implements AccountService { public void saveAccount() { System.out.println("保存用户成功....."); } public void updateAccount(Integer i) { System.out.println("更新用户成功....."); } public void deleteAccount(Integer i) { System.out.println("删除用户成功....."); } }
再编写一个增强的类Logger:
public class Logger { /*** * @Description: 用于打印日志 * @Date: 2021/2/9 */ public void Printlog(){ System.out.println("输出打印日志....."); } }
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"> <!--配置service--> <bean id="accountService" class="com.zy.aop.service.AccountServiceImpl"></bean> <!--配置切面logger类--> <bean id="logger" class="com.zy.aop.logger.Logger"></bean> <!--配置Aop--> <aop:config> <!--配置切入点表达式--> <aop:pointcut id="log" expression="execution(public void com.zy.aop.service.AccountServiceImpl.saveAccount())"/> <!--配置切面--> <aop:aspect id="" ref="logger"> <!--前置通知:在切入点方法执行前执行--> <aop:before method="Printlog" pointcut-ref="log"></aop:before> </aop:aspect> </aop:config> </beans>
测试代码:
public class TestAop { @Test public void TestSaveAccount(){ ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml"); AccountService accountService = (AccountService) context.getBean("accountService"); accountService.saveAccount(); accountService.updateAccount(1); Integer i = 10; } }
运行结果:
标签说明:
标签 | 说明 |
---|---|
aop:config | 该标签声明aop配置开始 |
aop:pointcut | 用于配置切入点表达式。就是指定对哪些类的哪些方法进行增强。属性:expression:用于定义切入点表达式。id:用于给切入点表达式提供一个唯一标识 |
aop:aspect | 用于配置切面。属性:id:给切面提供一个唯一标识。ref:引用配置好的通知类 bean 的 id |
通知的类型
通知类型 | 标签 |
---|---|
前置通知 | <aop:before method="" pointcut-ref=""></aop:before> |
后置通知 | <aop:after-returning method="" pointcut-ref=""></aop:after-returning> |
异常通知 | <aop:after-throwing method=""></aop:after-throwing> |
最终通知 | <aop:after method="" pointcut-ref=""></aop:after> |
环绕通知 | <aop:around method="" pointcut-ref=""></aop:around> |
每一个标签中都有这三个属性method:指定通知中方法的名称。pointct:定义切入点表达式pointcut-ref:指定切入点表达式的引用。
前置通知:
<aop:config> <aop:pointcut id="log" expression="execution(* com.zy.aop.service.AccountServiceImpl.saveAccount())"/> <aop:aspect id="" ref="logger"> <!--前置通知:在切入点方法执行前执行--> <aop:before method="Printlog" pointcut-ref="log"></aop:before> </aop:aspect> </aop:config>
后置通知:
<aop:config> <aop:pointcut id="log" expression="execution(* com.zy.aop.service.AccountServiceImpl.saveAccount())"/> <aop:aspect id="" ref="logger"> <!--后置通知:在切入点方法执行后执行--> <aop:after-returning method="Printlog" pointcut-ref="log"></aop:after-returning> </aop:aspect> </aop:config>
异常通知:
<aop:config> <aop:pointcut id="log" expression="execution(* com.zy.aop.service.AccountServiceImpl.saveAccount())"/> <aop:aspect id="" ref="logger"> <!--异常通知:程序出现异常的时候,进行的操作--> <aop:after-throwing method="Printlog" pointcut-ref="log"></aop:after-throwing> </aop:aspect> </aop:config>
最终通知:
<aop:config> <aop:pointcut id="log" expression="execution(* com.zy.aop.service.AccountServiceImpl.saveAccount())"/> <aop:aspect id="" ref="logger"> <!--最终通知:在切入点方法执行后执行,无论方法是否正常执行都会在后面执行--> <aop:after method="Printlog" pointcut-ref="log"></aop:after> </aop:aspect> </aop:config>
环绕通知:
<aop:config> <aop:pointcut id="log" expression="execution(* com.zy.aop.service.AccountServiceImpl.saveAccount())"/> <aop:aspect id="" ref="logger"> <!--环绕通知--> <aop:around method="Printlog2" pointcut-ref="log"></aop:around> </aop:aspect> </aop:config>
我们要修改一下增强的类Logger中的方法:
public class Logger { /*** * @Description: 用于打印日志 * @Date: 2021/2/9 */ public void Printlog() { System.out.println("输出打印日志....."); } public Object Printlog2(ProceedingJoinPoint pro) { Object proceed = null; Object[] args = pro.getArgs();//得到方法实行所需参数 try { //在这之前是前置通知 proceed = pro.proceed(args);//调用业务层方法 //在这之后是后置通知 return proceed; } catch (Throwable t) { //在这是异常通知 } finally { //在这是最终通知 } return proceed; } }
切入点表达式的写法:
表达式语法:execution([修饰符] 返回值类型 包名.类名.方法名(参数))
写法说明:
全匹配方式:public void com.zy.aop.service.AccountServiceImpl.saveAccount(参数类型)
访问修饰符可以省略: void com.zy.aop.service.AccountServiceImpl.saveAccount(参数类型)
返回值可以使用号,表示任意返回值: com.zy.aop.service.AccountServiceImpl.saveAccount(参数类型),具体可以百度在这我就不写了。
通常情况下,我们都是对业务层的方法进行增强,所以切入点表达式都是切到业务层实现类。
execution(* com.zy.aop.service..(…))