注:作者本人也是初学者,所以本文有些总结性见解可能存在问题,但是多数问题都是在上网查询过资料后总结的,如果有逻辑或者原理上的错误,或者见解不同,欢迎在评论区讨论!!!

目录

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.基本步骤

  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>
  2. 创建目标接口和目标类

    //目标类
    public class UserServiceImpl {public void method(){System.out.println("UserMethod...");}
    }
  3. 创建切面类(也就是为目标类增强方法的类,类中的方法从目标类非方法的不同运行阶段执行,对方法进行增强)

    public class MyAspect {public void before(){System.out.println("前置方法增强...");}
    }
  4. 配置

    <?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入门笔记以及个人总结相关推荐

  1. 视频教程-Spring框架快速入门到精通-Java

    Spring框架快速入门到精通 十年项目开发经验,主要从事java相关的开发,熟悉各种mvc开发框架. 王振伟 ¥18.00 立即订阅 扫码下载「CSDN程序员学院APP」,1000+技术好课免费看 ...

  2. spring框架 AOP核心详解

    AOP称为面向切面编程,在程序开发中主要用来解决一些系统层面上的问题,比如日志,事务,权限等待,Struts2的拦截器设计就是基于AOP的思想,是个比较经典的例子. 一 AOP的基本概念 (1)Asp ...

  3. go web框架_golang微服务框架go-micro 入门笔记2.2 micro工具之微应用利器micro web

    micro web micro 功能非常强大,本文将详细阐述micro web 命令行的功能 阅读本文前你可能需要进行如下知识储备 golang分布式微服务框架go-micro 入门笔记1:搭建go- ...

  4. Spring框架AOP源码剖析

    今天我要和大家分享的是 AOP(Aspect-Oriented Programming)这个东西的源码剖析,作为多年的开发者,想必大家在面试的时候都被问过,你知道Spring框架AOP的底层实现机制吗 ...

  5. Spring框架快速入门

    目录 什么是Spring? 为什么学习Spring? Spring的快速入门 Spring的版本 IOC概述 什么是IOC? Spring的IoC入门 下载Spring的开发包 创建web项目,引入S ...

  6. Spring Boot————AOP入门案例及切面优先级设置

    看了这篇文章,如果你还是不会用AOP来写程序,请你打我!! =.=||| 引言 Spring AOP是一个对AOP原理的一种实现方式,另外还有其他的AOP实现如AspectJ等. AOP意为面向切面编 ...

  7. Spring框架 AOP面向切面编程(转)

    一.前言 在以前的项目中,很少去关注spring aop的具体实现与理论,只是简单了解了一下什么是aop具体怎么用,看到了一篇博文写得还不错,就转载来学习一下,博文地址:http://www.cnbl ...

  8. Spring框架----AOP的概念及术语

    1.什么是AOP AOP:全称是 Aspect Oriented Programming 即:面向切面编程 简单的说它就是把我们程序重复的代码抽取出来,在需要执行的时候,使用动态代理的技术,在不修改源 ...

  9. ❤️六万字《Spring框架从入门到精通》(建议收藏)❤️

    ❤️ Spring 代码:https://github.com/Donkequan/Spring-Study ❤️ 希望各位博友三连+关注!!! 1.简介 spring理念:是现有的技术更加容易使用, ...

最新文章

  1. awk -f 分隔符 命令_Linux三剑客之awk
  2. java中两个整形相除,向上取整
  3. 【Android】3.12 兴趣点( POI)搜索功能
  4. 对“纯手工编写的第一个Hibernate程序”的改进
  5. java的imshow方法_imshow窗口是截止的
  6. hadoop SecondaryNameNode和NameNode
  7. wordpress二次开发技巧-functions.php篇
  8. [Linux运维 -- 数据库]mysql处理时间
  9. 将对话框(提示框)中的内容粘贴到记事本
  10. 16节课搞懂大数据,视频教程限时免费领取
  11. (转) Lua: 给 Redis 用户的入门指导
  12. 拓端tecdat:matlab用Logistic逻辑回归建模和马尔可夫链蒙特卡罗MCMC方法分析汽车实验数据
  13. Linux基础入门(详细版)
  14. 延时delay1s程序 c语言,汇编语言软件延时1s的实现方法
  15. IP前缀列表配置实验
  16. 微信推出“腾讯电子签”具有提醒对方还钱
  17. 两年老网站IP100 到底错哪儿了?
  18. git 和 phabricator arc 常用 命令解析
  19. 计算机硬件故障检测实验报告,计算机系统的硬件检测实验报告
  20. 【过关斩将7】面试谈薪资时,HR压价怎么办?

热门文章

  1. 合并文件夹内所有Excel文件(目前仅限于合并单层文件夹,如果文件夹下面有文件夹,暂未加入此功能,默认合并所有文件的所有Sheet)优化文件名_变更为:文件夹名字 + 合并的文件-(xls+xlsx)
  2. Redis 集群,分布式,微服务概念和区别理解
  3. java 两个经纬度 距离_Java 根据两个经纬度,得到两点距离
  4. Homebrew安装及换源
  5. MacBooster 6 for Mac v6.0.6 软件卸载系统清理 破解版下载
  6. 机器学习——代价函数
  7. KDD99数据集预处理
  8. 树莓派环境处理_树莓派开发环境配置
  9. Object.defineProperty方法(详解)
  10. PowerToys——免费、强大、高效的微软官方效率提升工具集,办公学习宝藏软件