Spring框架——AOP入门笔记以及个人总结
注:作者本人也是初学者,所以本文有些总结性见解可能存在问题,但是多数问题都是在上网查询过资料后总结的,如果有逻辑或者原理上的错误,或者见解不同,欢迎在评论区讨论!!!
目录
Spring的AOP
1.什么是AOP
2.AOP的作用及优势
3.AOP的底层实现
第一种情况:需要增强的类实现了某个接口(JDK动态代理)
第二种情况:需要增强的类并没有实现某个接口(也适用于有接口)(cblib代理)
4.AOP相关术语
这里我任然拿开篇说的例子来说明
基于xml的aop开发
1.基本步骤
2.配置文件书写步骤
3.通知配置语法
4.AOP五大通知类型
6.切入点表达式
基于注解的aop开发
1.配置文件
2.注解
Spring的AOP
1.什么是AOP
面向切面编程,通过预编译方式和运行期动态带来实现程序的统一维护的一种技术,利用AOP可以对业务逻辑的各个部分进行隔离,降低各部分的耦合度, 提高程序的可重用性,提高开发效率,aop是oop的延续,是Aspect Oriented Programming的缩写,意思是面向切面编程。
末日丧尸类电影应该都看过吧,末日神器之一就是绑在扫帚棍子上的水果刀。水果刀本身的形状功能没有变,但是我们通过绑上一根棍子加大了他的攻击范围,这就是AOP。
2.AOP的作用及优势
作用:在程序运行期,在不改变源码的情况下,可以对方法进行增强
优势:减少重复性代码,提高开发效率,并且便于维护
3.AOP的底层实现
第一种情况:需要增强的类实现了某个接口(JDK动态代理)
AOP底层是通过Spring提供的动态代理技术实现的,在运行期间,Spring通过动态代理技术动态的生成代理对象,代理对象方法执行时,进行增强功能的介入,在调用目标对象的方法,从而完成功能增强。
public interface TargetInterface {void show(); }
public class Target implements TargetInterface{@Overridepublic void show() {System.out.println("使用了普通show方法");} }
public class ProxyDemo {public static void main(String[] args) {final Target tg = new Target(); final TargetInterface target = (TargetInterface) Proxy.newProxyInstance(Target.class.getClassLoader(), Target.class.getInterfaces(), new InvocationHandler() {@Override//参数分别为proxy对象,method对象(反射机制Class类中的三大对象之一),方法的属性public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("使用增强方法去增强show方法");//通过原对象和方法对象,以及方法参数,利用反射机制启动方法Object invoke = method.invoke(tg,args);//return原方法的返回值return invoke;}}); target.show();} }
这里我们不难看出,通过这样的方法我们在使用反射机制使用原方法前,可以进行一些列调用前的操作。在原封不动获取原方法返回值后,同样可以对原方法的返回值进行加工。
第二种情况:需要增强的类并没有实现某个接口(也适用于有接口)(CGLIB代理)
这里就是基于CGLIB进行动态代理增强。
科普CGLIB:CGLIB是一个强大的、高性能的代码生成库。其被广泛应用于AOP框架(Spring、dynaop)中,用以提供方法拦截操作。
public class Demo1 {public static void main(final String[] args) {//目标对象final Target target = new Target();//使用动态生成给代理对象 基于cglib//1.创建增强器对象Enhancer enhancer = new Enhancer();//2.设置父类(目标类)(设置目标类为代理对象的父类)enhancer.setSuperclass(Target.class);//3.设置回调enhancer.setCallback(new MethodInterceptor() {@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {System.out.println("记录日志...");//执行方法Object invoke = method.invoke(target, args);System.out.println("使用aop增强了show方法");return invoke;}});//由设置的父类创建对象Target proxy = (Target) enhancer.create();proxy.show();} }
注意:增强器的create方法,此方法会依据以上第2步设置父类的操作,将代理对象的父类设置成为目标类,并且使用多态的方式返回一个Object类型对象,实际上就是:Object o = new 继承目标类的代理类()。所以最后使用Target强转,强转后的对象任然是利用了多态的方式的实现。
使用CGLIB方式结合多态,同样可以完成第一种情况的增强操作。
public class Demo1 {public static void main(final String[] args) {//目标对象final TargetInterface target = new Target();//使用动态生成给代理对象 基于cglib//1.创建增强器对象Enhancer enhancer = new Enhancer();//2.设置父类(目标类)(设置的是代理对象的父类为目标类的父类,此时代理对象类和目标类是兄弟类关系)enhancer.setSuperclass(TargetInterface.class);//3.设置回调enhancer.setCallback(new MethodInterceptor() {@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {System.out.println("记录日志...");//执行方法Object invoke = method.invoke(target, args);System.out.println("使用aop增强了show方法");return invoke;}});//由设置的目标类的父类创建对象TargetInterface proxy = (TargetInterface) enhancer.create();proxy.show();} }
如果代理对象父类设置的是Target,也就是目标类,那么代理对象就是Target的子类,增强后后创建对象就相当于把Target的子类强转成TargetInterface,当然,强转成Target也没有问题
如果代理对象父类设置的是TargetInterface,那么代理对象就是TargetInterface的实现类,此时Target和代理类是实现了同一个接口的兄弟类,不能够强转为Target。因此强转成TargetInterface可以,但是,此时代理对象与Target类是属性兄弟关系,无法强转。
4.AOP相关术语
目标对象:代理的目标对象
代理:一个类被AOP植入增强,产生的结果代理类
连接点:那些被拦截到的点(一般指被拦截到的方法)
切入点:针对于哪些连接点进行拦截定义
增强/通知:指定连接点之后的事情就是通知
切面:切入点和通知的结合
这里我任然拿开篇说的例子来说明
假设现在世界末日,到处都是被病毒感染的丧尸,你需要升级手头能用的日常工具,在末日中生存下来:
首先我们需要一把能够杀死丧尸的待增强武器——水果刀(目标对象)
其次我们需要把这个水果刀增强为长柄水果刀(产生的代理对象)
我们开始思考这把水果刀可以从刀把,是否淬毒等方面进行增强(连接点,即业务层所有方法)
我们发现淬毒杀不了丧尸,所有我们可以在刀把处增强(切入点,也就是被检测到需要增强的方法)
我们把水果刀加装了长木棍(织入)
你完成了攻击距离的增强(通知,具体增强的内容,分为前置通知、后置通知、异常通知、最终通知、环绕通知)
水果刀的刀把处(切入点)完成了攻击距离的增强(通知)(切面=切入点+通知)
基于xml的aop开发
1.基本步骤
导包
<dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.2.9.RELEASE</version> </dependency> <dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.8.13</version> </dependency>
创建目标接口和目标类
//目标类 public class UserServiceImpl {public void method(){System.out.println("UserMethod...");} }
创建切面类(也就是为目标类增强方法的类,类中的方法从目标类非方法的不同运行阶段执行,对方法进行增强)
public class MyAspect {public void before(){System.out.println("前置方法增强...");} }
配置
<?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/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttps://www.springframework.org/schema/aop/spring-aop.xsd"> <!--配置目标类--><bean id="userService" class="com.tencent.service.UserServiceImpl"></bean> <!--配置切面类--><bean id="myAspect" class="com.tencent.service.MyAspect"></bean> <!--配置切点表达式和增强的植入关系--><aop:config><!--引用myAspect的Bean为切面类--><aop:aspect ref="myAspect"><!--配置目标类的切入点--><aop:before method="before" pointcut="execution(public void com.tencent.service.UserServiceImpl.method())"></aop:before></aop:aspect></aop:config> </beans>
2.配置文件书写步骤
1.使用<bean>标签配置目标类(设置id,class)
2.使用<bean>标签配置切面类(设置id,class)
3.使用<aop:config>标签开始配置增强
4.在<aop:config>内部使用<aop:aspect>标签引入切面类,来增强目标类(使用ref引入)
5.使用<aop:通知类型>标签设置用于增强的方法(切面类中)和需要增强的方法(目标类中),其中method属性用来引入用于增强的方法,pointcut属性使用通知配置语法来引入需要增强的方法
3.通知配置语法
<aop:before method="before" pointcut="execution(* com..*.*(..))"></aop:before> <aop:通知类型 method="通知方法名称" pointcut="切入点表达式"></aop:通知类型>
4.AOP五大通知类型
通知类型有5个:前置通知(before),后置通知(after-returning),异常通知(after-throwing),最终通知(after),环绕通知。这五个通知一一对应切面类的五个方法(自己手写),在其对应的方法对目标方法增强后进行通知。
这五个通知的中文名不同的文章有不同的翻译,可能有的把after翻译为后置通知,而我把after-returning翻译为后置通知。当然这都没错,我这么翻译是基于自己对这五个通知的理解,但是所有文章的英文名一定对应其后面的功能。
前置通知before通知的方法在目标方法运行前执行
后置通知after-returning通知的方法在方法调用后正常返回的时候通知,可以获取返回值,发生异常的时候不会执行
异常通知after-throwing通知的方法在目标方法出现异常后执行
最终通知after通知的方法在方法执行完后,无论程序是否发生异常都会执行的
而最特殊的环绕通知around通知的方法是前四个通知的结合体
以下为切面类代码
public class Logger {//前置通知public void beforePrintLog(){System.out.println("在调用方法前记录操作日志");}//后置通知public void afterPrintLog(){System.out.println("在调用方法后记录操作日志");}//异常通知public void exceptionPrintLog(){System.out.println("出现异常后记录操作日志");}//最终通知public void finalPrintLog(){System.out.println("最终记录操作日志");}//环绕通知public Object aroundPrintLog(ProceedingJoinPoint proceedingJoinPoint){//设置空对象,用于记录目标方法被增强后的返回值Object returnValue = null;try {//此时相当于执行前置方法System.out.println("前置方法");//通过ProceedingJoinPoint对象获取原本方法的参数Object[] args = proceedingJoinPoint.getArgs();System.out.println("参数列表" + Arrays.toString(args));//通过ProceedingJoinPoint对象执行目标方法System.out.println("执行目标方法");returnValue = proceedingJoinPoint.proceed(args);//此处执行后置方法System.out.println("后置方法");return returnValue;} catch (Throwable throwable) {//此处相当于执行异常方法System.out.println("异常方法");throwable.printStackTrace();}finally {//此处相当于执行最终方法System.out.println("最终方法");return returnValue;}} }
6.切入点表达式
execution(public void com.tencent.service.UserServiceImpl.method())
1).语法:execution([访问权限修饰符] 返回值 包名.类名.方法名(参数))
2).修饰符:修饰符可以省略 ——> execution(返回值 包名.类名.方法名(参数))
3).返回值:可以是*表示,代替任意返回值
execution(* 包名.类名.方法名(参数))
4).包名,类名,方法名都可以使用*代替
execution(* *.*.*(参数))
5).参数列表可以使用..两个点,表示任意个数、任意类型参数
execution(* *.*.*(..))
基于注解的aop开发
1.配置文件
<!-- 配置注解作用域 --> <context:component-scan base-package="com.tencent"></context:component-scan> <!--aop的自动代理--> <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
2.注解
1.@Aspect:在类上进行注解,表示该类是一个切面bean,相当于原本用来导入切面bean的<aop:aspect>标签
2.@Before @AfterReturning @AfterThrowing @After @Around这五个注解都作用于方法上,分别对应前置通知,后置通知,异常通知,最终通知和环绕通知,每个注解用于表示该方法是对应通知。通知中使用切入点表达式,以此表示此方法用于增强的目标方法。使用这些注解,通知了何时(五个时间)在目标方法(execution中配置的方法)执行哪个方法(注解下方的方法)
3.@Pointcut:作用在方法上,与作用的方法内容无关,作用的方法仅仅就是一个标志,注解中可以配置execution切入点表达式,而作用的方法就相当于一个id,结合2中的五个注解,就可以达到减少代码冗余的操作。
以下为切面类代码
@Component("logger") @Aspect public class Logger {@Pointcut("execution(* com..AccountServiceImpl.*(..))")public void kong(){};@Before("kong()")public void beforeOp(){System.out.println("before...");}@AfterReturning("kong()")public void afterOp(){System.out.println("after...");}@AfterThrowing("kong()")public void exceptionOp(){System.out.println("exception...");}@After("kong()")public void finalOp(){System.out.println("final...");}@Around("kong()")public Object middle(ProceedingJoinPoint pjp){//定义返回值Object returnValue = null;try {//启动前置方法System.out.println("before...");//执行当前方法Object[] args = pjp.getArgs();System.out.println("当前方法参数为" + Arrays.toString(args));returnValue = pjp.proceed();//启动后置方法System.out.println("after...");return returnValue;} catch (Throwable throwable) {System.out.println("exception...");throwable.printStackTrace();}finally {System.out.println("finally...");return returnValue;}} }
Spring框架——AOP入门笔记以及个人总结相关推荐
- 视频教程-Spring框架快速入门到精通-Java
Spring框架快速入门到精通 十年项目开发经验,主要从事java相关的开发,熟悉各种mvc开发框架. 王振伟 ¥18.00 立即订阅 扫码下载「CSDN程序员学院APP」,1000+技术好课免费看 ...
- spring框架 AOP核心详解
AOP称为面向切面编程,在程序开发中主要用来解决一些系统层面上的问题,比如日志,事务,权限等待,Struts2的拦截器设计就是基于AOP的思想,是个比较经典的例子. 一 AOP的基本概念 (1)Asp ...
- go web框架_golang微服务框架go-micro 入门笔记2.2 micro工具之微应用利器micro web
micro web micro 功能非常强大,本文将详细阐述micro web 命令行的功能 阅读本文前你可能需要进行如下知识储备 golang分布式微服务框架go-micro 入门笔记1:搭建go- ...
- Spring框架AOP源码剖析
今天我要和大家分享的是 AOP(Aspect-Oriented Programming)这个东西的源码剖析,作为多年的开发者,想必大家在面试的时候都被问过,你知道Spring框架AOP的底层实现机制吗 ...
- Spring框架快速入门
目录 什么是Spring? 为什么学习Spring? Spring的快速入门 Spring的版本 IOC概述 什么是IOC? Spring的IoC入门 下载Spring的开发包 创建web项目,引入S ...
- Spring Boot————AOP入门案例及切面优先级设置
看了这篇文章,如果你还是不会用AOP来写程序,请你打我!! =.=||| 引言 Spring AOP是一个对AOP原理的一种实现方式,另外还有其他的AOP实现如AspectJ等. AOP意为面向切面编 ...
- Spring框架 AOP面向切面编程(转)
一.前言 在以前的项目中,很少去关注spring aop的具体实现与理论,只是简单了解了一下什么是aop具体怎么用,看到了一篇博文写得还不错,就转载来学习一下,博文地址:http://www.cnbl ...
- Spring框架----AOP的概念及术语
1.什么是AOP AOP:全称是 Aspect Oriented Programming 即:面向切面编程 简单的说它就是把我们程序重复的代码抽取出来,在需要执行的时候,使用动态代理的技术,在不修改源 ...
- ❤️六万字《Spring框架从入门到精通》(建议收藏)❤️
❤️ Spring 代码:https://github.com/Donkequan/Spring-Study ❤️ 希望各位博友三连+关注!!! 1.简介 spring理念:是现有的技术更加容易使用, ...
最新文章
- awk -f 分隔符 命令_Linux三剑客之awk
- java中两个整形相除,向上取整
- 【Android】3.12 兴趣点( POI)搜索功能
- 对“纯手工编写的第一个Hibernate程序”的改进
- java的imshow方法_imshow窗口是截止的
- hadoop SecondaryNameNode和NameNode
- wordpress二次开发技巧-functions.php篇
- [Linux运维 -- 数据库]mysql处理时间
- 将对话框(提示框)中的内容粘贴到记事本
- 16节课搞懂大数据,视频教程限时免费领取
- (转) Lua: 给 Redis 用户的入门指导
- 拓端tecdat:matlab用Logistic逻辑回归建模和马尔可夫链蒙特卡罗MCMC方法分析汽车实验数据
- Linux基础入门(详细版)
- 延时delay1s程序 c语言,汇编语言软件延时1s的实现方法
- IP前缀列表配置实验
- 微信推出“腾讯电子签”具有提醒对方还钱
- 两年老网站IP100 到底错哪儿了?
- git 和 phabricator arc 常用 命令解析
- 计算机硬件故障检测实验报告,计算机系统的硬件检测实验报告
- 【过关斩将7】面试谈薪资时,HR压价怎么办?
热门文章
- 合并文件夹内所有Excel文件(目前仅限于合并单层文件夹,如果文件夹下面有文件夹,暂未加入此功能,默认合并所有文件的所有Sheet)优化文件名_变更为:文件夹名字 + 合并的文件-(xls+xlsx)
- Redis 集群,分布式,微服务概念和区别理解
- java 两个经纬度 距离_Java 根据两个经纬度,得到两点距离
- Homebrew安装及换源
- MacBooster 6 for Mac v6.0.6 软件卸载系统清理 破解版下载
- 机器学习——代价函数
- KDD99数据集预处理
- 树莓派环境处理_树莓派开发环境配置
- Object.defineProperty方法(详解)
- PowerToys——免费、强大、高效的微软官方效率提升工具集,办公学习宝藏软件