Spring4入门之第三章AOP的XML方式

2024-03-05 08:58

本文主要是介绍Spring4入门之第三章AOP的XML方式,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Spring4入门之第三章AOP的XML方式

AOP的概述

  • 引用百度的一段详解:

在这里插入图片描述

  • 不修改程序源代码的情况下对程序进行增强。

    比如可以进行权限校验、日志记录、性能监控和事务控制等等。

  • AOP最早由AOP联盟的组织提出的,并制定了一套规范,Spring将AOP思想引入到框架当中,必须遵守AOP联盟的规范。

  • AOP面向切面编程。AOP是OOP的扩展和延伸,解决OOP开发中遇到的问题。

AOP的底层实现

JDK动态代理

  • JDK动态代理是代理模式的一种实现方式,其只能代理接口。

  • 为接口中的save方法在执行之前,进行权限校验。

  • 使用步骤:

    1、 新建一个接口

    2、 为接口创建一个实现类

    3、 创建代理类实现java.lang.reflect.InvocationHandler接口

    4、 测试

  • 首先新建一个接口UserDao.java

    public interface UserDao {public void save();public void update();public void find();public void delete();}
    
  • 然后为接口UserDao.java新建一个实现类UserDaoImpl.java

    public class UserDaoImpl implements UserDao {@Overridepublic void save() {System.out.println("保存用户。。。");}@Overridepublic void update() {System.out.println("修改用户。。。");}@Overridepublic void find() {System.out.println("查询用户。。。");}@Overridepublic void delete() {System.out.println("删除用户。。。");}
    }
    
  • 创建一个代理类JdkProxy实现java.lang.reflect.InvocationHandler接口,重写invoke方法

    /*** 使用JDK动态代理对UserDao产生代理* @author SYJ*/
    public class JdkProxy implements InvocationHandler {private UserDao userDao;public JdkProxy(UserDao userDao) {this.userDao = userDao;}/*** @Title: creatUserDaoProxy* @Description: TODO(产生UserDao的方法)* @param @param dao* @param @return* @return UserDao*/public UserDao creatUserDaoProxy() {UserDao userDaoProxy = (UserDao) Proxy.newProxyInstance(userDao.getClass().getClassLoader(),userDao.getClass().getInterfaces(), this);return userDaoProxy;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {if ("save".equals(method.getName())) {System.out.println("权限校验=========");}return method.invoke(userDao, args);}
    }
    

Cglib动态代理

  • CGLIB(Code Generation Library)是一个开源项目!是一个强大的,高性能,高质量的Code生成类库。

  • 它可以在运行期扩展Java类与实现Java接口。Hibernate用它来实现PO(Persistent Object 持久化对象)字节码的动态生成。

  • CGLIB是一个强大的高性能的代码生成包。它广泛的被许多AOP的框架使用,例如Spring AOP为他们提供方法的interception(拦截)。CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。

  • 除了CGLIB包,脚本语言例如Groovy和BeanShell,也是使用ASM来生成java的字节码。当然不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。

  • 实现一个业务类,注意,这个业务类并没有实现任何接口:

    CustomerDao

    public class CustomerDao {public void save() {System.out.println("客户保存。。。");}public void update() {System.out.println("客户修改。。。");}public void find() {System.out.println("客户查找。。。");}public void delete() {System.out.println("客户删除。。。");}
    }
    

    自定义CglibProxy类实现MethodInterceptor接口

    /*** Cglib实现动态代理* @author SYJ*/
    public class CglibProxy implements MethodInterceptor {private CustomerDao customerDao;public CglibProxy(CustomerDao customerDao) {this.customerDao = customerDao;}/*** @Title: creatCustomerDaoProxy* @Description: TODO(使用cglib产生代理的方法)* @param @return* @return CustomerDao*/public CustomerDao creatCustomerDaoProxy() {// 1.创建cglib的核心类对象Enhancer enhancer = new Enhancer();// 2.设置父类enhancer.setSuperclass(customerDao.getClass());// 3.设置回调enhancer.setCallback(this);// 4.产生代理对象CustomerDao customerDaoProxy = (CustomerDao) enhancer.create();return customerDaoProxy;}@Overridepublic Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {if ("save".equals(method.getName())) {System.out.println("Cglib的权限校验=========");}return methodProxy.invokeSuper(proxy, args);}
    }
    
  • 这个接口只有一个intercept()方法,这个方法有4个参数:

    1)proxy表示增强的对象,即实现这个接口类的一个对象;

    2)method表示要被拦截的方法;

    3)args表示要被拦截方法的参数;

    4)methodProxy表示要触发父类的方法对象;

AOP的相关术语

  • 相关术语

    • Aspect(切面): Aspect 声明类似于 Java 中的类声明,在 Aspect 中会包含着一些 Pointcut 以及相应的 Advice。
    • Joint point(连接点):表示在程序中明确定义的点,典型的包括方法调用,对类成员的访问以及异常处理程序块的执行等等,它自身还可以嵌套其它 joint point。
    • Pointcut(切点):表示一组 joint point,这些 joint point 或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的 Advice 将要发生的地方。
    • Advice(增强、通知):Advice 定义了在 Pointcut 里面定义的程序点具体要做的操作,它通过 before、after 和 around 来区别是在每个 joint point 之前、之后还是代替执行的代码。
    • Target(目标对象):织入 Advice 的目标对象.。
    • Weaving(织入):将 Aspect 和其他对象连接起来, 并创建 Adviced object 的过程
    • introduce(引入):为对象引入附加的方法或属性,从而达到修改对象结构的目的。有的AOP工具又将其称为mixin。
  • 图解AOP

在这里插入图片描述

  • AOP的Advice类型

    • 前置增强(Before advice):(可以获取切入点信息)

      在某连接点之前执行的增强,但这个增强不能阻止连接点前的执行(除非它抛出一个异常)

    • 后置返回增强(After returning advice):

      在某连接点正常完成后执行的增强:例如,一个方法没有抛出任何异常,正常返回。

    • 环绕增强(Around Advice):

      包围一个连接点的增强,如方法调用。这是最强大的一种增强类型。 环绕增强可以在方法调用前后完成自定义的行为。它也会选择是否继续执行连接点或直接返回它们自己的返回值或抛出异常来结束执行。

    • 后置异常增强(After throwing advice):

      在方法抛出异常退出时执行的增强。

    • 后置最终增强(After (finally) advice):

      当某连接点退出的时候执行的增强(不论是正常返回还是异常退出)。

  • AOP的基本运行流程(来源于网络)

在这里插入图片描述

Spring的AOP的入门(AspectJ的Xml方式)

  • 传统的AOP开发(Aop联盟)和基于AspectJ的AOP开发(AspcetJ和Spring的整合)

    • 为接口CustomerDao中的save方法在执行之前,进行权限的校验

    • CustomerDao.java

      public interface ProductDao {public void save();public void update();public void find();public String delete();
      }
      
    • ProductDaoImpl.java

      public class ProductDaoImpl implements ProductDao {@Override/** 增强save方法* */public void save() {System.out.println("保存商品");}@Overridepublic void update() {System.out.println("修改商品");}@Overridepublic void find() {System.out.println("查询商品");}@Overridepublic String delete() {System.out.println("删除商品");return "delete method";}}
      
    • 编写切面类:

      public class MyAspect {/*** @Title: chekPri* @Description: TODO(前置通知)* @param joinPoint* @return void*/public void chekPri() {System.out.println("权限校验=========");}
      }
      
    • 编写Spring的配置文件,将CustomerDao的实现类ProductDaoImpl和自己编写的切面类都交给Spring管理

      <?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.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- bean definitions here --><!-- 配置目标对象,被增强的对象 --><bean id="productDao" class="com.syj.Spring.aop.ProductDaoImpl" /><!-- 将切面交给Spring管理 --><bean  id="myAspect" class="com.syj.Spring.aop.MyAspect"/><!-- 通过AOP的配置完成对目标类产生代理 --><aop:config><!-- 定义一个切入点,通过表达式配置哪些类的哪些方法需要增强 --><aop:pointcut expression="execution(* com.syj.Spring.aop.ProductDaoImpl.save(..))"  id="pointcut1"/><!-- 配置切面,要给什么类的什么方法在什么时候执行什么方法  --><aop:aspect ref="myAspect" ><!-- 前置通知 --><aop:before  method="chekPri" pointcut-ref="pointcut1" /></aop:aspect></aop:config>
      </beans>
      
    • 运行结果,在执行save方法之前首先进行权限的校验

  • AOP开发的通知类型

    • 前置通知(获取切入点信息)

      在save方法执行前进行权限的校验,就是上面的入门代码。

      定义一个切入点,通过表达式配置哪些类的哪些方法需要增强

      		<aop:pointcut expression="execution(* com.syj.Spring.aop.ProductDaoImpl.save(..))"  id="pointcut1"/>
      

      配置切面

      <aop:aspect ref="myAspect" ><!-- 前置通知 --><aop:before  method="chekPri" pointcut-ref="pointcut1" />
      </aop:aspect>
      

      切面类的增强方法:

      /*** * @Title: chekPri* @Description: TODO(前置通知)* @param joinPoint* @return void*/public void chekPri(JoinPoint joinPoint) {System.out.println("权限校验=========" + joinPoint);//joinPoint获取切入点信息}
      
    • 后置通知(可以获取返回值)

      在delete执行之后执行日志的打印

      定义一个切入点,通过表达式配置哪些类的哪些方法需要增强

      <aop:pointcut expression="execution(* com.syj.Spring.aop.ProductDaoImpl.delete(..))"  id="pointcut2"/>
      

      配置切面

      <aop:aspect ref="myAspect" ><!-- 前置通知 --><aop:before  method="chekPri" pointcut-ref="pointcut1" /><!-- 后置打印日志 --><aop:after-returning method="writeLog"  pointcut-ref="pointcut2" returning="result" /></aop:aspect>
      

      切面类的增强方法

      	/*** @Title: writeLog* @Description: TODO(后置通知)* @param* @return void*/public void writeLog(Object result) {//会得到ProductDaoImpl中delete的返回值System.out.println("日志记录=========" + result);//配置切面的returning属性值一致}
      
    • 环绕通知()

      测试update方法的性能

      定义一个切入点,通过表达式配置哪些类的哪些方法需要增强

      <aop:pointcut expression="execution(* com.syj.Spring.aop.ProductDaoImpl.update(..))"  id="pointcut3"/>
      

      配置切面

      <aop:aspect ref="myAspect" ><!-- 前置通知 --><aop:before  method="chekPri" pointcut-ref="pointcut1" /><!-- 后置打印日志 --><aop:after-returning method="writeLog"  pointcut-ref="pointcut2" returning="result" /><!-- 对数据的修改进行性能的监控 --><aop:around method="around" pointcut-ref="pointcut3"   />	</aop:aspect>
      

      切面类的增强方法

      	/*** 性能的监控* * @throws Throwable*/public Object around(ProceedingJoinPoint joinPoint) throws Throwable {System.out.println("环绕前通知===========");Object obj = joinPoint.proceed();System.out.println("环绕后通知===========");return obj;}
      
    • 异常抛出通知(可以获取异常信息)

      在find方法执行如果抛出异常时的解决办法

      定义一个切入点,通过表达式配置哪些类的哪些方法需要增强

      <aop:pointcut expression="execution(* com.syj.Spring.aop.ProductDaoImpl.find(..))" id="pointcut4"/>
      

      配置切面

      <aop:aspect ref="myAspect" ><!-- 前置通知 --><aop:before  method="chekPri" pointcut-ref="pointcut1" /><!-- 后置打印日志 --><aop:after-returning method="writeLog"  pointcut-ref="pointcut2" returning="result" /><!-- 对数据的修改进行性能的监控 --><aop:around method="around" pointcut-ref="pointcut3"   /><!-- 异常抛出的通知 --><aop:after-throwing method="afterThrowing"  pointcut-ref="pointcut4"  throwing="ex" /></aop:aspect>
      

      切面类的增强方法

      	/*** * @Title: afterThrowing* @Description: TODO(异常抛出通知)* @param* @return void*/public void afterThrowing(Throwable ex) {System.out.println("异常抛出通知===========" + ex);//对应aop:after-throwing中throwing属性值}
      
    • 最终通知(异常中的finally)

      定义一个切入点,通过表达式配置哪些类的哪些方法需要增强

      切入点依然选择find方法,在异常中测试最终通知

      <aop:pointcut expression="execution(* com.syj.Spring.aop.ProductDaoImpl.find(..))" id="pointcut4"/>
      

      配置切面

      <aop:aspect ref="myAspect" ><!-- 前置通知 --><aop:before  method="chekPri" pointcut-ref="pointcut1" /><!-- 后置打印日志 --><aop:after-returning method="writeLog"  pointcut-ref="pointcut2" returning="result" /><!-- 对数据的修改进行性能的监控 --><aop:around method="around" pointcut-ref="pointcut3"   /><!-- 异常抛出的通知 --><aop:after-throwing method="afterThrowing"  pointcut-ref="pointcut4"  throwing="ex" /><!-- 最终通知 --><aop:after method="after" pointcut-ref="pointcut4" />
      </aop:aspect>
      

      切面类的增强方法

      	/*** * @Title: after* @Description: TODO(最终通知,最后一定会执行)* @param* @return void*/public void after() {System.out.println("最终一定会执行==========");}
      

Spring的Pointcut表达式之execution

  • 基于execution的函数完成的

  • 语法

    • [访问修饰符] 方法返回值 包名.类型.方法名(参数)
    • public void com.syj.spring.CustomerDao.save(…)
    • * *.*. *. *Dao.save(…) 包的的所有Dao结尾的save方法执行
    • * com.syj.spring.CustomerDao+.save(…) +代表当前的类和其子类
    • * com.syj.spring…*.*(…) 这个包以及子包下面的所有类的所有方法

在这里插入图片描述

这篇关于Spring4入门之第三章AOP的XML方式的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/775964

相关文章

从入门到精通MySQL联合查询

《从入门到精通MySQL联合查询》:本文主要介绍从入门到精通MySQL联合查询,本文通过实例代码给大家介绍的非常详细,需要的朋友可以参考下... 目录摘要1. 多表联合查询时mysql内部原理2. 内连接3. 外连接4. 自连接5. 子查询6. 合并查询7. 插入查询结果摘要前面我们学习了数据库设计时要满

从入门到精通C++11 <chrono> 库特性

《从入门到精通C++11<chrono>库特性》chrono库是C++11中一个非常强大和实用的库,它为时间处理提供了丰富的功能和类型安全的接口,通过本文的介绍,我们了解了chrono库的基本概念... 目录一、引言1.1 为什么需要<chrono>库1.2<chrono>库的基本概念二、时间段(Durat

java实现docker镜像上传到harbor仓库的方式

《java实现docker镜像上传到harbor仓库的方式》:本文主要介绍java实现docker镜像上传到harbor仓库的方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地... 目录1. 前 言2. 编写工具类2.1 引入依赖包2.2 使用当前服务器的docker环境推送镜像2.2

python删除xml中的w:ascii属性的步骤

《python删除xml中的w:ascii属性的步骤》使用xml.etree.ElementTree删除WordXML中w:ascii属性,需注册命名空间并定位rFonts元素,通过del操作删除属... 可以使用python的XML.etree.ElementTree模块通过以下步骤删除XML中的w:as

解析C++11 static_assert及与Boost库的关联从入门到精通

《解析C++11static_assert及与Boost库的关联从入门到精通》static_assert是C++中强大的编译时验证工具,它能够在编译阶段拦截不符合预期的类型或值,增强代码的健壮性,通... 目录一、背景知识:传统断言方法的局限性1.1 assert宏1.2 #error指令1.3 第三方解决

Springboot如何正确使用AOP问题

《Springboot如何正确使用AOP问题》:本文主要介绍Springboot如何正确使用AOP问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录​一、AOP概念二、切点表达式​execution表达式案例三、AOP通知四、springboot中使用AOP导出

springboot项目打jar制作成镜像并指定配置文件位置方式

《springboot项目打jar制作成镜像并指定配置文件位置方式》:本文主要介绍springboot项目打jar制作成镜像并指定配置文件位置方式,具有很好的参考价值,希望对大家有所帮助,如有错误... 目录一、上传jar到服务器二、编写dockerfile三、新建对应配置文件所存放的数据卷目录四、将配置文

深度解析Spring AOP @Aspect 原理、实战与最佳实践教程

《深度解析SpringAOP@Aspect原理、实战与最佳实践教程》文章系统讲解了SpringAOP核心概念、实现方式及原理,涵盖横切关注点分离、代理机制(JDK/CGLIB)、切入点类型、性能... 目录1. @ASPect 核心概念1.1 AOP 编程范式1.2 @Aspect 关键特性2. 完整代码实

XML重复查询一条Sql语句的解决方法

《XML重复查询一条Sql语句的解决方法》文章分析了XML重复查询与日志失效问题,指出因DTO缺少@Data注解导致日志无法格式化、空指针风险及参数穿透,进而引发性能灾难,解决方案为在Controll... 目录一、核心问题:从SQL重复执行到日志失效二、根因剖析:DTO断裂引发的级联故障三、解决方案:修复

从入门到精通MySQL 数据库索引(实战案例)

《从入门到精通MySQL数据库索引(实战案例)》索引是数据库的目录,提升查询速度,主要类型包括BTree、Hash、全文、空间索引,需根据场景选择,建议用于高频查询、关联字段、排序等,避免重复率高或... 目录一、索引是什么?能干嘛?核心作用:二、索引的 4 种主要类型(附通俗例子)1. BTree 索引(