当前位置: 首页 > news >正文

第8章 Spring AOP

目录/Contents

  • 第6章 初识Spring框架
    • 学习目标
      • 了解Spring AOP的概念及其术语
      • 熟悉Spring AOP的JDK动态代理
      • 熟悉Spring AOP的CGLib动态代理
      • 掌握基于XML的AOP实现
      • 掌握基于注解的AOP实现
    • 学习内容
      • 1 Spring AOP介绍
        • 1.1 Spring AOP概述
          • 1.1.1 AOP概述
          • 1.1.2 未使用AOP的面向切面编程案例
          • 1.1.3 AOP面向切面编程的优势
        • 1.2 Spring AOP术语
          • 1.2.1 AOP术语
          • 1.2.2 切面(Aspect)
          • 1.2.3 连接点(Joinpoint)
          • 1.2.4 切入点(Pointcut)
          • 1.2.5 通知/增强处理(Advice)
          • 1.2.6 目标对象(Target)
          • 1.2.7 织入(Weaving)
          • 1.2.8 代理(Proxy)
          • 1.2.9 引介(Introduction)
      • 2 Spring AOP的实现机制
        • 2.1 JDK动态代理
          • 2.1.1 Spring AOP的默认代理方式
        • 2.2 一个案例演示Spring中JDK动态代理的实现过程
          • STEP 01
          • STEP 02
          • STEP 03
          • STEP 04
          • STEP 05
          • newProxyInstance()方法的3个参数
          • STEP 06
          • STEP 07
        • 2.3 CGLib代理
          • 2.3.1 JDK与CGLib动态代理的比较
        • 2.4 一个案例演示CGLib动态代理的实现过程
          • STEP 01
          • STEP 02
          • STEP 03
          • STEP 04
      • 3 基于XML的AOP实现
        • 3.1 使用AOP代理对象的好处
        • 3.2 配置Spring AOP的XML元素
        • 3.3 配置切面
        • 3.4 \<aop:aspect>元素的id属性和ref属性的描述
        • 3.5 配置切入点
        • 3.6 \<aop:pointcut>元素的id属性和expression属性描述
        • 3.7 Spring AOP切入点表达式的基本格式
        • 3.8 execution表达式各部分参数说明
        • 3.9 配置通知
        • 3.10 \<aop:aspect>元素的常用属性
        • 3.11 案例演示如何在Spring中使用XML实现Spring AOP
          • STEP 01
          • STEP 02
          • STEP 03
          • STEP 04
          • STEP 05
          • STEP 06
          • STEP 07
      • 4 基于注解的AOP实现
        • 4.1 Spring AOP的注解
        • 4.2 案例演示基于注解的AOP的实现
          • STEP 01
          • STEP 02
          • STEP 03
          • STEP 04

第6章 初识Spring框架

学习目标

了解Spring AOP的概念及其术语

熟悉Spring AOP的JDK动态代理

熟悉Spring AOP的CGLib动态代理

掌握基于XML的AOP实现

掌握基于注解的AOP实现

学习内容

1 Spring AOP介绍

1.1 Spring AOP概述

1.1.1 AOP概述

AOP的全称是Aspect Oriented Programming,即面向切面编程。和OOP不同,AOP主张将程序中相同的业务逻辑进行横向隔离,并将重复的业务逻辑抽取到一个独立的模块中,以达到提高程序可重用性和开发效率的目的。
在传统的业务处理代码中,通常都会进行事务处理、日志记录等操作。虽然使用OOP可以通过组合或者继承的方式来达到代码的重用,但如果要实现某个功能(如日志记录),同样的代码仍然会分散到各个方法中。

1.1.2 未使用AOP的面向切面编程案例

例如,订单系统中有添加订单信息、更新订单信息和删除订单信息3个方法,
这3个方法中都包含事务管理业务代码,订单系统的逻辑如图所示。
在这里插入图片描述

1.1.3 AOP面向切面编程的优势

由订单系统可知,添加订单信息、修改订单信息、删除订单信息的方法体中都包含事务管理的业务逻辑,这就带来了一定数量的重复代码并使程序的维护成本增加。基于AOP的面向切面编程,可以为此类问题提供解决方案,AOP可以将事务管理的业务逻辑从这三个方法体中抽取到一个可重用的模块,进而降低横向业务逻辑之间的耦合,减少重复代码。AOP的使用,使开发人员在编写业务逻辑时可以专心于核心业务,而不用过多地关注其他业务逻辑的实现,不但提高了开发效率,而且增强了代码的可维护性。

1.2 Spring AOP术语

1.2.1 AOP术语

AOP并不是一个新的概念,AOP中涉及很多术语,如切面、连接点、切入点、通知/增强处理、目标对象、织入、代理和引介等,下面针对AOP的常用术语进行简单介绍。

1.2.2 切面(Aspect)

切面是指关注点形成的类(关注点是指类中重复的代码),通常是指封装的、用于横向插入系统的功能类(如事务管理、日志记录等)。在实际开发中,该类被Spring容器识别为切面,需要在配置文件中通过元素指定。

1.2.3 连接点(Joinpoint)

连接点是程序执行过程中某个特定的节点,例如,某方法调用时或处理异常时。在Spring AOP中,一个连接点通常是一个方法的执行。

1.2.4 切入点(Pointcut)

当某个连接点满足预先指定的条件时,AOP就能够定位到这个连接点,在连接点处插入切面,该连接点也就变成了切入点。

1.2.5 通知/增强处理(Advice)

通知/增强处理就是插入的切面程序代码。可以将通知/增强处理理解为切面中的方法,它是切面的具体实现。

1.2.6 目标对象(Target)

目标对象是指被插入切面的方法,即包含主业务逻辑的类对象。或者说是被一个或者多个切面所通知的对象。

1.2.7 织入(Weaving)

将切面代码插入到目标对象上,从而生成代理对象的过程。织入可以在编译时,类加载时和运行时完成。在编译时进行织入就是静态代理,而在运行时进行织入则是动态代理。

1.2.8 代理(Proxy)

将通知应用到目标对象之后,程序动态创建的通知对象,就称为代理。代理类既可能是和原类具有相同接口的类,也可能是原类的子类,可以采用调用原类相同的方式调用代理类。

1.2.9 引介(Introduction)

引介是一种特殊的通知,它可为目标对象添加一些属性和方法。这样,即使一个业务类原本没有实现某一个接口,通过AOP的引介功能,也可以动态地为该业务类添加接口的实现逻辑,让业务类成为这个接口的实现类。

2 Spring AOP的实现机制

2.1 JDK动态代理

2.1.1 Spring AOP的默认代理方式

默认情况下,Spring AOP使用JDK动态代理,JDK动态代理是通过java.lang.reflect.Proxy 类实现的,可以调用Proxy类的newProxyInstance()方法创建代理对象。JDK动态代理可以实现无侵入式的代码扩展,并且可以在不修改源代码的情况下,增强某些方法。

2.2 一个案例演示Spring中JDK动态代理的实现过程

STEP 01

在IDEA中创建一个名为chapter08的Maven项目,然后在项目的pom.xml文件中加载需使用到的Spring基础包和Spring的依赖包。

STEP 02

创建接口UserDao,在UserDao接口中编写添加和删除的方法。

package com.itheima.demo01;
public interface UserDao {
    public void addUser();
    public void deleteUser();
}
STEP 03

创建UserDao接口的实现类UserDaoImpl,分别实现接口中的方法。

package com.itheima.demo01;
public class UserDaoImpl implements UserDao {
	public void addUser() {
		System.out.println("添加用户");	}
	public void deleteUser() {
		System.out.println("删除用户");	}
}
STEP 04

创建切面类MyAspect,在该类中定义一个模拟权限检查的方法和一个模拟日志记录的方法,这两个方法就是切面中的通知。

package com.itheima.demo01;
// 切面类:存在多个通知Advice(增强的方法)
public class MyAspect {
    public void check_Permissions(){
        System.out.println("模拟检查权限...");		}
    public void log(){
        System.out.println("模拟记录日志...");		}
}
STEP 05

创建代理类MyProxy,该类需要实现InvocationHandler接口设置代理类的调用处理程序。在代理类中,通过newProxyInstance()生成代理方法。

public class MyProxy implements InvocationHandler {
    private UserDao userDao;
    public  Object createProxy(UserDao userDao) {
        this.userDao = userDao;
        ClassLoader classLoader = MyProxy.class.getClassLoader(); // 1.类加载器
        Class[] classes = userDao.getClass().getInterfaces(); // 2.被代理对象实现的所有接口
        return  Proxy.newProxyInstance(classLoader,classes,this); // 3.返回代理对象
    }
    // 所有动态代理类的方法调用,都会交由invoke()方法去处理。篇幅问题这里省略invoke()方法
newProxyInstance()方法的3个参数

第1个参数是classLoader,表示当前类的类加载器。
第2个参数是classes,表示被代理对象实现的所有接口。
第3个参数是this,表示代理类JdkProxy本身。

STEP 06

创建测试类JDKTest。在该类中的main()方法中创建代理对象jdkProxy和目标对象userDao,然后从代理对象jdkProxy中获得对目标对象userDao增强后的对象userDao1,最后调用userDao1对象中的添加和删除方法。

public class JDKTest {
    public static void main(String[] args) {
        MyProxy jdkProxy = new MyProxy();// 创建代理对象
        UserDao userDao = new UserDaoImpl();// 创建目标对象
        // 从代理对象中获取增强后的目标对象
        UserDao userDao1 = (UserDao) jdkProxy.createProxy(userDao);
        // 执行方法
        userDao1.addUser();
        userDao1.deleteUser();
}}
STEP 07

在IDEA中启动JDKTest类,控制台会输出结果。
在这里插入图片描述

2.3 CGLib代理

2.3.1 JDK与CGLib动态代理的比较

JDK动态代理存在缺陷,它只能为接口创建代理对象,当需要为类创建代理对象时,就需要使用CGLib(Code Generation Library)动态代理,CGLib动态代理不要求目标类实现接口,它采用底层的字节码技术,通过继承的方式动态创建代理对象。Spring的核心包已经集成了CGLib所需要的包,所以开发中不需要另外导入JAR包。

2.4 一个案例演示CGLib动态代理的实现过程

STEP 01

创建目标类UserDao,在该类中编写添加用户和删除用户的方法。

package com.itheima.demo02;
public class UserDao {
    public void addUser(){
        System.out.println("添加用户");	}
    public void deleteUser(){
        System.out.println("删除用户");	}
}
STEP 02

创建代理类CglibProxy,该代理类需要实现MethodInterceptor接口用于设置代理类的调用处理程序,并实现接口中的intercept()方法。

public class CglibProxy implements MethodInterceptor {
    public  Object createProxy(Object target) {// 代理方法
        Enhancer enhancer = new Enhancer();// 创建一个动态类对象
        enhancer.setSuperclass(target.getClass());// 确定需要增强的类,设置其父类
        enhancer.setCallback(this);// 添加回调函数
        return enhancer.create();// 返回创建的代理类
    }
    // intercept()方法省略
}
STEP 03

创建测试类CglibTest,在main()方法中首先创建代理对象cglibProxy和目标对象userDao,然后从代理对象cglibProxy中获得增强后的目标对象userDao1,最后调用userDao1对象的添加和删除方法。

public class CglibTest {
      public static void main(String[] args) {
            CglibProxy cglibProxy = new CglibProxy(); // 创建代理对象
            UserDao userDao = new UserDao(); // 创建目标对象
            // 获取增强后的目标对象
            UserDao userDao1 = (UserDao)cglibProxy.createProxy(userDao);
            // 执行方法
            userDao1.addUser();
            userDao1.deleteUser();
}}
STEP 04

在IDEA中启动CglibTest类,控制台会输出结果。
在这里插入图片描述

3 基于XML的AOP实现

3.1 使用AOP代理对象的好处

因为Spring AOP中的代理对象由IoC容器自动生成,所以开发者无须过多关注代理对象生成的过程,只需选择连接点、创建切面、定义切点并在XML文件中添加配置信息即可。Spring提供了一系列配置Spring AOP的XML元素。

3.2 配置Spring AOP的XML元素

在这里插入图片描述

3.3 配置切面

在Spring的配置文件中,配置切面使用的是<aop:aspect>元素,该元素会将一个已定义好的Spring
Bean转换成切面Bean,因此,在使用<aop:aspect>元素之前,要在配置文件中先定义一个普通的Spring Bean。SpringBean定义完成后,通过<aop:aspect>元素的ref属性即可引用该Bean。配置<aop:aspect>元素时,通常会指定id和ref两个属性。

3.4 <aop:aspect>元素的id属性和ref属性的描述

在这里插入图片描述

3.5 配置切入点

在Spring的配置文件中,切入点是通过<aop:pointcut>元素来定义的。当<aop:pointcut>元素作为<aop:config>元素的子元素定义时,表示该切入点是全局的,它可被多个切面共享;当<aop:pointcut>元素作为<aop:aspect>元素的子元素时,表示该切入点只对当前切面有效。定义<aop:pointcut>元素时,通常会指定id、expression属性。

3.6 <aop:pointcut>元素的id属性和expression属性描述

在这里插入图片描述

3.7 Spring AOP切入点表达式的基本格式

execution(modifiers-pattern?ret-type-pattern declaring-type-pattern?
name-pattern(param-pattern) throws-pattern?)

3.8 execution表达式各部分参数说明

modifiers-pattern:表示定义的目标方法的访问修饰符,如public、private等。
ret-type-pattern:表示定义的目标方法的返回值类型,如void、String等。
declaring-type-pattern:表示定义的目标方法的类路径,如com.itheima.jdk.UserDaoImpl。
name-pattern:表示具体需要被代理的目标方法,如add()方法。
param-pattern:表示需要被代理的目标方法包含的参数,本章示例中目标方法参数都为空。
throws-pattern:表示需要被代理的目标方法抛出的异常类型。

3.9 配置通知

在Spring的配置文件中,使用aop:aspect元素配置了5种常用通知,分别为前置通知、后置通知、环绕通知、返回通知和异常通知。

3.10 <aop:aspect>元素的常用属性

在这里插入图片描述

3.11 案例演示如何在Spring中使用XML实现Spring AOP

STEP 01

在chapter08项目的pom.xml文件中导入AspectJ框架的相关JAR包。

<!-- aspectjrt包的依赖 -->
<dependency>
     <groupId>org.aspectj</groupId>
     <artifactId>aspectjrt</artifactId>
     <version>1.9.1</version>	
</dependency>
<!-- aspectjweaver包的依赖 -->
<dependency>
     <groupId>org.aspectj</groupId>
     <artifactId>aspectjweaver</artifactId>
     <version>1.9.6</version>	
</dependency>
STEP 02

创建接口UserDao,并在该接口中编写添加、删除、修改和查询的方法。

package com.itheima.demo03;
public interface UserDao {
    public void insert();
    public void delete();
    public void update();
    public void select();
}
STEP 03

创建UserDao接口的实现类UserDaoImpl,实现UserDao接口中的方法。

public class UserDaoImpl implements UserDao{
    public void insert() {
        System.out.println("添加用户信息"); }
    public void delete() {
        System.out.println("删除用户信息"); }
    public void update() {
        System.out.println("更新用户信息"); }
    public void select() {
        System.out.println("查询用户信息"); }
}
STEP 04

创建XmlAdvice类,用于定义通知。

public class XmlAdvice {
    // 前置通知
    public void before(JoinPoint joinPoint){
        System.out.print("这是前置通知!");
        System.out.print("目标类是:"+joinPoint.getTarget());
        System.out.println(",被织入增强处理的目标方法为:"+
                                joinPoint.getSignature().getName());
    }
    // 因为篇幅问题,其他通知省略:返回通知、环绕通知、异常通知、后置通知
}
STEP 05

创建applicationContext.xml文件,在该文件中引入AOP命名空间,使用元素添加Spring AOP的配置信息。

<-- 注册bean省略,下面内容为配置Spring AOP-->
<aop:config>
     <aop:pointcut id="pointcut" expression="execution(* 
           com.itheima.demo03.UserDaoImpl.*(..))"/><!-- 指定切点 -->
     <aop:aspect ref ="xmlAdvice"><!-- 指定切面 -->
         <aop:before method="before" pointcut-ref="pointcut"/><!-- 指定前置通知 -->
         <aop:after-returning method="afterReturning" pointcut-ref="pointcut"/>
         <aop:around method="around" pointcut-ref="pointcut"/>-- 指定环绕方式 -->
         <aop:after-throwing method="afterException" pointcut-ref="pointcut"/>
         <aop:after method="after" pointcut-ref="pointcut"/><!-- 指定后置通知 -->
    </aop:aspect>
</aop:config> 
STEP 06

创建测试类TestXml,测试基于XML的AOP实现。

public class TestXml{
    public static void main(String[] args){
        ApplicationContext context=new  ClassPathXmlApplicationContext("applicationContext.xml");
        UserDao userDao=context.getBean("userDao",UserDao.class);
        userDao.delete();	 
        userDao.insert();	
        userDao.select();	
        userDao.update();	
    }
} 
STEP 07

在IDEA中启动TestXml类,控制台会输出结果。

在这里插入图片描述

4 基于注解的AOP实现

4.1 Spring AOP的注解

在这里插入图片描述

4.2 案例演示基于注解的AOP的实现

STEP 01

创建AnnoAdvice类,用于定义通知。

@Aspect
public class AnnoAdvice {/*
    @Pointcut("execution( * com.itheima.demo03.UserDaoImpl.*(..))")
    @Before("poincut()")
    @AfterReturning("poincut()")
    @Around("poincut()")
    @AfterThrowing("poincut()")
    @After(“poincut()")
     使用以上注解分别定义切点、前置通知、返回通知、环绕通知、异常通知、后置通知*/
}
STEP 02

创建applicationContext-Anno.xml文件,在该文件中引入AOP命名空间,使用元素添加Spring
AOP的配置信息。

<!-- 注册Bean -->
<bean name="userDao" class="com.itheima.demo03.UserDaoImpl"/>
<bean name="AnnoAdvice" class="com.itheima.demo04.AnnoAdvice"/>
<!-- 开启@aspectj的自动代理支持 -->
<aop:aspectj-autoproxy/>
STEP 03

创建测试类TestAnnotation,用于测试基于注解的AOP实现。

public class TestAnnotation {
    public static void main(String[] args){
        ApplicationContext context = new 
        ClassPathXmlApplicationContext("applicationContext-Anno.xml");
        UserDao userDao = context.getBean("userDao",UserDao.class);
        userDao.delete();	
        userDao.insert();	
        userDao.select();	 
        userDao.update();
}}
STEP 04

在IDEA中启动TestAnnotation类,控制台会输出结果。

在这里插入图片描述

相关文章:

  • 操作系统 | 【一 概述】强化阶段 —— 应用题总结
  • 深度学习(PyTorch)——python中的两大法宝(dir与help)
  • 记一次vue^2.6.5-router^3.0.6的keep-alive事故
  • vi vim 快速跳到文件末尾 在最后一行下方新增一行 (光标换行,文字不换行)
  • 【我不熟悉的css】03. 使用px、em、rem
  • 1.直流无刷电机BLDC转速计算推论
  • 猿创征文|小而巧的API文档生成工具之smart-doc
  • PyTorch错误定位系列之DDP训练中 double free or corruption (out)
  • Go template详解(中)- 变量使用、if语句、迭代(数组、切片、map)、内置函数(比较、逻辑判断、打印、索引、函数调用)
  • JavaScript(三):理解异步
  • JVM阶段(3)-OutOfMemoryError异常
  • 企业运维容器之 docker 网络
  • 【QML】 如何导入QML文档目录
  • 【前端】命令行基础,linux常用命令
  • 【ZYNQ-嵌入式】zynq学习笔记(二)—— GIPO的硬件配置和软件配置
  • 【跃迁之路】【585天】程序员高效学习方法论探索系列(实验阶段342-2018.09.13)...
  • 2018以太坊智能合约编程语言solidity的最佳IDEs
  • ES2017异步函数现已正式可用
  • JAVA SE 6 GC调优笔记
  • java 多线程基础, 我觉得还是有必要看看的
  • java取消线程实例
  • markdown编辑器简评
  • python docx文档转html页面
  • python_bomb----数据类型总结
  • Python进阶细节
  • Python学习之路13-记分
  • socket.io+express实现聊天室的思考(三)
  • vue-cli在webpack的配置文件探究
  • 案例分享〡三拾众筹持续交付开发流程支撑创新业务
  • 从输入URL到页面加载发生了什么
  • 分享几个不错的工具
  • 理解在java “”i=i++;”所发生的事情
  • 如何将自己的网站分享到QQ空间,微信,微博等等
  • 小李飞刀:SQL题目刷起来!
  • 验证码识别技术——15分钟带你突破各种复杂不定长验证码
  • 一个完整Java Web项目背后的密码
  • 一些css基础学习笔记
  • 云栖大讲堂Java基础入门(三)- 阿里巴巴Java开发手册介绍
  • 2017年360最后一道编程题
  • # Swust 12th acm 邀请赛# [ K ] 三角形判定 [题解]
  • %3cscript放入php,跟bWAPP学WEB安全(PHP代码)--XSS跨站脚本攻击
  • (C++17) std算法之执行策略 execution
  • (delphi11最新学习资料) Object Pascal 学习笔记---第8章第2节(共同的基类)
  • (WSI分类)WSI分类文献小综述 2024
  • (定时器/计数器)中断系统(详解与使用)
  • (七)c52学习之旅-中断
  • (学习日记)2024.01.19
  • (一)Mocha源码阅读: 项目结构及命令行启动
  • (转)Oracle存储过程编写经验和优化措施
  • .equal()和==的区别 怎样判断字符串为空问题: Illegal invoke-super to void nio.file.AccessDeniedException
  • .net core 6 集成 elasticsearch 并 使用分词器
  • .net MVC中使用angularJs刷新页面数据列表
  • .net Stream篇(六)
  • .NET中GET与SET的用法
  • .skip() 和 .only() 的使用