热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

【Spring从入门到精通】02AOP

笔记来源:尚硅谷Spring框架视频教程(spring5源码级讲解)目录AOP1、AOP概述2、AOP底层原理2.1、JDK动态代理2.2、CGlib动态代理3、JDK动态代理实现

笔记来源:尚硅谷Spring框架视频教程(spring5源码级讲解)


目录



  • AOP

    • 1、AOP 概述

    • 2、AOP 底层原理

      • 2.1、JDK 动态代理

      • 2.2、CGlib 动态代理



    • 3、JDK 动态代理实现

    • 4、AOP 术语

    • 5、AOP 准备工作

      • 5.1、AspectJ 介绍

      • 5.2、引入 AOP 相关依赖

      • 5.3、切入点表达式



    • 6、AspectJ 注解实现

      • 6.1、Spring 配置文件

      • 6.2、创建被增强对象和增强对象

      • 6.3、添加增强类注解和切入点表达式

      • 6.4、代码测试

      • 6.5、抽取相同切入点表达式

      • 6.6、设置增强类优先级

      • 6.7、完全注解开发



    • 7、AspectJ 配置文件实现

      • 7.1、创建被增强对象和增强对象

      • 7.2、Spring 配置文件

      • 7.3、代码测试



    • 小结




AOP

1、AOP 概述



  • 定义:AOP(Aspect Oriented Programming,面向切面编程),通过预编译和运行时动态代理扩展程序功能

  • 作用:利用 AOP 可以对业务逻辑的各个部分进行隔离,降低耦合性,提高程序可重用性和开发效率

  • 场景:日志记录,性能统计,安全控制,事务处理,异常处理

  • 通俗描述:不修改源代码,在主干功能中添加新功能

使用登录功能案例说明 AOP

image-20220304205631186


2、AOP 底层原理



  • 底层原理:动态代理

    • 有接口情况JDK动态代理

    • 无接口情况CGLib动态代理



如果学习过设计模式,应该对上述两种代理方式非常了解了。没有学习过也没关系,我们接着往下看


2.1、JDK 动态代理

public interface UserDao {
void login();
}
public class UserDaoImpl implements UserDao {
@Override
public void login(){
//登录实现过程
}
}

有接口情况:创建 UserDao 接口实现类代理对象


2.2、CGlib 动态代理

public class User {
public void add(){
//...
}
}
// 原始方法:通过子类继承,重写User类方法
public class Person extends User {
@Override
public void add(){
super.add();
//增强代码逻辑
}
}

无接口情况:创建 User 类子类代理对象



由于 Spring5 中对上述代理已经做了很好的封装,我们只需要通过最简单的方式进行配置即可

但仍然需要我们对原理有一定的认识,只有做到“知其然,知其所以然”,才能真正“以不变应万变”


3、JDK 动态代理实现

实现方式:使用Proxy中的方法创建代理对象

image-20220304212508671

具体方法newProxyInstance()

方法参数



  • ClassLoader loader:类加载器

  • Class[] interfaces:增强方法所在类实现的接口数组

  • InvocationHandler h:实现InvocationHandler接口,创建代理对象,编写增强方法

image-20220304212440321

常言道:“Talking is cheap, show me the code"。话不多说,下面上代码~



  • 1)创建 UserDao 接口和对应实现类

public interface UserDao {
int add(int a, int b);
String update(String id);
}
public class UserDaoImpl implements UserDao {
@Override
public int add(int a, int b) {
return a + b;
}
@Override
public String update(String id) {
return id;
}
}


  • 2)创建 UserDao 代理对象

public class UserDaoProxy {
private UserDao target;
public UserDaoProxy(UserDao target) {
this.target = target;
}
public UserDao newProxyInstance() {
Class targetClass = target.getClass();
ClassLoader classLoader = targetClass.getClassLoader();
Class[] interfaces = targetClass.getInterfaces();
return (UserDao) Proxy.newProxyInstance(classLoader, interfaces, new UserDaoInvocationHandler());
}
class UserDaoInvocationHandler implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 被代理对象方法前置逻辑
System.out.print("method=" + method.getName() + ", args=" + Arrays.toString(args));
// 被代理对象方法
Object result = method.invoke(target, args);
// 被代理对象方法后置逻辑
System.out.println(", result=" + result);
return result;
}
}
}


  • 3)测试

UserDao target = new UserDaoImpl();
UserDaoProxy userDaoProxy = new UserDaoProxy(target);
UserDao userDao = userDaoProxy.newProxyInstance();
userDao.add(1, 2);
userDao.update("UUID1");
// method=add, args=[1, 2], result=3
// method=update, args=[UUID1], result=UUID1

4、AOP 术语



  • 连接点:类中可以被增强的方法,称为连接点



  • 切入点:类中实际被增强的方法,称为切入点



  • 通知(增强):实际增强的逻辑部分,称为通知

    通知分为五种类型:



    • 前置通知:方法执行之前的处理

    • 后置通知:方法执行之后的处理

    • 环绕通知:方法执行前后的处理

    • 异常通知:方法抛出异常的处理

    • 最终通知:方法执行最终的处理(相当于try-catch-finally中的finally



  • 切面:是一个动作,即把通知应用到切入点的过程




5、AOP 准备工作


5.1、AspectJ 介绍

Spring 一般都是基于AspectJ实现 AOP 操作的



  • AspectJ不是 Spring 的一部分,而是一个独立的 AOP 框架

  • 一般会把AspectJ和 Spring 搭配使用,进行 AOP 操作,因为这样更加方便

基于 AspectJ 进行 AOP 操作的两种方式:



  • 基于 XML 配置文件方式实现

  • 基于注解方式实现(推荐使用)


5.2、引入 AOP 相关依赖

image-20220305130205073


org.aspectj
aspectjrt
1.9.8


org.aspectj
aspectjweaver
1.9.8


5.3、切入点表达式

切入点表达式的作用:知道对哪个类的哪个方法进行增强

语法结构:execution([权限修饰符][返回类型][类全路径][方法名]([参数列表]))

举例

⭐ 举例1:对com.vectorx.dao.BookDao中的add()方法进行增强

execution(* com.vectorx.dao.BookDao.add(..))

⭐ 举例2:对com.vectorx.dao.BookDao中的所有方法进行增强

execution(* com.vectorx.dao.BookDao.*(..))

⭐ 举例3:对com.vectorx.dao包中所有类的所有方法进行增强

execution(* com.vectorx.dao.*.*(..))

6、AspectJ 注解实现


6.1、Spring 配置文件



  • 1)引入contextaop名称空间

  • 2)配置组件扫描基础包

  • 3)开启AspectJ生成代理对象


xmlns:cOntext="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">






6.2、创建被增强对象和增强对象



  • 1)创建 User 对象,并添加@Component注解

  • 2)创建 UserProxy 对象,并添加@Component注解

@Component
public class User {
public void add() {
System.out.println("add...");
}
}
@Component
public class UserProxy {
/**
* 前置通知
*/
public void before() {
System.out.println("before...");
}
/**
* 后置通知
*/
public void afterReturning() {
System.out.println("afterReturning...");
}
/**
* 最终通知
*/
public void after() {
System.out.println("after...");
}
/**
* 异常通知
*/
public void afterThrowing() {
System.out.println("afterThrowing...");
}
/**
* 环绕通知
*/
public void around() {
System.out.println("around...");
}
}

6.3、添加增强类注解和切入点表达式


@Component
@Aspect
public class UserProxy {
/**
* 前置通知
*/
@Before(value = "execution(* com.vectorx.spring5.s13_aspectj_annatation.User.add(..))")
public void before() {
System.out.println("before...");
}
/**
* 后置通知
*/
@AfterReturning(value = "execution(* com.vectorx.spring5.s13_aspectj_annatation.User.add(..))")
public void afterReturning() {
System.out.println("afterReturning...");
}
/**
* 最终通知
*/
@After(value = "execution(* com.vectorx.spring5.s13_aspectj_annatation.User.add(..))")
public void after() {
System.out.println("after...");
}
/**
* 异常通知
*/
@AfterThrowing(value = "execution(* com.vectorx.spring5.s13_aspectj_annatation.User.add(..))")
public void afterThrowing() {
System.out.println("afterThrowing...");
}
/**
* 环绕通知
*/
@Around(value = "execution(* com.vectorx.spring5.s13_aspectj_annatation.User.add(..))")
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("around before...");
// 执行被增强的方法
joinPoint.proceed();
System.out.println("around after...");
}
}

6.4、代码测试

ApplicationContext cOntext= new ClassPathXmlApplicationContext("bean11.xml");
User user = context.getBean("user", User.class);
user.add();

结果

around before...
before...
add...
afterReturning...
after...
around after...

为了演示异常通知,需要修改下被增强对象中的方法,模拟一个异常

@Component
public class User {
public void add() {
System.out.println("add...");
// 模拟一个异常
int i = 2 / 0;
}
}

运行结果

around before...
before...
add...
afterThrowing...
after...

对比正常情况下,发现少了afterReturning即后置异常和around after即环绕增强的后置处理


6.5、抽取相同切入点表达式

通过上述的例子,应该对AspectJ注解实现有了一定的了解

同时我们发现切入点表达式都是完全一样的,可以对这些相同的切入点表达式进行抽取,以达到重用切入点表达式定义的目的



  • 1)首先想到的应该是定义成员变量

private final String execution = "execution(* com.vectorx.spring5.s13_aspectj_annatation.User.add(..))";
@Before(value = execution)
public void before() {
System.out.println("before...");
}


  • 2)AspectJ中提供了Pointcut注解(推荐)

@Pointcut(value = "execution(* com.vectorx.spring5.s13_aspectj_annatation.User.add(..))")
private void pointcut(){}
@Before(value = "pointcut()")
public void before() {
System.out.println("before...");
}

6.6、设置增强类优先级

如果有多个增强类对类中同一个方法进行增强,可以设置增强类的优先级,来决定哪个增强类先执行,哪个增强类后执行

使用@Order注解设置增强类的优先级,其中指定优先级数字,注解格式:@Order(数字类型值)



  • 数字类型值越小,优先级越高

  • 数字类型值越大,优先级越低

⭐最佳实践

@Component
@Aspect
@Order(1)
public class PersonProxy {
//...
}
@Component
@Aspect
@Order(3)
public class UserProxy {
//...
}

测试结果

person around before...
person before...
user around before...
user before...
add...
user afterReturning...
user after...
user around after...
person afterReturning...
person after...
person around after...

我们发现:



  • PersonProxy 中的前置通知先于 UserProxy 中的前置通知执行

  • PersonProxy 中的后置通知晚于 UserProxy 中的后置通知执行


6.7、完全注解开发

如果要用完全注解的方式进行开发,可以使用注解类代替 Spring 配置文件

@Configuration
@ComponentScan(value = "com.vectorx.spring5.s13_aspectj_annatation")
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class AopConfig {
}

其中:



  • 注解@ComponentScan(value = "com.vectorx.spring5.s13_aspectj_annatation")代替了进行组件扫描的配置

  • 注解@EnableAspectJAutoProxy(proxyTargetClass = true)代替了开启AspectJ生成代理对象

对应关系



















注解方式配置文件方式
@ComponentScan
@EnableAspectJAutoProxy

7、AspectJ 配置文件实现


7.1、创建被增强对象和增强对象

public class Book {
public void buy() {
System.out.println("buy...");
}
}
public class BookProxy {
public void before() {
System.out.println("before...");
}
public void afterReturning() {
System.out.println("afterReturning...");
}
public void after() {
System.out.println("after...");
}
public void afterThrowing() {
System.out.println("afterThrowing...");
}
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("around before...");
joinPoint.proceed();
System.out.println("around after...");
}
}

7.2、Spring 配置文件



  • 1)引入aop名称空间

  • 2)配置被增强对象和增强对象创建

  • 3)配置aop增强


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 https://www.springframework.org/schema/aop/spring-aop.xsd">






















其中,配置文件的标签与注解的对应关系如下表







































配置文件方式注解方式
@Pointcut
@Aspect
@Before
@AfterReturning
@After
@AfterThrowing
@Around

7.3、代码测试

ApplicationContext cOntext= new ClassPathXmlApplicationContext("bean12.xml");
Book book = context.getBean("book", Book.class);
book.buy();

测试结果

before...
around before...
buy...
around after...
after...
afterReturning...

小结

本节重点



  1. AOP 概述

  2. AOP 底层原理

  3. AOP 术语

  4. 切入点表达式

  5. AspectJ 实现

  6. 完全注解开发

以下总结仅供参考

02-AOP


原文链接:https://www.cnblogs.com/vectorx/p/15968216.html



推荐阅读
  • 如何搭建服务器环境php(2023年最新解答)
    导读:本篇文章编程笔记来给大家介绍有关如何搭建服务器环境php的相关内容,希望对大家有所帮助,一起来看看吧。本文目录一览:1、怎么搭建p ... [详细]
  • 本文介绍了Python高级网络编程及TCP/IP协议簇的OSI七层模型。首先简单介绍了七层模型的各层及其封装解封装过程。然后讨论了程序开发中涉及到的网络通信内容,主要包括TCP协议、UDP协议和IPV4协议。最后还介绍了socket编程、聊天socket实现、远程执行命令、上传文件、socketserver及其源码分析等相关内容。 ... [详细]
  • Java序列化对象传给PHP的方法及原理解析
    本文介绍了Java序列化对象传给PHP的方法及原理,包括Java对象传递的方式、序列化的方式、PHP中的序列化用法介绍、Java是否能反序列化PHP的数据、Java序列化的原理以及解决Java序列化中的问题。同时还解释了序列化的概念和作用,以及代码执行序列化所需要的权限。最后指出,序列化会将对象实例的所有字段都进行序列化,使得数据能够被表示为实例的序列化数据,但只有能够解释该格式的代码才能够确定数据的内容。 ... [详细]
  • 初识java关于JDK、JRE、JVM 了解一下 ... [详细]
  • 初学反射基本原理
    反射:框架设计的灵魂*框架:半成品软件。可以在框架的基础上进行软件开发,简化编码*反射:将类的各个组成部分封装为其他对象 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • 本文介绍了使用Java实现大数乘法的分治算法,包括输入数据的处理、普通大数乘法的结果和Karatsuba大数乘法的结果。通过改变long类型可以适应不同范围的大数乘法计算。 ... [详细]
  • Android中高级面试必知必会,积累总结
    本文介绍了Android中高级面试的必知必会内容,并总结了相关经验。文章指出,如今的Android市场对开发人员的要求更高,需要更专业的人才。同时,文章还给出了针对Android岗位的职责和要求,并提供了简历突出的建议。 ... [详细]
  • 给定一个二维平面上的一些点,通过计算曼哈顿距离,求连接所有点的最小总费用。只有任意两点之间有且仅有一条简单路径时,才认为所有点都已连接。给出了几个示例并给出了对应的输出。 ... [详细]
  • 如何用UE4制作2D游戏文档——计算篇
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了如何用UE4制作2D游戏文档——计算篇相关的知识,希望对你有一定的参考价值。 ... [详细]
  • Voicewo在线语音识别转换jQuery插件的特点和示例
    本文介绍了一款名为Voicewo的在线语音识别转换jQuery插件,该插件具有快速、架构、风格、扩展和兼容等特点,适合在互联网应用中使用。同时还提供了一个快速示例供开发人员参考。 ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • 本文介绍了在Windows环境下如何配置php+apache环境,包括下载php7和apache2.4、安装vc2015运行时环境、启动php7和apache2.4等步骤。希望对需要搭建php7环境的读者有一定的参考价值。摘要长度为169字。 ... [详细]
  • 华为和阿里VPN
    阿里云创建VPN1、 创建VPN网关2、 填写名称,选择对应的VPC,不指定交换机,选择带宽3、购买的是IPSEC-VPN不开启SSLVPN,点击立即购买 4、 创建客户网关  ... [详细]
  • 问题描述:域名已经备案,我全部都有,也在后台配置了,但是手机预览,还是请求失败,PC端是可以请求 ... [详细]
author-avatar
黑小羊Mark
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有