AOP

作者: 赵恩栋 | 来源:发表于2022-02-27 20:40 被阅读0次

    一、AOP介绍

    AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

    理解

    u=2029981592,3062824930&fm=26&gp=0

    1. 原理探究

    AOP是Aspect Oriented Programming,即面向切面编程。

    那什么是AOP?

    我们先回顾一下OOP:Object Oriented Programming,OOP作为面向对象编程的模式,获得了巨大的成功,OOP的主要功能是数据封装、继承和多态。

    而AOP是一种新的编程方式,它和OOP不同,OOP把系统看作多个对象的交互,AOP把系统分解为不同的关注点,或者称之为切面(Aspect)。

    要理解AOP的概念,我们先用OOP举例,比如一个业务组件BookService,它有几个业务方法:

    • createBook:添加新的Book;

    • updateBook:修改Book;

    • deleteBook:删除Book。

    对每个业务方法,例如,createBook(),除了业务逻辑,还需要安全检查、日志记录和事务处理,它的代码像这样:

    <pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="java" cid="n30" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">public class BookService {
    public void createBook(Book book) {
    securityCheck();
    Transaction tx = startTransaction();
    try {
    // 核心业务逻辑
    tx.commit();
    } catch (RuntimeException e) {
    tx.rollback();
    throw e;
    }
    log("created book: " + book);
    }
    }</pre>

    继续编写updateBook(),代码如下:

    <pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="java" cid="n32" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">public class BookService {
    public void updateBook(Book book) {
    securityCheck();
    Transaction tx = startTransaction();
    try {
    // 核心业务逻辑
    tx.commit();
    } catch (RuntimeException e) {
    tx.rollback();
    throw e;
    }
    log("updated book: " + book);
    }
    }</pre>

    对于安全检查、日志、事务等代码,它们会重复出现在每个业务方法中。使用OOP,我们很难将这些四处分散的代码模块化。

    考察业务模型可以发现,BookService关心的是自身的核心逻辑,但整个系统还要求关注安全检查、日志、事务等功能,这些功能实际上“横跨”多个业务方法,为了实现这些功能,不得不在每个业务方法上重复编写代码。

    一种可行的方式是使用Proxy模式,将某个功能,例如,权限检查,放入Proxy中:

    <pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="java" cid="n36" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">public class SecurityCheckBookService implements BookService {
    private final BookService target;

    public SecurityCheckBookService(BookService target) {
    this.target = target;
    }

    public void createBook(Book book) {
    securityCheck();
    target.createBook(book);
    }

    public void updateBook(Book book) {
    securityCheck();
    target.updateBook(book);
    }

    public void deleteBook(Book book) {
    securityCheck();
    target.deleteBook(book);
    }

    private void securityCheck() {
    ...
    }
    }</pre>

    这种方式的缺点是比较麻烦,必须先抽取接口,然后,针对每个方法实现Proxy。

    另一种方法是,既然SecurityCheckBookService的代码都是标准的Proxy样板代码,不如把权限检查视作一种切面(Aspect),把日志、事务也视为切面,然后,以某种自动化的方式,把切面织入到核心逻辑中,实现Proxy模式。

    如果我们以AOP的视角来编写上述业务,可以依次实现:

    1. 核心逻辑,即BookService;

    2. 切面逻辑,即:

    3. 权限检查的Aspect;

    4. 日志的Aspect;

    5. 事务的Aspect。

    然后,以某种方式,让框架来把上述3个Aspect以Proxy的方式“织入”到BookService中,这样一来,就不必编写复杂而冗长的Proxy模式。

    AOP原理

    如何把切面织入到核心逻辑中?这正是AOP需要解决的问题。换句话说,如果客户端获得了BookService的引用,当调用bookService.createBook()时,如何对调用方法进行拦截,并在拦截前后进行安全检查、日志、事务等处理,就相当于完成了所有业务功能。

    在Java平台上,对于AOP的织入,有3种方式:

    1. 编译期:在编译时,由编译器把切面调用编译进字节码,这种方式需要定义新的关键字并扩展编译器,AspectJ就扩展了Java编译器,使用关键字aspect来实现织入;

    2. 类加载器:在目标类被装载到JVM时,通过一个特殊的类加载器,对目标类的字节码重新“增强”;

    3. 运行期:目标对象和切面都是普通Java类,通过JVM的动态代理功能或者第三方库实现运行期动态织入。

    最简单的方式是第三种,Spring的AOP实现就是基于JVM的动态代理。由于JVM的动态代理要求必须实现接口,如果一个普通类没有业务接口,就需要通过CGLIB或者Javassist这些第三方库实现。

    AOP技术看上去比较神秘,但实际上,它本质就是一个动态代理,让我们把一些常用功能如权限检查、日志、事务等,从每个业务方法中剥离出来。

    需要特别指出的是,AOP对于解决特定问题,例如事务管理非常有用,这是因为分散在各处的事务代码几乎是完全相同的,并且它们需要的参数(JDBC的Connection)也是固定的。另一些特定问题,如日志,就不那么容易实现,因为日志虽然简单,但打印日志的时候,经常需要捕获局部变量,如果使用AOP实现日志,我们只能输出固定格式的日志,因此,使用AOP时,必须适合特定的场景。

    2. AOP常用实现方式

    AOP常用的实现方式有两种,

    1、采用声明的方式来实现(基于XML),

    2、是采用注解的方式来实现(基于AspectJ)。

    3. AOP中一些比较重要的概念

    • 横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志 , 安全 , 缓存 , 事务等等 ....

    • <mark style="box-sizing: border-box; background: rgb(255, 255, 0); color: rgb(0, 0, 0);">切面(ASPECT)</mark>:横切关注点 被模块化 的特殊对象。即,它是一个类。(把通知应用到切入点的过程)

    • <mark style="box-sizing: border-box; background: rgb(255, 255, 0); color: rgb(0, 0, 0);">通知/增强(Advice)</mark>:切面必须要完成的工作。即,它是类中的一个方法。说的通俗点,就是在指定切点上要干些什么。

      • 前置通知 Before

      • 后置通知 afterReturning

      • 环绕通知 Around

      • 异常通知 AfterThrowing

      • 最终通知 after

    • 目标(Target):被通知对象。

    • 代理(Proxy):向目标对象应用通知之后创建的对象。

    • <mark style="box-sizing: border-box; background: rgb(255, 255, 0); color: rgb(0, 0, 0);">切入点(PointCut)</mark>:切面通知 执行的 “地点”的定义。(真正被增强的方方法)

    • <mark style="box-sizing: border-box; background: rgb(255, 255, 0); color: rgb(0, 0, 0);">连接点(JointPoint)</mark>:与切入点匹配的执行点。在Spring中就是某一个方法的执行 。(简单理解:哪些方法可以被增强?)

    注意:

    在使用spring框架配置AOP的时候,不管是通过XML配置文件还是注解的方式都需要定义pointcut"切入点"。

    <mark style="box-sizing: border-box; background: rgb(255, 255, 0); color: rgb(0, 0, 0);">execution([权限修饰符] [返回类型] [类全路径] [方法名称] ([参数列表]))</mark>

    例如定义切入点表达式 execution(* com.sample.service.impl..(..))

    execution()是最常用的切点函数,其语法如下所示:

    整个表达式可以分为五个部分:

    (1)、execution(): 表达式主体。

    (2)、第一个*号:表示权限修饰符,可以是public等

    (3)、包名:表示需要拦截的包名,后面的两个句点表示当前包和当前包的所有子包,com.sample.service.impl包、子孙包下所有类的方法。

    (4)、第二个号:表示类名,号表示所有的类。

    (5)、(..):最后这个星号表示方法名,号表示所有的方法,后面括弧里面表示方法的参数,两个句点表示任何参数。

    二、AOP在spring中的应用

    1. 基于XML配置的Spring AOP

    1. 导入Aop织入依赖

    <pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="xml" cid="n110" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">
    <dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.4</version>
    </dependency></pre>

    2. 添加业务接口

    <pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="java" cid="n112" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">public interface UserService {
    /**
    *功能描述: 执行查询

    • @author mfei
    • @date 2021/3/12
      */
      void select();
      }</pre>

    3. 业务实现

    <pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="java" cid="n114" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">public class UserServiceImpl implements UserService {

    /**

    • 功能描述: 执行查询
    • @author mfei
    • @date 2022/2/12
      */
      public void select() {
      System.out.println("执行查询用户的方法");
      }
      }</pre>

    4. 创建切面类

    <pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="java" cid="n116" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">public class AopAspect {
    /**

    • 前置通知:目标方法调用之前执行的代码
    • @param jp 目标方法
      /
      public void doBefore(JoinPoint jp){
      System.out.println("===========执行前置通知============");
      }
      /
      *
    • 最终通知:目标方法调用之后执行的代码(无论目标方法是否出现异常均执行)
    • 因为方法可能会出现异常,所以不能返回方法的返回值
    • @param jp 目标方法
      */
      public void doAfter(JoinPoint jp){
      System.out.println("===========执行最终通知============");
      }

    /**

    • 环绕通知:目标方法调用前后执行的代码,可以在方法调用前后完成自定义的行为。
    • 包围一个连接点(join point)的通知。它会在切入点方法执行前执行同时方法结束也会执行对应的部分。
    • 主要是调用proceed()方法来执行切入点方法,来作为环绕通知前后方法的分水岭。
    • 环绕通知类似于动态代理的全过程:ProceedingJoinPoint类型的参数可以决定是否执行目标方法。
    • 而且环绕通知必须有返回值,返回值即为目标方法的返回值
    • @param pjp 目标方法
    • @return 返回值
    • @throws Throwable 异常接口
      /
      public Object doAround(ProceedingJoinPoint pjp) throws Throwable{
      System.out.println("======执行环绕通知开始=========");
      // 调用方法的参数
      Object[] args = pjp.getArgs();
      // 调用的方法名
      String method = pjp.getSignature().getName();
      // 获取目标对象
      Object target = pjp.getTarget();
      // 执行完方法的返回值
      // 调用proceed()方法,就会触发切入点方法执行
      Object result=pjp.proceed();
      System.out.println("输出,方法名:" + method + ";目标对象:" + target + ";返回值:" + result);
      System.out.println("======执行环绕通知结束=========");
      return result;
      }
      @AfterThrowing("execution(
      com.shida.dao.service.UserServiceImpl.*(..))")
      public void doAfterThrowing(){
      System.out.println("======在异常之后执行=========");
      }
      }</pre>

    5. spring配置

    <pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="xml" cid="n118" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;"><?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.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop.xsd">

    <bean id="userService" class="com.shida.service.impl.UserServiceImpl"/>


    <bean id="aspectBean" class="com.shida.aspect.AopAspect" />
    <aop:config>
    <aop:aspect ref="aspectBean">
    <aop:pointcut id="pointcut" expression="execution(* com.shida.service.impl.UserServiceImpl..*(..))"/>
    <aop:before method="doBefore" pointcut-ref="pointcut"/>
    <aop:after method="doAfter" pointcut-ref="pointcut" />
    <aop:around method="doAround" pointcut-ref="pointcut"/>
    </aop:aspect>
    </aop:config>

    </beans>
    </pre>

    6. 测试类

    <pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="java" cid="n120" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;"> @Test
    public void testAop(){
    ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
    UserService userService = (UserService) context.getBean("userService");
    userService.select();
    }</pre>

    image-20210312105555314

    2. 基于注解实现

    采用注解来做aop, 主要是将写在spring 配置文件中的连接点写到注解里面。

    业务接口和业务实现与上边一样,具体切面类如下:

    <pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="java" cid="n125" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">package com.shida.aspect;

    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.After;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;

    /**

    • Description:
    • @author mfei
    • @date 2022/2/12
      /
      @Aspect
      public class AnnotationAopAspect {
      @Before("execution(
      com.shida.service.impl.UserServiceImpl.
      (..))")
      public void doBefore(){
      System.out.println("---------方法执行前---------");
      }

    @After("execution(* com.shida.service.impl.UserServiceImpl.*(..))")
    public void doAfter(){
    System.out.println("===========执行最终通知============");
    }

    @Around("execution(* com.shida.service.impl.UserServiceImpl.*(..))")
    public Object doAround(ProceedingJoinPoint jp) throws Throwable {
    System.out.println("======执行环绕通知开始=========");
    // 调用方法的参数
    Object[] args = jp.getArgs();
    // 调用的方法名
    String method = jp.getSignature().getName();
    // 获取目标对象
    Object target = jp.getTarget();
    // 执行完方法的返回值
    // 调用proceed()方法,就会触发切入点方法执行
    Object result=jp.proceed();
    System.out.println("输出,方法名:" + method + ";目标对象:" + target + ";返回值:" + result);
    System.out.println("======执行环绕通知结束=========");
    return result;
    }
    }</pre>

    spring配置

    <pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="xml" cid="n127" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;"><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.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop.xsd">

    <context:component-scan base-package="com.shida.dao"/>


    <bean id="hello" class="com.shida.entity.Hello">
    <property name="name" value="Spring"/>
    </bean>

    <bean id="userService" class="com.shida.service.impl.UserServiceImpl"/>











    <bean id="annotationPointcut" class="com.shida.aspect.AnnotationPointcut"/>


    <aop:aspectj-autoproxy/>

    </beans></pre>

    注:若有两个增强类对同一方法进行增强,可以设置@Order(1) ,数字越小,执行顺序越提前

    Aop底层实现:动态代理

    1.动态代理:有接口的情况

    JDK动态代理实现,创建接口实现类的代理对象,增强类的方法

    底层实现原理:implements

    <pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="java" cid="n133" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">public interface UserDao {
    /**
    *功能描述: 获取用户信息

    • @author mfei
    • @date 2022/2/25
      */
      void getUser();
      }</pre>

    <pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="java" cid="n134" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">public class UserDaoImpl implements UserDao {

    @Override
    public void getUser() {
    System.out.println("获取到了用户信息");
    }
    }</pre>

    jdk动态代理Proxy

    Java 8 中文版 - 在线API中文手册 - 码工具 (matools.com)

    image-20220227113032721

    参数理解:

    interfaces 增强方法所在的类:这个类实现的接口,支持多个接口

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

    1. 创建接口,实现方法

      <pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="java" cid="n144" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit;">public interface UserDao {

      int add(int a, int b);

      String getUser(String id);

      }</pre>

      <pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="java" cid="n145" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit;">public class UserDaoImpl implements UserDao {
      @Override
      public int add(int a, int b) {
      return a+b;
      }

      @Override
      public String getUser(String id) {
      return id;
      }
      }</pre>

    2. 使用Proxy类创建接口的代理对象

      <pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="java" cid="n148" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit;">public class JDKProxy {

      public static void main(String[] args) {
      Class[] interfaces = {UserDao.class};
      // 创建接口实现类
      Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new InvocationHandler() {
      // 匿名内部类
      @Override
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      return null;
      }
      });

      }</pre>

      不适用匿名内部类

      完整代码:

      <pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="java" cid="n151" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit;">public class JDKProxy {

      public static void main(String[] args) {
      Class[] interfaces = {UserDao.class};
      UserDaoImpl userDao = new UserDaoImpl();
      UserDao user = (UserDao) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new UseDaoProxy(userDao));
      int add = user.add(1, 2);
      System.out.println(add);
      }
      }
      class UseDaoProxy implements InvocationHandler{
      private Object obj;
      public UseDaoProxy(Object obj) {
      this.obj = obj;
      }

     @Override
     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
     System.out.println("方法之前执行"+method.getName()+"传递的参数"+ Arrays.toString(args));
     Object invoke = method.invoke(obj, args);
     System.out.println(invoke);
     System.out.println(obj);
     return invoke;
     }
    }</pre>
    

    2.没有接口情况的动态代理

    底层实现原理:extends

    <pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="java" cid="n155" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">/**

    • Description:
    • @author mfei
    • Date: 2022/2/25 9:36
      **/
      public class UserService {

    public void getUser(){
    System.out.println("获取到了用户信息");
    }
    }</pre>

    <pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="java" cid="n156" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">public class PersonService extends UserService {

    @Override
    public void getUser() {
    super.getUser();
    // 增强的逻辑
    }
    }</pre>

    通过CGLIB动态代理

    实现MethodInterceptor接口

    相关文章

      网友评论

          本文标题:AOP

          本文链接:https://www.haomeiwen.com/subject/wgtkrrtx.html