Spring5系列(九) | spring动态代理详解

简介: Spring5系列(九) | spring动态代理详解

上篇文章中,我们了解了Spring动态代理的实现和一些细节,但其实主要都是讲的如何使用。今天我们更深一步,简单说说里边的一些实现原理,和切入点表达式的一些其他写法。

一. 回顾动态代理开发的四个步骤

  1. 目标对象
  2. 额外功能
  3. 切入点
  4. 组装

目标对象不用说了,其实一般就是我们自己写的一个业务上的接口和实现。我们今天重点说说剩下的三个步骤。

关于额外功能,我们上篇文章中的额外功能,是通过实现了MethodBeforeAdvice接口来实现的,那么我们先来说说额外功能。

二. 额外功能详解

我们上篇文章中用的是MethodAdvice接口,其实我们还可以使用MethodInterceptor方法拦截器的接口来实现额外功能的开发。我们分别来看一下

2.1 MethodAdvice

我们先来看下之前是怎么写的,再来解释下参数的具体含义。

publicclassBeforeimplementsMethodBeforeAdvice{ 
@Overridepublicvoidbefore(Methodmethod, Object[objects,Objecttarget){ 
System.out.println("proxy......"); 
  }
}


  1. before方法详解:

通过实现MethodBeforeAdvice接口,重写before方法实现。介绍下参数具体含义。

  • method: 额外功能所增加给的那个原始方法,如果增加给login, 那么method就是login
  • args: 额外功能所增加给的那个原始方法的参数,如果增加给login,那么args代表的就是login的参数(name, password)
  • target: 额外功能所增加给的原始对象。 userServiceImpl/orderServiceImpl.

这个参数的主要目的,其实就是我们在添加额外功能的时候,如果想要对原始方法进行加工,那么可以方便我们获取到原始对象、方法和参数,当然如果我们不需要加工,就可以不使用即可。

  1. 三个参数在实战中如何使用

before方法的参数在实战中会根据需要进行使用,不一定都会用到,也可能都不用。

2.2 MethodInterceptor 方法拦截器

MethodInterceptor接口,也可以帮助我们实现额外功能,他相当于是一个方法拦截器,就相当于对我们的目标方法做了拦截,我们就可以在方法的前后增加一些额外功能。该接口有两个需要实现的方法。

MethodAdvice 和 MethodInterceptor对比:

MethodBeforeAdvice: 原始方法运行之前执行

MethodInterceptor: 可以根据需要,执行在方法前,也可以执行在方法后,甚至前后都可以添加额外功能

MethodInterceptor 演示:

  1. 目标对象, 省略,继续使用UserService, UserServiceImpl.
  2. 额外功能,实现MethodInterceptor接口来添加
//导入的是 aopalliance包publicclassArroundimplementsMethodInterceptor{
/*** invoke方法作用: 额外功能,原始方法之前原始方法之后原始方法之前,之后确定原始方法怎么运行?@param MethodInvocation(before中的method) : 额外功能锁增加给的那个原始方法返回值: 原始方法执行后的返回值*/@OverridepublicObjectinvoke (MethodInvocationinvocation){
// 额外功能运行在原始方法执行之前System.out.println("----before----");
// 原始方法执行,获取返回值返回Objectobj=invocation.proceed();
// 额外功能运行在原始方法执行之后System.out.println("----after----");
returnobj;
  }
}
复制代码

这中方式的好处就是把目标方法的调用由我们自己控制,通过调用invocation.proceed();方法运行,这样我们就可以自由的在目前方法前后增加我们想要的功能。

装配到bean工厂。

<beanid="arround"claa="com.xxx.Around"/>
  1. 定义切入点和 组装
<aop:confg><aop:pointcutid="pc"expression="execution(* *(..))"/><aop:advisoradvice-ref="around"pointcut-ref="pc"/></aop:confg>

除了在目标方法前后增加增加额外功能,我们也可以在目标方法抛出异常的时候添加额外功能。

@OverridepublicObjectinvoke (MethodInvocationinvocation){
try{
Objectobj=invocation.proceed();
    }catch (Throwablet){
// 添加额外功能    }
returnobj;
}

注意: MethodInterceptor影响方法的返回值

  1. 如果吧玉环市方法的返回值,直接作为invoke方法的返回值,是不会影响到原始方法的返回的
  2. 如果接到原始方法的返回值后做修改,就会影响。

三. 切入点表达式详解

概念: 切入点,决定了额外功能加入的位置(方法) 我们回忆下上面的案例中使用的切入点表达式的写法:

<aop:pointcutid="pc"expression="execution(* *(..))"/>

这个写法代表的是匹配所有的方法,也就是会给所有的方法都加上额外功能。所以不管是userService还是orderService在执行里边方法的时候都会执行额外功能的代码。

那切入点表达式到时代表什么含义呢?

excution: 代表切入点函数,除了execution还有其他的函数,如args等
* *(..) 代表的是切入点表达式

3.1 切入点表达式

我们以一个方法为例:

public void add (int a, int b)
     *       *  (..)

第一个* : 代表修饰符返回值

第二个*:    代表方法名

():            代表参数列表

..   :          代表参数有没有,有几个,什么类型都可以。

举例:

  • *(..)    任意修饰符返回值, 任意方法名,任意参数,作为切入点,所以所有方法都包含
  • login(..)   任意修饰符返回值,方法名叫login,任意参数作为切入点,所以只切login方法
  • login(String,String):  只有login方法,且两个字符串类型参数的方法会执行额外功能
  • register(com.xxx.User); 和上面意思差不多,要注意类型不是java.lang要用全类名
  • login(String,..) : 第一个参数 必须是String,后面不限,可有可无,有几个无所谓

3.2 精准切入点限定

如果不同包下有同名的类,同名的方法,我们应该如何处理呢。

image.png

精准的表达方式,需要加入全类名限定。 修饰符返回值   包.类.方法(参数)

修饰符返回值               包.类.方法(参数)
  *   com.xxx.UserServiceImpl.login(..)
  • 类切入点:指定特定类作为额外功能加入的位置,自然这个类中所有的方法都会加入额外功能。
修饰符返回值           包.类.方法(参数)
  * com.xxx.UserServiceImpl.login(..)
  • 包名不同,类名相同的切入点情况
# 这种写法只能扫描一层包,只能扫描到com包下的
* *.UserServiceImpl.*(..)  
# 扫描多层包
* *..UserServiceImpl.*(..)
  • 包切入点表达式,某个包下所有类都扫描
# 第一个星代表包下所有类,第二个星代表所有方法
* com.xxx.demo.*.*(..)
注意: 切入点中的所有类必须都在demo包下,子包不行
# 包及子包下的所有类都生效写法
* com.xxx.demo..*.*(..)

3.3 切入点函数

作用: 用于执行切入点表达式

  • execution
最重要的切入点函数,功能最全
执行:方法切入点表达式, 类切入点表达式,  包切入点表达式
弊端: execution 执行切入点表达式,书写麻烦
  execution(* com.demo..*.*(..))
注意: 其他切入点函数,目的就是简化execution书写复杂度,功能上完全一致
  • args
作用: 主要用于函数(方法)参数的匹配
切入点: 方法的参数必须得是两个字符串类型的参数
execution(* *(String, String))
args(String,String)

xml案例:

<bean id="arround" claa="com.xxx.Around" />
<aop:confg>
   <aop:pointcut id="pc" expression="args(String,String)" />
   <aop:advisor advice-ref="around" pointcut-ref="pc" />
</aop:confg>
  • within
主要用于类,包切入点表达式的匹配
切入点: UserServiceImpl
execution(* *..UserServiceImpl.*(..))
within(*..UserServiceImpl)
包切入点:
executionn(* com.xxx..*.*(..))
within(com.xxx..*)

xml案例:

<beanid="arround"claa="com.xxx.Around"/><aop:confg><aop:pointcutid="pc"expression="within(*..UserServiceImpl)"/><aop:advisoradvice-ref="around"pointcut-ref="pc"/></aop:confg>
  • annocation

作用:  为有特殊注解的类或方法添加额外功能

@Target(ElementType.METHOD)
@Retention(RetentionType.RUNTIME)
public@intefaceLog{
}
publicclassUserServiceImplimplementsUserService{
@Overridepublicbooleanlogin(Stringname,Stringpassword){
    ...
  }
@Override@Logpublicvoidregister(Useruser){
    ...
  }
}
<beanid="arround"claa="com.xxx.Around"/><aop:confg><aop:pointcutid="pc"expression="@annocation(com.xxx.Log)"/><aop:advisoradvice-ref="around"pointcut-ref="pc"/></aop:confg>
  • 切入点函数的逻辑运算

整合多个切入点函数一起配合工作,进而完成更复杂的需求

and操作: 与操作

案例: login, 两个字符串参数作为切入点
1. execution(* login(String String))
2. execution(* login(String,String)) and args(String,String)
注意: 与操作不能用于同种类型的切入点函数
反例   execution() and  execution() 写法不行 
只要 login 和  register方法,不可能同时login又叫register
用or操作
复制代码

or操作: 或操作

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


目录
相关文章
|
6月前
|
运维 Java 程序员
Spring5深入浅出篇:Spring切入点详解
该文档是关于Spring框架中切入点的详细解释。切入点是AOP(面向切面编程)的核心概念,用于定义通知(如日志、事务管理)应该附加到代码的哪些位置。文档主要介绍了切入点表达式的不同类型: 1. 方法切入点表达式:使用`execution()`定义匹配的方法,星号`*`代表任意返回值和方法名,`(..)`表示任意参数。 2. 类切入点:指定特定类以应用额外功能,可以精确到类中的所有方法,或者只包含特定包的类。 3. 包切入点表达式:适用于整个包或包及其子包内的所有类和方法。
|
6月前
|
运维 Java 程序员
Spring5深入浅出篇:Spring动态代理详解
# Spring动态代理详解 本文探讨了Spring中的MethodBeforeAdvice和MethodInterceptor在动态代理中的应用和差异。MethodBeforeAdvice在方法执行前执行额外功能,而MethodInterceptor则可在方法执行前后或抛出异常时运行额外逻辑。MethodInterceptor还能影响原始方法的返回值。
|
3月前
|
XML Java 测试技术
Spring5入门到实战------17、Spring5新功能 --Nullable注解和函数式注册对象。整合JUnit5单元测试框架
这篇文章介绍了Spring5框架的三个新特性:支持@Nullable注解以明确方法返回、参数和属性值可以为空;引入函数式风格的GenericApplicationContext进行对象注册和管理;以及如何整合JUnit5进行单元测试,同时讨论了JUnit4与JUnit5的整合方法,并提出了关于配置文件加载的疑问。
Spring5入门到实战------17、Spring5新功能 --Nullable注解和函数式注册对象。整合JUnit5单元测试框架
|
3月前
|
Java
Spring5入门到实战------9、AOP基本概念、底层原理、JDK动态代理实现
这篇文章是Spring5框架的实战教程,深入讲解了AOP的基本概念、如何利用动态代理实现AOP,特别是通过JDK动态代理机制在不修改源代码的情况下为业务逻辑添加新功能,降低代码耦合度,并通过具体代码示例演示了JDK动态代理的实现过程。
Spring5入门到实战------9、AOP基本概念、底层原理、JDK动态代理实现
|
19天前
|
Java 数据安全/隐私保护 Spring
Spring进阶:初识动态代理
本文介绍了Spring框架中AOP切面编程的基础——动态代理。通过定义Vehicle接口及其实现类Car和Ship,展示了如何使用动态代理在不修改原代码的基础上增强功能。文章详细解释了动态代理的工作原理,包括通过`Proxy.newProxyInstance()`方法创建代理对象,以及`InvocationHandler`接口中的`invoke()`方法如何处理代理对象的方法调用。最后,通过一个测试类`TestVehicle`演示了动态代理的具体应用。
|
2月前
|
设计模式 Java 测试技术
spring复习04,静态代理动态代理,AOP
这篇文章讲解了Java代理模式的相关知识,包括静态代理和动态代理(JDK动态代理和CGLIB),以及AOP(面向切面编程)的概念和在Spring框架中的应用。文章还提供了详细的示例代码,演示了如何使用Spring AOP进行方法增强和代理对象的创建。
spring复习04,静态代理动态代理,AOP
|
3月前
|
XML Java Maven
Spring5入门到实战------16、Spring5新功能 --整合日志框架(Log4j2)
这篇文章是Spring5框架的入门到实战教程,介绍了Spring5的新功能——整合日志框架Log4j2,包括Spring5对日志框架的通用封装、如何在项目中引入Log4j2、编写Log4j2的XML配置文件,并通过测试类展示了如何使用Log4j2进行日志记录。
Spring5入门到实战------16、Spring5新功能 --整合日志框架(Log4j2)
|
3月前
|
Java API Spring
Spring5入门到实战------1、Spring5框架概述、入门案例
这篇文章是Spring5框架的入门教程,概述了Spring框架的核心概念和特点,并通过一个创建普通Java类的案例,详细演示了从下载Spring核心Jar包、创建配置文件、编写测试代码到运行测试结果的完整流程,涵盖了Spring IOC容器的使用和依赖注入的基本用法。
|
5月前
|
XML Java 数据格式
Spring5系列学习文章分享---第三篇(AOP概念+原理+动态代理+术语+Aspect+操作案例(注解与配置方式))
Spring5系列学习文章分享---第三篇(AOP概念+原理+动态代理+术语+Aspect+操作案例(注解与配置方式))
50 0
|
5月前
|
Java Spring
深入解析Spring源码,揭示JDK动态代理的工作原理。
深入解析Spring源码,揭示JDK动态代理的工作原理。
57 0