Spring ----AOP

简介: Spring ----AOP

一、静态代理设计模式

1.为什么需要代理设计模式

1.1回顾MVC和三层架构

我们先来回顾一下MVC

MVC:Model 模型 比如实体类 ,View 视图 比如JSP页面,Controller 控制器 比如Servlet
早期的三层架构在这里插入图片描述
用户直接访问控制层,控制层就可以直接操作数据库
servlet--对数据进行增删改查
缺点:程序臃肿,不利于维护
Servlet的代码中:处理请求,响应,视图跳转,处理JDBC,处理业务代码,处理逻辑代码
因为上面的架构,分工不够明确,所以就演变成下面这样的架构
在这里插入图片描述
Model:

  • 业务处理:业务逻辑(Service)
  • 数据持久层:CRUD(DAO)

View

  • 展示数据
  • 提供连接发起Servlet请求

Controller

  • 接收用户请求:(req:请求参数,Session参数)
  • 提供连接发起Servlet请求交给业务层处理对应的代码
  • 控制视图跳转
  • 登录--->接收用户登录请求--->处理用户请求(获取用户登录的参数用户名,密码)--->交给业务层处理登录业务(判断用户名密码是否正确)--->DAO层查询用户名和密码是否正确

然后来简单回顾一个开发中的分层
DAO--->Service--->Controler

  • DAO:数据访问层,主要用来对数据库的数据进行操作
  • Service:业务层,用来处理业务逻辑
  • Controler:控制器

这里我推荐两篇关于MVC和三层架构的文章,我个人觉得讲解得很详细
浅谈 MVC与三层架构
MVC与三层架构理解

1.2为什么需要代理

Service层中主要是用来处理业务的,也就是核心功能,像那些业务逻辑,访问数据库的操作等。像那些事务,日志,性能等操作,属于附加功能,可有可无,代码量也比较少
那么额外功能写在Service层中好不好呢

  • Service层从调用者角度(Controller):需要在Service层写额外功能
  • 从软件设计角度,不希望Service层写额外功能,因为它是可有可无的

接下来举一个现实生活中的例子来说明一下生活中也会有类似场景

比如说有人想要租房子,那么我们可以把房东看成一个类,代表业务类,包含核心功能也就是和房客签订出租房屋的合同,但是它肯定不能只有核心功能,别人为什么要找你租房,而不是找其他人呢?可能是因为你张贴广告,并且房客来看过房子了,所以还需要有额外功能,贴广告和看房。但是我们来想一下,贴广告这些其实挺累的,房东肯定不想做这些事情,他们希望的是房客直接来找他们签合同租房,也就是不希望额外功能。但是房客又不允许房东没有额外功能,毕竟没有看到房子怎么样,房客怎么可能直接签订合同
在这里插入图片描述

  • 房东(类似于软件设计者):不想要额外功能
  • 房客(调用者):想要额外功能

我们肯定是尽量满足所有人的要求的,所以我们以后就跟房东说,你只要实现核心功能就可以了,其他的不用你管。但是房客就很迷糊了,我都不知道谁有房子出租,而且没有看房怎么签合同。这个时候,中介(Proxy)出现了,中介说,大家放心,我负责提供房子信息,房客以后就只需要找中介就可以了,房客看到房子后觉得很满意,想要签订合同,但是这个时候,这个功能就不属于中介管了,这就需要房东了,毕竟房产证又不在你中介手里,房客说,我怎么知道这个房子是你的,万一不是你的,我的钱不就打水漂了,所以到签合同的这个时候,中介就会说,房东你来吧,这样子房客的诉求也满足了,皆大欢喜了。
如果说,房客对中介提供的房源不满意,那么他可以找其他的中介,这样子,我们发现,我们并不需要修改代码,这样就提高了代码的维护性
在这里插入图片描述

2.代理设计模式

2.1概念

  • 通过代理类,为原始类(目标类)增加额外功能
  • 代理类提供的方法要和目标类的方法一一对应
  • 有利于目标类的维护
  • 目标类(原始类):指的是业务类,里面只做核心功能,比如业务运算,DAO的调用
  • 目标方法(原始方法):目标类中的方法
  • 额外功能:比如事务,日志,性能

2.2开发的核心要素

代理类=目标类+额外功能+目标类和代理类要实现相同的接口(为了让代理类和目标类的方法保持同步)

2.3实战

我们以租房子为例,来进行演示,下面这个演示是静态代理
静态代理:为每一个目标类,都提供一个代理类
提供代理类和目标类的公共接口

public interface House {
    /**
     * 出租房屋
     */
    public void rent();

}

房东

/**房东
 * @author zengyihong
 * @create 2022--04--18 22:13
 */
public class Landlord implements House {
    @Override
    public void rent() {
        System.out.println("房东:签订合同,出租房屋");
    }
}

房屋中介(代理类)

**房屋中介
 * @author zengyihong
 * @create 2022--04--18 22:12
 */
public class HouseProxy implements House {

    Landlord landlord=new Landlord();
    Lodger lodger=new Lodger();
    /**
     * 当中介带领房客看房屋后,如果房客说满意,那么就可以签订合同了
     * 如果房客不满意,那么他可能就会寻找其他房屋了
     */
    @Override
    public void rent() {
        boolean flag= lodger.satisfy();;
        if (flag){
            landlord.rent();
        }else {
            System.out.println("房客:我还想看看其他的房屋");
        }
    }

    /**
     * 带领想租房子的人看房,对方如果说满意了,就可以签订合同了
     * @return
     */
    public void show(){
        System.out.println("中介:带领房客看房屋");
        rent();


    }
}

房客

/**房客
 * @author zengyihong
 * @create 2022--04--18 22:11
 */
public class Lodger {
    Scanner scanner=new Scanner(System.in);
    /**
     * 看房子是否满意
     */
    public boolean satisfy(){
        System.out.println("请问您对房子满意吗(Y/N)");
        String str=scanner.next();
        if ("Y".equalsIgnoreCase(str)){
            return true;
        }else{
            return false;
        }
    }

}

测试
在这里插入图片描述
在这里插入图片描述

2.4静态代理存在的问题

  • 静态代理的类文件过多,一个目标类就有一个代理类,不利于项目管理
  • 以上面的代码举例子,可能有不同的中介来提供房屋信息,不同的中介其实它们提供的功能基本是一样的,但是现在写了这么多的代理类,其实也没什么意义
  • 额外功能维护性差,也就是代理类中,额外功能修改复杂,比较麻烦

二、动态代理

1.Spring动态代理的概念

动态代理和静态代理在概念上没有什么区别,主要是开发步骤和底层实现的不同

  • 概念:通过代理类为目标类增加额外功能
  • 好处:有利于目标类的维护

2.开发环境

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>org.example</groupId>
  <artifactId>spring-01</artifactId>
  <version>1.0-SNAPSHOT</version>

  <name>spring-01</name>
  <!-- FIXME change it to the project's website -->
  <url>http://www.example.com</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.7</maven.compiler.source>
    <maven.compiler.target>1.7</maven.compiler.target>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter</artifactId>
      <version>RELEASE</version>
      <scope>compile</scope>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.3.18</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.3.18</version>
    </dependency>
<!--    日志-->
    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.17</version>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-log4j12</artifactId>
      <version>1.7.21</version>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>1.7.21</version>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>8.0.16</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aop</artifactId>
      <version>5.3.18</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.9.6</version>
      <scope>runtime</scope>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjrt -->
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjrt</artifactId>
      <version>1.9.8</version>
      <scope>runtime</scope>
    </dependency>



  </dependencies>


</project>

3.Spring动态代理的开发步骤

public interface UserService {
    public void register(User user);
    public boolean login(String name,String password);
}

①创建原始对象(目标对象)

/**
 * 原始类
 *
 * @author zengyihong
 * @create 2022--04--18 21:57
 */
public class UserServiceImpl implements UserService {
    @Override
    public void register(User user) {
        System.out.println("UserServiceImpl.register 业务运算+DAO");
    }

    @Override
    public boolean login(String name, String password) {

        System.out.println("UserServiceImpl.login");
        return true;


    }
}

然后在Spring的配置文件进行配置来创建对象

<bean id="userServiceImpl" class="com.zyh.proxy.UserServiceImpl"></bean>

②提供额外功能
我们需要把额外功能写在MethodBeforeAdvice接口的实现中
一旦实现了这个接口,那么在运行的时候,这个接口里面的额外功能会在原始类的方法运行前执行

/**before方法的作用就是把运行在原始方法执行之前运行的额外功能写在before方法中
 * @author zengyihong
 * @create 2022--04--19 8:56
 */
public class Before implements MethodBeforeAdvice {
 @Override
 public void before(Method method, Object[] args, Object target) throws Throwable {
  System.out.println("----method before advice log---");
 }
}
 <bean id="before" class="com.zyh.dynamic.Before"></bean>

③定义切入点

  • 切入点指的是额外功能加入的位置
  • 目的:由程序员根据自己的需要,觉得额外功能加给哪一个原始方法
  • 简单测试:所有方法都作为切入点,都加入额外的功能
<!--代表所有方法都作为切入点 目前是login register-->
    <aop:config>
        <aop:pointcut id="pc" expression="execution(* *(..))"/>
    </aop:config>

④组装
把第二步和第三步进行整合
为了看清楚一点,我把上面的配置都在这里体现

  <bean id="userServiceImpl" class="com.zyh.proxy.UserServiceImpl"></bean>
<!--代表所有方法都作为切入点 目前是login register-->
    <aop:config>
        <aop:pointcut id="pc" expression="execution(* *(..))"/>
<!--        组装,把切入点和额外功能整合-->
        <aop:advisor advice-ref="before" pointcut-ref="pc"></aop:advisor>
    </aop:config>

⑤调用
获得Spring工厂创建的动态代理对象,并且进行调用

ApplicationContext ctx=new ClassPathXmlApplicationContext("/applicationContext.xml");
注意:
1.Spring工厂通过原始对象的id值获得的是代理对象
2.获得代理对象后,可以通过声明接口类型,进行对象的存储
UserService userService=(UserService)ctx.getBean("userServiceImpl");
userService.login("","");
userService.regist();

测试

 @Test
    public void test() {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext3.xml");
        UserService userService = (UserService) ctx.getBean("userServiceImpl");
        userService.login("tom","123");
        userService.register(new User());

    }

运行结果

----method before advice log---   额外功能
UserServiceImpl.login              原始功能
----method before advice log---
UserServiceImpl.register 业务运算+DAO

4.动态代理细节分析

  • Spring创建的动态代理类在哪里?

    • Spring创建的动态代理类,是Spring框架在运行的时候,通过动态字节码技术,在JVM创建的,运行在JVM内部,等程序结束后,会和JVM一起消失
  • 什么又叫动态字节码技术

    • 先来回顾一下,以前是怎么运行一个类,是JVM运行这个类的字节码文件,然后创建该对象,进而调用这个对象的方法
    • 现在通过动态字节码技术的话,我们就不需要编写源文件编译成.class文件
    • 动态字节码是通过第三方框架来完成,我们通过这些框架,就可以直接在JVM生成字节码(动态字节码),把动态字节码加载到JVM中后,就可以创建对象了
    • 动态代理其实就是动态字节码

    总结一下

    • 动态字节码技术:通过第三方的动态字节码框架,在JVM中创建对应类的字节码,进而创建对象
    • 动态代理不需要定义类文件,都是JVM运行过程中动态创建的,所以不会产生像静态代理,类文件数量过多,影响项目管理的问题
    • 在这里插入图片描述
  • 动态代理编程简化代理的开发

    • 在额外功能不变的情况下,我们在创建目标类的代理对象时,只需要指定目标对象就可以了
    • 动态代理额外功能的维护性大大增强

      • 以前用静态代理的话,比如说有几个类的方法的功能是一样的,但是这个时候我们想要修改它的功能,就得对这几个类的方法都进行修改,比较麻烦
      • 使用动态代理的话,我们只需要新创建一个类来实现 MethodBeforeAdvice接口,并且实现里面的before方法,然后在配置文件进行配置即可

三、Spring动态代理详解

1.MethodBeforeAdvice详解

 @Override
 public void before(Method method, Object[] args, Object target) throws Throwable {
  System.out.println("----method before advice log---");
 }
  • Method:指的是额外功能要增加给的那个原始方法

    • 比如是在login方法执行之前,我们想要执行这个额外功能,那么login就是原始方法
    • 额外功能要增加给的原始方法的参数
    • 在这里插入图片描述

MethodBeforeAdvice接口作用:额外功能运行在原始方法执行前进行额外功能操作的话,就得实现这个接口

2.MethodInterceptor(方法拦截器)

我们要写一个类实现这个接口,并且实现这个接口的invoke方法

  @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        return null;
    }

可以在原始方法执行之前或之后运行
所以我们应该要先知道原始方法在什么时候运行,这样才可以让额外功能在原始方法之前或之后运行
MethodInvocation invocation:这个参数代表的就是额外功能所增加给的那个原始方法

这个方法就代表原始方法的执行
 invocation.proceed();

方法的返回值Object代表原始方法的返回值,如果原始方法没有返回值,就认为返回的是null
在这里插入图片描述
配置文件配置

<bean id="arround" class="com.zyh.dynamic.Arround"></bean>
    
<!--代表所有方法都作为切入点 目前是login register-->
    <aop:config>
        <aop:pointcut id="pc" expression="execution(* *(..))"/>
<!--        组装,把切入点和额外功能整合-->
        <aop:advisor advice-ref="arround" pointcut-ref="pc"></aop:advisor>
    </aop:config>
public class Arround implements MethodInterceptor {
    /**
     * 我们把额外功能写在invoke方法中,这样额外功能就可以运行在原始方法执行之前或之后
     * @param invocation
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("额外功能执行");
        Object obj = invocation.proceed();

        return obj;
    }
}

运行效果
在这里插入图片描述
像事务的开启,提交这样的操作,我们就可以把它写在核心功能的前面和后面,在前面写事务的开启,在后面写事务的提交
额外功能如果是运行在原始方法抛出异常的时候呢,我们可以在try-catch里面写额外功能

3切入点详解

切入点决定了额外功能添加的位置

<!--代表所有方法都作为切入点 目前是login register-->
    <aop:config>
        <aop:pointcut id="pc" expression="execution(* *(..))"/>
<!--        组装,把切入点和额外功能整合-->
        <aop:advisor advice-ref="arround" pointcut-ref="pc"></aop:advisor>
    </aop:config>
execution(* *(..))匹配了所有方法
execution是切入点函数
 * *(..)是切入点表达式

3.1切入点表达式

3.1.1方法切入点表达式

 * *(..)是切入点表达式 匹配了所有方法

在这里插入图片描述
接下来我们来看看,怎么把一个具体的方法定义成切入点

  • 定义login方法作为切入点
<!--代表所有方法都作为切入点 目前是login register-->
    <aop:config>
        <aop:pointcut id="pc" expression="execution(* login(..))"/>
<!--        组装,把切入点和额外功能整合-->
        <aop:advisor advice-ref="arround" pointcut-ref="pc"></aop:advisor>
    </aop:config>

在这里插入图片描述

  • 定义login方法并且login方法有两个字符串类型的参数作为切入点
 <aop:config>
        <aop:pointcut id="pc" expression="execution(* login(String,String ))"/>
<!--        组装,把切入点和额外功能整合-->
        <aop:advisor advice-ref="arround" pointcut-ref="pc"></aop:advisor>
    </aop:config>

如果我们说切入点的参数类型和我们的方法类型不匹配,就不会执行额外功能
注意:如果参数类型不是java.lang包中的类型,在切入点中必须写全限定名称

上面讲解的方法切入点表达式不精准,因为不同的包可能有同名同参的方法,我们本来只是想要匹配其中的一个方法,但是现在就会导致匹配多个方法。
为了解决这个方法,我们就得通过包,类,方法名,参数来限定我们要选择的方法
切入点
在这里插入图片描述
在这里插入图片描述

<!--代表所有方法都作为切入点 目前是login register-->
    <aop:config>
        <aop:pointcut id="pc" expression="execution(* com.zyh.proxy.UserServiceImpl.login())"/>
<!--        组装,把切入点和额外功能整合-->
        <aop:advisor advice-ref="arround" pointcut-ref="pc"></aop:advisor>
    </aop:config>

3.2类切入点

如果一个类的所有方法都要加入额外功能的话,我们就可以把整个类作为切入点,这样就避免一个方法一个方法来配置了

  <aop:config>
        <aop:pointcut id="pc" expression="execution(* com.zyh.proxy.UserServiceImpl.*(..))"/>
<!--        组装,把切入点和额外功能整合-->
        <aop:advisor advice-ref="arround" pointcut-ref="pc"></aop:advisor>
    </aop:config>

在这里插入图片描述
在这里插入图片描述

注意:*只能套一层包,比如说类A写在com.zyh.proxy下面,那么这个时候,如果我们写的是.UserServiceImpl.(..)是不可以的,因为*只能处理一层包
但是如果我们这个时候就想用*的话,要怎么办
那就写* . .UserServiceImpl.*(..)注意:这里有两个.
两个.代表一级包乃至多级包

3.3包切入点

包切入点就是说把指定包作为额外功能加入的位置,那么,包的所有类以及方法都会加入额外功能

      <aop:pointcut id="pc" expression="execution(* com.zyh.proxy.*.*(..))"/>
注意:如果使用这种方法的话,那么切入点包中的所有类必须放在proxy包下面,而不能是proxy包下的子包
如果我们想要把proxy包及其子包下的所有类及其方法作为切入点的话,要怎么办?
 <aop:pointcut id="pc" expression="execution(* com.zyh.proxy..*.*(..))"/>
 
注意:com.zyh.proxy. . *.*(..)
proy后面有两个.代表proxy包及其子包

4.切入点函数

4.1execution,args,within切入点函数

切入点函数:用于执行切入点表达式
接下来看看有哪些主要的切入点函数
①execution
execution函数是最重要的切入点函数,功能最全面
它可以执行方法切入点表达式,类切入点表达式,包切入点表达式
缺点:execution执行切入点表达式书写麻烦
为了书写简单一点,因此就有其他切入点函数,它们的功能是一样的,只是为简化execution的复杂度
②args
主要用来方法参数的匹配
比如说方法是什么我们现在并不关心,只关心参数类型,这个时候就可以使用这个
execution( (String ,String))
args(String,String)
在这里插入图片描述
在这里插入图片描述
③within
主要用于类,包切入点表达式的匹配
比如说我们选择切入点是UserServiceImpl,不关心它是哪一个包的
execution(* *. .UserServiceImpl.**(String ,String))
within(*. .UserServiceImpl)
如果是要把某一包及其子包下面的类和方法作为切入点
within(com.zyh.proxy. .*)

4.2@annotation

作用:为具有特殊注解的方法加入额外功能
我们以后可能为某一个方法来加入注解,那么这个时候,@annotaion就会进行扫描,如果扫描到某个注解,就可以为它进行切入
所以第一步我们要先自定义一个注解
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4.3切入点函数的逻辑运算

切入点函数的逻辑运算指的是:整合多个切入点函数一起配合工作,进而完成更为复杂的需求
①and与操作

案例:login方法 同时参数是两个字符串
execution(* login(String,String))
execution(* login(..)) and args(String,String)

注意:与操作不能用于同种类型的切入点函数

②or或操作

execution(* login(..)) or execution(* register(..))

四、AOP

1.AOP概念

AOP(Aspect Oriented Programing):面向切面编程
面向切面编程就是以切面为基本单位的程序开发,通过切面间的彼此协同相互调用,完成程序的构建
切面=切入点+额外功能

  • AOP的概念

    • 本质上是Spring的动态代理开发,通过代理类为原始类增加额外功能
    • 好处:有利于原始类的维护

2.AOP编程的开发步骤

①原始对象
②额外功能
③切入点
私组装切面(额外功能+切入点)

3.切面的名词解释

4.AOP底层实现原理

1.核心问题

①AOP怎么创建动态代理类(动态字节码技术)
②Spring工厂怎么加工创建代理对象
通过原始对象的id值,获得的是代理对象

2.JDK动态代理

在这里插入图片描述
在这里插入图片描述

package com.zyh.jdk;

import com.zyh.proxy.User;
import com.zyh.proxy.UserService;
import com.zyh.proxy.UserServiceImpl;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * @author zengyihong
 * @create 2022--04--20 16:34
 */
public class TestJDKProxy {
    public static void main(String[] args) {
        //创建原始对象
        final UserService userService=new UserServiceImpl();
        //JDK动态代理
        /**
         * InvocationHandler:提供额外功能  额外功能其实是一个接口
         */
        InvocationHandler handler=new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("---proxy  log-----");
               //原始方法运行
                Object obj = method.invoke(userService, args);
                return obj;

            }
        };
        UserService userServiceProxy= (UserService)Proxy.newProxyInstance(TestJDKProxy.class.getClassLoader(),userService.getClass().getInterfaces(),handler);
        userServiceProxy.login("tom","123");
        userServiceProxy.register(new User());










    }
}
---proxy  log-----
UserServiceImpl.login
---proxy  log-----
UserServiceImpl.register 业务运算+DAO

在这里插入图片描述

3.CGlib动态代理

CGlib创建动态代理的原理:父子继承关系创建代理对象,原始类作为辅助,代理类作为子类,既可以保证二者方法一致,同时还可以在代理类中提供新的方法实现(额外功能+原始方法)
在这里插入图片描述

public class UserService {
    public void login(String name,String password){
        System.out.println("UserService.login");
    }
    public void register(User user){
        System.out.println("UserService.register");
    }


}
public class TestCglib {
    public static void main(String[] args) {
        //创建原始对象
        final UserService userService = new UserService();
        //CGlib方式创建动态代理对象
        Enhancer enhancer = new Enhancer();
        enhancer.setClassLoader(TestCglib.class.getClassLoader());
        enhancer.setSuperclass(userService.getClass());
        MethodInterceptor interceptor = new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                System.out.println("-------cglib  log---------");
                Object obj = method.invoke(userService, args);
                return obj;


            }
        };
        enhancer.setCallback(interceptor);
        UserService userServiceProxy = (UserService) enhancer.create();
        userServiceProxy.login("tom","123");
        userServiceProxy.register(new User());
    }
}

在这里插入图片描述

JDK动态代理 Proxy.newProxyInstance()   通过接口插件代理的实现类
Cglib动态代理 Enhancer    通过继承父类创建的代理类
相关文章
|
7天前
|
Java Spring
在Spring Boot中使用AOP实现日志切面
在Spring Boot中使用AOP实现日志切面
|
18天前
|
前端开发 Java 数据库
浅谈Spring AOP 面向切面编程 最通俗易懂的画图理解AOP、AOP通知执行顺序~
浅谈Spring AOP 面向切面编程 最通俗易懂的画图理解AOP、AOP通知执行顺序~
|
18天前
|
XML Java 数据格式
技术好文:Spring基础篇——AOP切面编程
技术好文:Spring基础篇——AOP切面编程
|
7天前
|
Java Spring
在Spring Boot中使用AOP实现日志切面
在Spring Boot中使用AOP实现日志切面
|
13天前
|
设计模式 缓存 程序员
Spring6(三):面向切面AOP(1)
Spring6(三):面向切面AOP(1)
14 1
|
13天前
|
XML 监控 Java
Java中的AOP编程:AspectJ与Spring AOP的应用
Java中的AOP编程:AspectJ与Spring AOP的应用
|
14天前
|
监控 Java 数据安全/隐私保护
Spring AOP实现原理及其在企业应用中的实际应用
Spring AOP实现原理及其在企业应用中的实际应用
|
7天前
|
XML 监控 Java
如何在Spring Boot中使用AOP
如何在Spring Boot中使用AOP
|
7天前
|
监控 Java Spring
在Spring Boot中使用AOP实现日志记录
在Spring Boot中使用AOP实现日志记录
|
12天前
|
Java Spring
Spring AOP(面向切面编程)详解
Spring AOP(面向切面编程)详解