spring学习笔记 (3)aop的原理与实现
'有道云笔记' 笔记整理与学习辅助 #生活技巧# #工作学习技巧# #在线教育平台推荐#
(一)什么是aop
AOP即是面向切面编程,把分布在多个类中的内容封装在一个单独的类中,成为“切面”,这些职责内容称为“横切关注点”(即一些方法),例如日志记录,缓存,安全性能等都是常见的横切关注点。
(二)为什么使用aop
aop的用途其实很简单,即将那些不单独属于某个业务逻辑中的方法提取出来放在一个类中,供给多个类公用,以此提升代码的重用率,是业务逻辑层的代码更关注业务本身。我大致理解成其是对oop的一种横向补充,面向对象编程可以实现继承以实现子类对父类代码的继承重用,而aop将横向的类中的公用代码抽离出来以重用。
(三)aop术语
切面:即是从多个业务逻辑类中抽离出来的公用方法集合放置的一个java类。横切关注点:多个业务逻辑类中公用的方法。通知:在切面中(即那个公用Java类中)实现一个横切关注点的方法叫做通知。(四)aop的工作原理
aop终究要实现调用对象对目标对象的调用,在调用时的某个时间点需要执行如日志等其他非业务逻辑本身的操作,这就需要有某种东西拦截下这个调用,再去触发其他操作方法,即代理。所以aop框架是基于代理的。
实际的工作过程是调用对象对目标对象发起调用,而代理将这个调用拦截,并且由代理执行适用于目标方法的通知。
(五)关于代理
spring框架由基于两种代理:CGLIB的代理 和 JavaSE的代理(JDK代理)
两种代理的区别:目标对象没有实现接口,使用CGLIB;目标对象有实现接口,使用基于JavaSE的代理。
代理的实现:
JavaSE:
稍后补全 1
CGLIB:
稍后补全 1
(六)aop的实现(即如何开发切面)
在spring框架中有两种方法:
AspectJ注释样式 (注意要导入AspectJ的jar包)我导入的jar包:aspectjweaver-1.8.10.jarXML模式样式
使用这两种方法时,Spring AOP框架会自动帮助我们生成代理,不用我们手动显式创建。
ps:仅展示了切面类的实现与配置,业务逻辑类没有展示
<1> 注释样式实现:
切面类MyAspect.java:
import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component; @Aspect @Component public class MyAspect { //1.使用@PointCut表明切入点,其后会跟一个空方法 // PointCut 称为切入点 // com.unkonwn.jdk *:正则表达式,表示拦截该命名空间下的所有类与方法 @Pointcut("execution(* com.unkonwn.jdk.*.*(..))") private void myPointCut() { } //2.以下均为通知,即对应横切关注点实现的方法,这里顺便说明几种通知的类型 //a.前置通知(使用@Before()注解,括号中即使刚切入点的空方法) //前置通知会在目标方法执行前执行 @Before("myPointCut()") public void myBefore(JoinPoint joinPoint) { System.out.print("前置通知 :模拟执行权限检查...,"); System.out.print("目标类是:" + joinPoint.getTarget()); System.out.println(",被织入增强处理的目标方法为:" + joinPoint.getSignature().getName()); }//b.返回后通知(使用AfterReturning()注解,括号中即使刚切入点的空方法) //返回后通知再目标方法返回后执行 @AfterReturning(value = "myPointCut()") public void myAfterReturing(JoinPoint joinPoint) { System.out.print("后置通知:模拟记录日志...,"); System.out.println("被织入增强处理的目标方法为:" + joinPoint.getSignature().getName()); }//c.环绕通知(使用@Around()注解,括号中即使刚切入点的空方法) //目标方法执行前后都会被执行,其可以控制目标方法时候执行 @Around("myPointCut()") public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { // 开始 System.out.println("环绕开始:执行目标方法之前,模拟开启事务..."); // 执行当前目标方法 Object obj = proceedingJoinPoint.proceed(); // 结束 System.out.println("环绕结束:执行目标方法之后,模拟关闭事务..."); return obj; }//d.抛出后通知(使用AfterThrowing()注解,括号中即使刚切入点的空方法) //目标方法抛出异常后会被执行 @AfterThrowing(value = "myPointCut()", throwing = "e") public void myAfterThrowing(JoinPoint joinPoint, Throwable e) { System.out.println("异常通知:" + "出错了" + e.getMessage()); }//e.后置通知(使用@After()注解,括号中即使刚切入点的空方法) //目标方法之后执行,不管目标方法是否正常完成还是抛出异常 @After(value = "myPointCut()") public void myAfter() { System.out.println("最终通知:模拟方法结束后的释放资源..."); } }
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859配置文件applicationContext.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" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd"> <bean id="userDao" class="com.unkonwn.jdk.UserDaoImpl"/> <!-- 指定需要扫描的包,使注解生效 --> <context:component-scan base-package="com.unkonwn"/> <!-- 启动基于注解的声明式AspectJ支持 --> <aop:aspectj-autoproxy/> </beans>
1234567891011121314151617测试:即实现横切关注点的某个方法,从而被拦截触发通知。这里执行user类中的adduser()方法。
import com.unkonwn.jdk.UserDao; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestAnnotationAspectj { public static void main(String args[]) { String xmlPath = "com/unkonwn/aspectj/annotation/applicationContext.xml"; ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath); // 1 从spring容器获得内容 UserDao userDao = (UserDao) applicationContext.getBean("userDao"); // 2 执行方法 userDao.addUser(); } }
12345678910111213141516<2> XML样式实现:
切面类MyAspect.java:
import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; public class MyAspect { // 前置通知 public void myBefore(JoinPoint joinPoint) { System.out.print("前置通知 :模拟执行权限检查...,"); System.out.print("目标类是:" + joinPoint.getTarget()); System.out.println(",被织入增强处理的目标方法为:" + joinPoint.getSignature().getName()); } // 后置通知 public void myAfterReturning(JoinPoint joinPoint, Object returnVal) { System.out.print("后置通知:模拟记录日志...,"); System.out.println("被织入增强处理的目标方法为:" + joinPoint.getSignature().getName()); } /** * 环绕通知 * ProceedingJoinPoint 是JoinPoint子接口,表示可以执行目标方法 * 1.必须是Object类型的返回值 * 2.必须接收一个参数,类型为ProceedingJoinPoint * 3.必须throws Throwable */ public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { // 开始 System.out.println("环绕开始:执行目标方法之前,模拟开启事务..."); // 执行当前目标方法 Object obj = proceedingJoinPoint.proceed(); // 结束 System.out.println("环绕结束:执行目标方法之后,模拟关闭事务..."); return obj; } // 异常通知 public void myAfterThrowing(JoinPoint joinPoint, Throwable e) { System.out.println("异常通知:" + "出错了" + e.getMessage()); } // 最终通知 public void myAfter() { System.out.println("最终通知:模拟方法结束后的释放资源..."); } }
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748可以看到类中不存在注解,其通知的类型等全都交给xml文件配置
配置文件applicationContext.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-4.3.xsd"> <!-- 1 目标类 --> <bean id="userDao" class="com.unkonwn.jdk.UserDaoImpl"/> <!-- 2 切面 --> <bean id="myAspect" class="com.unkonwn.aspectj.xml.MyAspect"/> <!-- 3 aop编程 --> <aop:config> <!-- 配置切面 --> <aop:aspect ref="myAspect"> <!-- 3.1 配置切入点,通知最后增强哪些方法 --> <aop:pointcut expression="execution(* com.unkonwn.jdk.*.*(..))" id="myPointCut"/> <!-- 3.2 关联通知Advice和切入点pointCut --> <!-- 3.2.1 前置通知 --> <aop:before method="myBefore" pointcut-ref="myPointCut"/> <!-- 3.2.2 后置通知,在方法返回之后执行,就可以获得返回值 returning属性:用于设置后置通知的第二个参数的名称,类型是Object --> <aop:after-returning method="myAfterReturning" pointcut-ref="myPointCut" returning="returnVal"/> <!-- 3.2.3 环绕通知 --> <aop:around method="myAround" pointcut-ref="myPointCut"/> <!-- 3.2.4 抛出通知:用于处理程序发生异常--> <!-- * 注意:如果程序没有异常,将不会执行增强 --> <!-- * throwing属性:用于设置通知第二个参数的名称,类型Throwable --> <aop:after-throwing method="myAfterThrowing" pointcut-ref="myPointCut" throwing="e"/> <!-- 3.2.5 最终通知:无论程序发生任何事情,都将执行 --> <aop:after method="myAfter" pointcut-ref="myPointCut"/> </aop:aspect> </aop:config> </beans>
123456789101112131415161718192021222324252627282930313233343536373839测试代码一致。
菜鸟一枚,有错误的地方欢迎指出!
网址:spring学习笔记 (3)aop的原理与实现 https://www.yuejiaxmz.com/news/view/465673
相关内容
springboot整合AOP,实现log操作日志基于Spring Boot的在线学习系统的设计与实现
Spring事务控制 @Transactional(propagation = Propagation.REQUIRED, rollbackFor = {Exception.class},)
利用AOP实现一个简单的缓存存储、清除的工具
Spring Boot 事务的简单使用
如何使用SpringBoot框架构建一个具有用户管理和菜谱管理功能的美食分享在线平台?
【开题报告】基于SSM的健康饮食系统设计与实现
SSM大学校园二手书籍交易n85e5
揭秘Java框架私活:实战技巧与职场生存指南
有AI加持的卡片笔记与高效学习记忆工具