侧边栏壁纸
  • 累计撰写 98 篇文章
  • 累计创建 85 个标签
  • 累计收到 9 条评论

spring系列笔记 - 第⼗六章 Spring动态代理详解

bearjun
2021-07-07 / 0 评论 / 0 点赞 / 605 阅读 / 5,028 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2021-07-07,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

额外功能的详解

1、MethodBeforeAdvice分析
  1. MethodBeforeAdvice接口作用:额外功能运行在原始方法执行之前,进行额外功能操作。
  2. 方法参数详解
public class Before implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println("---new method before advice log---");
    }

}

**Method:**额外功能所增加给的那个原始方法,例如:login方法,regist方法。
**Object[]:**额外功能所增加给的那个原始方法的参数,例如:login(String username, String password)的参数。
**Object:**额外功能所增加给的那个原始对象,例如:整个login的原始对象。
3) before方法的3个参数在实战中,该如何使用?
before方法的参数,在实战中,会根据需要进行使用,不⼀定都会用到,也有可能都不用。

2、MethodInterceptor(方法拦截器)

methodinterceptor接口:额外功能可以根据需要运行在原始方法执行 前、后、前后

  • 参数:MethodInvocation:额外功能所增加给的那个原始方法 (login, register)。
  • 返回值:Object:原始方法的返回值 (没有就返回 null)。
  • invocation.proceed():原始方法运行。

额外功能运行在原始方法 之前

public class Around implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        System.out.println("---额外功能运行在原始方法执行之前---");
        Object ret = methodInvocation.proceed(); // 原始方法运行, 获取原始方法的返回值
        return ret;
    }
}

额外功能运行在原始方法 之后

public class Around implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        Object ret = methodInvocation.proceed(); // 原始方法运行, 获取原始方法的返回值
        System.out.println("---额外功能运行在原始方法执行之后---");
        return ret;
    }
}

额外功能运行在原始方法 之前、之后

public class Around implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
    	System.out.println("---额外功能运行在原始方法执行之前---");
        Object ret = methodInvocation.proceed(); // 原始方法运行, 获取原始方法的返回值
        System.out.println("---额外功能运行在原始方法执行之后---");
        return ret;
    }
}

额外功能运行在原始方法抛出异常的时候:

public class Around implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        Object ret = null;
        try {
            ret = methodInvocation.proceed(); // 原始方法运行, 获取原始方法的返回值
        } catch (Throwable throwable) {
            System.out.println("---额外功能运行在原始方法抛异常的时候---");
        }
        return ret;
    }
}

MethodInterceptor影响原始方法的返回值
原始方法的返回值,直接作为invoke方法的返回值返回,MethodInterceptor不会影响原始方法的返回值。

public class Around implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        System.out.println("---log---");
        Object ret = methodInvocation.proceed();
        // 原始方法返回值ret可能为true,影响后总是返回false
        return false;
    }
}

切入点详解

切入点:切入点决定额外功能的加入位置(方法)

<aop:pointcut id = "xx" expression = "execution(* *(..))">
execution()  切入点函数
* *(..)      切入点表达式
切入点表达式

1、⽅法切⼊点表达式

//修饰符 返回值 方法名 参数表
public void add(int a, int b)   -->方法
    *        * (..)		-->切入点表达式
    
*    --> 修饰符  返回值
*    --> 方法名  
( )  --> 参数表
..   --> 对于参数没有要求  
  • 定义xxx方法为切入点
<!-- 定义login作为切入点 -->
<aop:pointcut id="x" expression="execution(* login (..))"/>

<!-- 定义register作为切入点 -->
<aop:pointcut id="x" expression="execution(* register (..))"/>
  • 定义xxx方法且xxx方法有n个字符串类型的参数作为切入点
<aop:pointcut id="pc" expression="execution(* login (String,String))"/><

<!-- ⾮ java.lang java.lang 包中的类型, 必须要写全限定名 -->
<aop:pointcut id="pc" expression="execution(* register (com.bearjun.proxy.User))"/>

<!--  ..可以和具体的参数类型连用 -->
<aop:pointcut id="pc" expression="execution(* login(String, ..))"/>
<!-- === login(String), login(String,String), login(String,com.bearjun.proxy.User) -->

上面所写的切入点表达式最大的缺点是匹配不精准

  • 精准方法切入点限定
<!-- 格式:修饰符  返回值  包  类.方法(参数) -->
<aop:pointcut id="pc" expression="execution(* com.bearjun.proxy.UserServiceImpl.login(..))"/>

<aop:pointcut id="pc" expression="execution(* com.bearjun.proxy.UserServiceImpl.login(String, String))"/>

** 2、类切入点 **

指定特定类作为切入点(额外功能加入的位置),这个类中的所有方法,都会加上对应的额外功能。
语法一:

<!-- 类中的所有方法全部加入额外功能 -->
* cn.bearjun.proxy.UserServiceImpl.*(..))

语法二:

# 忽略包
1. 类只存在一级包 com.UserServiceImpl
* *.UserServiceImple.*(..)
2. 类存在多级包  com.bearjun.proxy.UserServiceImpl
* *..UserServiceImpl.*(..)

** 3、包切⼊点表达式**

指定包作为额外功能加入的位置,自然包中的所有类及其方法都会加入额外的功能。
语法1:

# 切入点包中的所有类,必须在proxy中,不能在proxy包的⼦包中
<aop:pointcut id="pc" expression="execution(* com.bearjun.proxy.*.*(..))"/>

语法2:

# 切入点当前包及其⼦包都生效
<aop:pointcut id="pc" expression="execution(* com.bearjun.proxy..*.*(..))"/>
切入点函数

切入点函数:用于执行切入点表达式
1、execution
execution是最为重要的切入点函数,功能最全;可以执行执行 方法切入点表达式类切入点表达式包切入点表达式
弊端:execution执⾏切入点表达式 ,书写麻烦

execution(* com.bearjun.proxy..*.*(..))

注意:其他切入点函数 是简化execution书写复杂度,功能上完全一致。
2、args

作用:主要用于函数(方法参数)的匹配

# 例如:切入点为方法参数必须是两个字符串类型的参数
args(String,String)
等同于
execution(* *(String, String))

3、within

作用:主要用于包、类切入点表达式的匹配

例如:切入点为UserServiceImpl这个类
within(*..UserServiceImpl)
等同于
execution(* *..UserServiceImpl.*(..))

例如:切入点为com.bearjun.proxy的包以及子包作为接入点
within(com.bearjun.proxy..*)
等同于
execution(* com.bearjun.proxy..*.*(..))
# 其实也就是把execution中的修饰符和方法名简化了,只关注包

4、@Annotation

作用:为具有特殊注解的方法加入额外功能
添加自定义注解:

//注解所在位置
@Target(ElementType.METHOD)
//注解在什么时候执行 一般都是Runtime
@Retention(RetentionPolicy.RUNTIME)
public @interface Advice{
}

配置切面

<aop:pointcut id="xx" expression="@annotation(cn.bearjun.Advice)"/>

5、切入点函数的逻辑运算

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

  • and与操作
# 案例:login同时参数为两个字符串
execution(* login(String, String))
等同于
execution(* login(..)) and args(String, String)

注意 and操作不可用于同种类型的切入点函数

  • or或操作
# 案例:register方法和login方法作为切入点
execution(* login(..)) or execution(* register(..))
0

评论区