额外功能的详解
1、MethodBeforeAdvice分析
- MethodBeforeAdvice接口作用:额外功能运行在原始方法执行之前,进行额外功能操作。
- 方法参数详解
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(..))
评论区