1.额外功能详解
1.MethodBeforeAdvice分析
1. MethodBeforeAdvice接⼝作⽤:额外功能运⾏在原始⽅法执⾏之前,进⾏额外功能操作。
public class Before1 implements MethodBeforeAdvice {
/*
作⽤:需要把运⾏在原始⽅法执⾏之前运⾏的额外功能,书写在before⽅法中
before方法中的三个参数:
Method: 额外功能所增加给的那个原始⽅法
login⽅法
register⽅法
showOrder⽅法
Object[]: 额外功能所增加给的那个原始⽅法的参数,和method一一对应。eg:method如果是login(String name,String password)方法,那么Object[]:String name,String password。
Object: 额外功能所增加给的那个原始对象 UserServiceImpl
*/
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("-----new method before advice log------");
}
}
2. before⽅法的3个参数在实战中,该如何使⽤。
before⽅法的参数,在实战中,会根据需要进⾏使⽤,不⼀定都会⽤到,也有可能都不⽤。
2.MethodInterceptor(⽅法拦截器)
methodinterceptor接⼝:额外功能可以根据需要运⾏在原始⽅法执⾏ 前、后、或前后都有。
public class Arround implements MethodInterceptor {
/*
invoke方法的作用:额外功能书写在invoke方法中
额外功能 :运行在原始方法之前、之后、之前之后。
确定一件事:原始方法怎么运行
参数:MethodInvocation :额外功能所增加给的那个原始方法。类似before方法的Method
invocation.proceed() 代表原始方法开始运行。
额外功能写在invocation.proceed();之前就是在原始方法之前运行额外功能。
额外功能写在invocation.proceed();之后就是在原始方法之后运行额外功能。
返回值:Object:原始方法的返回值
通过invocation.proceed()的运行进而获取到原始方法的返回值。
Object ret = invocation.proceed();
*/
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("额外功能运行在原始方法之前。");
Object ret = invocation.proceed();
System.out.println("额外功能运行在原始方法之后。");
return ret;
}
}
什么样的额外功能 运⾏在原始⽅法执⾏之前,之后都要添加:事务
额外功能运⾏在原始⽅法抛出异常的时候
public Object invoke(MethodInvocation invocation) throws Throwable {
Object ret = null;
try {
ret = invocation.proceed(); //原始方法抛出异常
} catch (Throwable throwable) {//捕获异常
System.out.println("-----原始⽅法抛出异常 执⾏的额外功能 ----");
throwable.printStackTrace();
}
return ret; }
MethodInterceptor影响原始⽅法的返回值
原始⽅法的返回值,直接作为invoke⽅法的返回值返回,MethodInterceptor不会影响原始⽅法的返回值。
public boolean login(String name,String password){
System.out.println("UserServiceImpl.login");
return true;
}
eg: public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("------log-----");
Object ret = invocation.proceed();
return ret; //返回的ret还是true
}
MethodInterceptor影响原始⽅法的返回值如何做:
Invoke⽅法的返回值,不要直接返回原始⽅法的运⾏结果即可。
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("------log-----");
Object ret =invocation.proceed();
return false; //这里返回给原始方法的返回值是false(一般不改)
}
2.切入点详解
切⼊点决定额外功能加⼊位置(⽅法)
<aop:pointcut id="pc" expression="execution(* *(..))"/>
exection(* *(..)) ---> 匹配了所有⽅法
1. execution() 切⼊点函数
2. * *(..) 切⼊点表达式
2.1 切入点表达式
1.方法切入点表达式
修饰符 返回值 方法名 参数
* *(..) --> 所有⽅法
第一个* ---> 修饰符 返回值
第二个* ---> ⽅法名
()---> 参数表
..---> 对于参数没有要求 (参数有没有,参数有⼏个都⾏,参数是什么类型的都⾏)
eg:定义login⽅法作为切⼊点
* login(..)
定义register register作为切⼊点
* register(..)
* login(String,String)
eg:定义login⽅法且login⽅法有两个字符串类型的参数 作为切⼊点
##注意:⾮java.lang 包中的类型,必须要写全限定名
* register(com.baizhiedu.proxy.User)
..可以和具体的参数类型连⽤
* login(String,..) 可以匹配第一个参数是String,后面的参数没有要求。
可以匹配如下方法:
login(String),login(String,String),login(String,com.baizhiedu.proxy.User)
上面所讲解的方法切入点表达式不精准。
eg:在两个包(a,b)中有相同方法名和参数列表的方法login,而我只想要匹配其中一个包的方法。login(String,String)会把两个包的login方法都匹配到。
2.2精准⽅法切⼊点限定
修饰符 返回值 包.类.⽅法(参数)
* com.baizhiedu.proxy.UserServiceImpl.login(..)
* com.baizhiedu.proxy.UserServiceImpl.login(String,String)
2.3 类切⼊点
指定特定类作为切⼊点(额外功能加⼊的位置),那么这个类中的所有⽅法,都会加上对应的额外功能
语法1:
#类中的所有⽅法加⼊了额外功能
* com.baizhiedu.proxy.UserServiceImpl.*(..)
语法2:
#忽略包
1. 类只存在⼀级包
com.UserServiceImpl(此时类UserServiceImpl直接在com包下)
* *.UserServiceImpl.*(..)
2. 类存在多级包
com.baizhiedu.proxy.UserServiceImpl(此时类UserServiceImpl在com.baizhiedu.proxy多级包下)
* *..UserServiceImpl.*(..) --- 多加一个点
2.4包切入点表达式(实战更多)
指定包作为额外功能加⼊的位置,那么包中的所有在配置文件中创建对象的类 以及类中的所有⽅法都会加⼊额外的功能。
语法1:
#切⼊点包中的所有类,必须在proxy包中,不能在proxy包的⼦包中
* com.baizhiedu.proxy.*.*(..)
语法2:
#切⼊点当前包及其⼦包都⽣效(在当前包后面多加一个点)
* com.baizhiedu.proxy..*.*(..)
2.5切⼊点函数
切⼊点函数:⽤于执⾏切⼊点表达式
1.execution
1. 最为重要的切⼊点函数,功能最全。
2. 执⾏⽅法切⼊点表达式、类切⼊点表达式、包切⼊点表达式
3. 弊端:execution执⾏切⼊点表达式书写麻烦
eg:execution(* com.baizhiedu.proxy..*.*(..))
4. 注意:其他的切⼊点函数 简化了execution书写复杂度,功能上完全⼀致
2.切入点函数:args
1. 作⽤:主要⽤于函数(⽅法) 参数的匹配
2. 切⼊点:⽅法参数必须得是2个字符串类型的参数:不管你是哪个包下哪个类下的 只关心参数
execution(* *(String,String)) == args(String,String)
3.切入点函数:within
# 作⽤:主要⽤于进⾏类、包切⼊点表达式的匹配 (把方法和参数取消了)
切⼊点:UserServiceImpl这个类:
1. execution(* *..UserServiceImpl.*(..)) == within(*..UserServiceImpl)
切⼊点:proxy这个包:
2. execution(* com.baizhiedu.proxy..*.*(..)) == within(com.baizhiedu.proxy..* )
4.@annotation
作⽤:为具有特殊注解的⽅法加⼊额外功能
<aop:pointcut id="pc" expression="@annotation(com.baizhiedu.Log)"/>
2.6切⼊点函数的逻辑运算
指的是 整合多个切⼊点函数⼀起配合⼯作,进⽽完成更为复杂的需求
and:与操作
案例:login方法同时 参数是2个字符串的方法
1. execution(* login(String,String)) ==
2. execution(* login(..)) and args(String,String)
3.注意:与操作不能⽤于同种类型的切⼊点函数;
eg:execution(* login(..)) and execution(* register(..))
这种写法错误,因为不存在既叫login又叫register的方法。
or:或操作
案例:register⽅法 和 login⽅法作为切⼊点
execution(* login(..)) or execution(* register(..))