項目地址:https://gitee.com/chuyunfei/learn.git
一、AOP的概念
:面向切面編程,即將多個業務線的共有操作進行提取,集成爲一個環節,所有需要這個環節的業務可以共用這個切面
:spring對aop的支持是方法級別的支持
二、動態代理基礎
JDK動態代理-》基於接口的動態代理:
1、需要一個接口
public interface Actions {
String askName(String person) throws Exception;
}
2、需要一個實現類,也就是需要被代理的對象
public class ActionsImpl implements Actions {
private String myName = “楚雲飛”;
public String askName(String person) throws Exception{
if(person.equals(myName)){
return “myName is ” + myName;
}else {
throw new Exception(“不知道你在說啥!”);
}
}
}
3、需要一個實現代理處理器接口的代理處理器
public class ProxyHandler implements InvocationHandler {
//被代理對象,執行目標方法的實際對象
private Object target;
public ProxyHandler(Object target){
this.target = target;
}
/**
* @param proxy:鏈接Handler和target的代理對象
* @param method:要執行的方法
* @param args:執行方法的參數
* @return:返回真正的方法執行的結果
* @throws Throwable
*/
public Object invoke(Object proxy,Method method,Object[] args) throws Throwable{
//表示proxy並不是被代理的對象,所以需要保存被代理對象
System.out.println(proxy.getClass().getName());
System.out.println(“準備執行方法:” + method.getReturnType() + ” ” + method.getName() +
“(” + Arrays.toString(args) + “)”);
//真正的執行方法
Object result = method.invoke(target,args);
System.out.println(“執行方法:” + method.getReturnType() + ” ” + method.getName() +
“(” + Arrays.toString(args) + “):完成,準備返回執行的結果!”);
return result;
}
}
4、需要一個生成代理對象的工廠類
public class ProxyBeanFactory {
/**
* 獲取一個基於JDK動態代理的代理對象
* @param target
* @return
*/
public static Object getProxyBean(Object target){
return Proxy.newProxyInstance(
//一個類加載器,這裏選用傳入的對象進行加載
target.getClass().getClassLoader(),
//該對象實現的全部接口
target.getClass().getInterfaces(),
//一個實現了處理器接口的bean,不能是null:Objects.requireNonNull(handler);
new ProxyHandler(target)
);
}
}
5、需要一個測試用的類
public class JDKProxyApplication {
public static void main(String[] args) throws Exception{
Actions actions = new ActionsImpl();
Actions proxied = (Actions) ProxyBeanFactory.getProxyBean(actions);
String name = proxied.askName(“楚雲飛”);
System.out.println(name);
//output
// com.sun.proxy.$Proxy0
// 準備執行方法:class java.lang.String askName([楚雲飛])
// 執行方法:class java.lang.String askName([楚雲飛]):完成,準備返回執行的結果!
// myName is 楚雲飛
}
}
6、注意事項:
:需要被代理的對象實現接口纔可以進行代理,只能代理接口裏面的方法
:需要保存被代理的對象
CGLIB動態代理-》基於繼承的動態代理:
1、需要一個可以繼承的待代理類:
public class Actions {
private String myName = "楚雲飛";
public String askName(String person) throws Exception{
if(person.equals(myName)){
return "myName is " + myName;
}else {
throw new Exception("不知道你在說啥!");
}
}
}
2、一個Cglib的方法攔截接口的實現類:
public class CglibEnhancerProxy implements MethodInterceptor {
public Object getProxy(Class clazz){
//聲明一個enhancer對象來實現動態代理
Enhancer enhancer = new Enhancer();
//設置代理的父類,cglib代理基於繼承來實現動態代理
enhancer.setSuperclass(clazz);
//設置代理的攔截器對象實現類
enhancer.setCallback(this);
//創建一個代理對象
return enhancer.create();
}
/**
* @param object:被代理的對象
* @param method:被攔截的方法
* @param args:被攔截方法的參數
* @param methodProxy:代理的方法,用於執行被代理對象的方法
* @return:執行的真正的返回值
* @throws Throwable
*/
public Object intercept(Object object,Method method,Object[] args,MethodProxy methodProxy) throws Throwable{
System.out.println("準備執行方法:" + method.getReturnType() + " " + method.getName() +
"(" + Arrays.toString(args) + ")");
//真正的執行方法,執行父類的方法
Object result = methodProxy.invokeSuper(object,args);
System.out.println("執行方法:" + method.getReturnType() + " " + method.getName() +
"(" + Arrays.toString(args) + "):完成,準備返回執行的結果!");
return result;
}
}
3、一個測試的主類:
public class CglibProxyApplication {
public static void main(String[] args) throws Exception{
Actions action = (Actions) new CglibEnhancerProxy().getProxy(Actions.class);
String name = action.askName("楚雲飛");
System.out.println(name);
//output
// 準備執行方法:class java.lang.String askName([楚雲飛])
// 執行方法:class java.lang.String askName([楚雲飛]):完成,準備返回執行的結果!
// myName is 楚雲飛
}
}
4、注意事項:
:基於繼承的代理,所以被代理的類不能被final修飾
:同時,實體的方法也不能被final修飾,否則將無法被攔截
三、AOP的配置語法基礎
註解的方式進行配置:
1、需要一個實際接口以及用於增強類型的另外一個接口
public interface Actions {
String askName(String person) throws Exception;
}
public interface ActionPlus {
boolean match(String name);
}
2、兩個接口所對應的實現類
@Component("actionsImpl")//註冊一個bean
public class ActionsImpl implements Actions {
private String myName = "楚雲飛";
public String askName(String person) throws Exception{
if(person.equals(myName)){
return "myName is " + myName;
}else {
throw new Exception("不知道你在說啥!");
}
}
}
//這個類沒有被註冊
public class ActionPlusImpl implements ActionPlus {
public boolean match(String name){
return name != null;
}
}
3、需要一個配置類來開啓對aop的支持
@Configuration//標識其爲配置類
@ComponentScan(basePackages = "springaop.aop.annotion")//包掃描
@EnableAspectJAutoProxy//開啓對AOP的支持
public class RootConfig {
}
4、需要一個切面
@Aspect
/**
* 這是一個切面:
* 1、@Aspect 是三方的註解,該註解不會將這個切面註冊爲一個bean,如果要將其註冊爲一個bean的話,需要配置或者運用註解將其註冊到spring容器裏面
*/
@Component
//將這個切面註冊爲一個bean
@Order(1)
//當多個切面的時候可以規定切面執行的順序,實行責任鏈模式
public class ActionsAspect {
/**
* 對已經存在的類進行動態的增強:引入
* 注意:AOP無法對引入的方法再次進行攔截,也就是說無法對aop生成的動態代理方法進行攔截
*/
@DeclareParents(
//對那個類型進行增強,應該是一個具體的實現類,下面的表達式表示爲 ActionsImpl+ 增加一個接口的功能
value = "springaop.aop.annotion.service.actions.impl.ActionsImpl+"
//上面一個增加功能的默認實現類是這個屬性所配置的類來實現
,defaultImpl = ActionPlusImpl.class
)
public ActionPlus actionPlus;
/**
* 各個通知的執行順序:
* around->before->after->around->afterReturning/afterThrowing
*/
/**
* @Pointcut 用於定義一個切點,使用一個方法來提供依附點,不然沒有地方打註解
* execution:在方法執行時會觸發這個攔截動作
* *:方法的返回值不限
* springaop.aop.annotion.service.actions.impl.ActionsImpl:要攔截的方法的類的全限定名稱
* askName:要攔截的方法名稱
* (..):方法的參數不限
* 含義:
* 當springaop.aop.annotion.service.actions.impl.ActionsImpl#askName方法執行時,觸發攔截動作,無論這個方法有幾個
* 重載的方法,無論它的返回值是什麼類型,我都要攔截
*/
@Pointcut(value = "execution(* springaop.aop.annotion.service.actions.impl.ActionsImpl.askName(..))")
public void pointCut0(){}
/**
* 前置通知:執行目標方法之前
*/
@Before(value = "pointCut0()")
public void beforePointCut0(JoinPoint joinPoint){
System.out.println("pointCut0 前置通知:執行" +
joinPoint.getSignature().getName() + "(" + Arrays.toString(joinPoint.getArgs()) + ")"
+ "方法之前!");
}
/**
* 後置通知:執行目標方法之後
*/
@After("pointCut0()")
public void afterPointCut0(JoinPoint joinPoint){
System.out.println("pointCut0 後置通知:執行" +
joinPoint.getSignature().getName() + "(" + Arrays.toString(joinPoint.getArgs()) + ")"
+ "方法之後!");
}
/**
* 後置返回通知:在目標方法返回執行的目標值之後
* result用於指定方法執行結果的參數名稱
*/
@AfterReturning(value = "pointCut0()",returning = "result")
public void afterReturningPointCut0(JoinPoint joinPoint,Object result){
System.out.println("pointCut0 後置通知:執行" +
joinPoint.getSignature().getName() + "(" + Arrays.toString(joinPoint.getArgs()) + ")"
+ "方法正確返回之後的結果是:" + result);
}
/**
* 後置異常通知:在執行目標方法出現異常之後
* @param joinPoint:鏈接點,用於獲取一些必要信息
* @param exception
*/
@AfterThrowing(value = "pointCut0()",throwing = "exception")
public void afterThrowingPointCut0(JoinPoint joinPoint,Exception exception){
System.out.println("pointCut0 後置通知:執行" +
joinPoint.getSignature().getName() + "(" + Arrays.toString(joinPoint.getArgs()) + ")"
+ "方法發生異常: " + exception.getMessage());
}
/**
* 環繞通知相當於前置通知和後置通知合併一起,但是卻更加強大:
* 1、環繞通知可以影響、操控被攔截方法的執行,所以可以篡改方法的執行結果
* 2、環繞通知可以對執行方法的異常進行處理
* 3、環繞通知可以獲取連接點的相關信息
* @param joinPoint(ProceedingJoinPoint):鏈接點,也就是待執行的方法
* @return:該方法執行的真正的結果
*/
@Around("pointCut0()")
public Object aroundPointCut0(ProceedingJoinPoint joinPoint){
System.out.println("pointCut0 環繞通知:執行" + joinPoint.getSignature().getName() + "方法之前-------");
Object result = null;
try{
//真正的執行被攔截的方法
result = joinPoint.proceed();
System.out.println("pointCut0 環繞通知:執行" + joinPoint.getSignature().getName() + "方法之後-------");
}catch(Throwable throwable){
//可以進行異常處理
throwable.printStackTrace();
System.out.println("pointCut0 環繞通知:執行" + joinPoint.getSignature().getName() + "方法出現異常之後-------");
}
System.out.println("pointCut0 環繞通知:執行" + joinPoint.getSignature().getName() + "方法之後返回執行的結果-------");
//返回執行的結果
return result;
}
/**
* 唯一的確定一個唄攔截的方法
* 類springaop.aop.annotion.service.actions.impl.ActionsImpl 的一個返回值類型爲String 且參數類型爲String 的askName方法
*/
@Pointcut("execution(String springaop.aop.annotion.service.actions.impl.ActionsImpl.askName(String))")
public void pointCut1(){}
/**
* @param joinPoint
* @param person 可以使用 args(person) 將連接點的參數直接傳入進來,但是如果傳入鏈接點的話將沒有必要了
*/
@Before("execution(String springaop.aop.annotion.service.actions.impl.ActionsImpl.askName(String))" +
"&& args(person)")
public void beforePointCut1(JoinPoint joinPoint,String person){
System.out.println("pointCut1 前置通知:執行" +
joinPoint.getSignature().getName() + "(" + Arrays.toString(joinPoint.getArgs()) + ")"
+ "方法之前!同時直接傳入的參數爲:" + person);
}
@Around("pointCut1()")
public Object aroundPointCut1(ProceedingJoinPoint joinPoint){
System.out.println("pointCut1 環繞通知:執行" + joinPoint.getSignature().getName() + "方法之前-------");
Object result = null;
try{
//真正的執行被攔截的方法
result = joinPoint.proceed();
System.out.println("pointCut1 環繞通知:執行" + joinPoint.getSignature().getName() + "方法之後-------");
}catch(Throwable throwable){
//可以進行異常處理
throwable.printStackTrace();
System.out.println("pointCut1 環繞通知:執行" + joinPoint.getSignature().getName() + "方法出現異常之後-------");
}
System.out.println("pointCut1 環繞通知:執行" + joinPoint.getSignature().getName() + "方法之後返回執行的結果-------");
//返回執行的結果
return result;
}
/**
* 表示springaop.aop.annotion.service.actions包的所有字包(直接子包,之間沒有其他目錄級別)裏面的所有類的所有方法都會
* 被攔截,無論參數列表和返回值類型
*/
@Pointcut("execution(* springaop.aop.annotion.service.actions.*.*(..))")
public void pointCut2(){}
@Around("pointCut2()")
public Object aroundPointCut2(ProceedingJoinPoint joinPoint){
System.out.println("pointCut2 環繞通知:執行" + joinPoint.getSignature().getName() + "方法之前-------");
Object result = null;
try{
//真正的執行被攔截的方法
result = joinPoint.proceed();
System.out.println("pointCut2 環繞通知:執行" + joinPoint.getSignature().getName() + "方法之後-------");
}catch(Throwable throwable){
//可以進行異常處理
throwable.printStackTrace();
System.out.println("pointCut2 環繞通知:執行" + joinPoint.getSignature().getName() + "方法出現異常之後-------");
}
System.out.println("pointCut2 環繞通知:執行" + joinPoint.getSignature().getName() + "方法之後返回執行的結果-------");
//返回執行的結果
return result;
}
/**
* 1、@Pointcut("execution(* springaop.aop.annotion.service.actions..*(..))")
* pringaop.aop.annotion.service.actions 包及其子包(兩個點,無論之間多少目錄級別)裏面的所有類的所有方法,無論參數
* 類型和返回值類型,將都會被攔截
* 2、@Pointcut("args(String)")
* 所有參數表爲(String)的方法將都會被攔截下來
* 3、@Pointcut("target(springaop.aop.annotion.service.actions.impl.ActionsImpl)")
* 所有該類型的所有方法將會被攔截下來(父類(子類對象即爲父類對象)和實現的接口的方法也將被攔截)
* 4、@Pointcut("@target(org.springframework.stereotype.Component)")
* 所有帶有這個註解的類的所有方法將被攔截
* 5、@Pointcut("within(springaop.aop.annotion.service..*)")
* 所有在springaop.aop.annotion.service包及其子包(兩個點,當一個點的時候和1是一樣的道理)裏面的類的所有方法將被攔截
* 6、@Pointcut("@within(org.springframework.stereotype.Component)")
* 匹配指定的註解類型的類型的所有方法將被攔截
* 7、@Pointcut("@annotation()")
* 註解指定註解的方法將被攔截
*/
@Pointcut("execution(* springaop.aop.annotion.service..*(..))")
public void pointCut3(){}
/**
* 多個 “且” 鏈接點使用 && 進行連接,但是在xml裏面 & 是一個特殊字符,所以用 and 來代替
* 多個 “或” 鏈接點使用 || 進行連接,但是在xml裏面 | 是一個特殊字符,所以用 or 來代替
* 多個 “非” 鏈接點使用 ! 進行連接,但是在xml裏面 ! 是一個特殊字符,所以用 not 來代替
* @param joinPoint
* @return
*/
@Around("pointCut3() && pointCut2() || pointCut1() && pointCut0()")
public Object aroundPointCut3s(ProceedingJoinPoint joinPoint){
System.out.println("pointCut3 環繞通知:執行" + joinPoint.getSignature().getName() + "方法之前-------");
Object result = null;
try{
//真正的執行被攔截的方法
result = joinPoint.proceed();
System.out.println("pointCut3 環繞通知:執行" + joinPoint.getSignature().getName() + "方法之後-------");
}catch(Throwable throwable){
//可以進行異常處理
throwable.printStackTrace();
System.out.println("pointCut3 環繞通知:執行" + joinPoint.getSignature().getName() + "方法出現異常之後-------");
}
System.out.println("pointCut3 環繞通知:執行" + joinPoint.getSignature().getName() + "方法之後返回執行的結果-------");
//返回執行的結果
return result;
}
}
5、需要一個測試的主類
public class AopApplication {
public static void main(String[] args) throws Exception{
//加載註解配置的spring容器
ApplicationContext context = new AnnotationConfigApplicationContext(RootConfig.class);
//獲取指定的bean
Actions actions = (Actions) context.getBean("actionsImpl");
//不會拋異常,正常的退出
String name = actions.askName("楚雲飛");
//使用AOP爲已經存在的實例增強功能增加接口
ActionPlus actionPlus = (ActionPlus) actions;
//無法對這個方法的執行進行再次攔截
actionPlus.match("楚雲飛");
//拋異常,異常退出
name = actions.askName("石冬梅");
// pointCut0 環繞通知:執行askName方法之前-------
// pointCut1 環繞通知:執行askName方法之前-------
// pointCut2 環繞通知:執行askName方法之前-------
// pointCut3 環繞通知:執行askName方法之前-------
// pointCut0 前置通知:執行askName([楚雲飛])方法之前!
// pointCut1 前置通知:執行askName([楚雲飛])方法之前!同時直接傳入的參數爲:楚雲飛
// pointCut3 環繞通知:執行askName方法之後-------
// pointCut3 環繞通知:執行askName方法之後返回執行的結果-------
// pointCut2 環繞通知:執行askName方法之後-------
// pointCut2 環繞通知:執行askName方法之後返回執行的結果-------
// pointCut1 環繞通知:執行askName方法之後-------
// pointCut1 環繞通知:執行askName方法之後返回執行的結果-------
// pointCut0 環繞通知:執行askName方法之後-------
// pointCut0 環繞通知:執行askName方法之後返回執行的結果-------
// pointCut0 後置通知:執行askName([楚雲飛])方法之後!
// pointCut0 後置通知:執行askName([楚雲飛])方法正確返回之後的結果是:myName is 楚雲飛
// pointCut0 環繞通知:執行askName方法之前-------
// pointCut1 環繞通知:執行askName方法之前-------
// pointCut2 環繞通知:執行askName方法之前-------
// pointCut3 環繞通知:執行askName方法之前-------
// pointCut0 前置通知:執行askName([石冬梅])方法之前!
// pointCut1 前置通知:執行askName([石冬梅])方法之前!同時直接傳入的參數爲:石冬梅
// java.lang.Exception: 不知道你在說啥!
// pointCut3 環繞通知:執行askName方法出現異常之後-------
// pointCut3 環繞通知:執行askName方法之後返回執行的結果-------
// pointCut2 環繞通知:執行askName方法之後-------
// pointCut2 環繞通知:執行askName方法之後返回執行的結果-------
// pointCut1 環繞通知:執行askName方法之後-------
// pointCut1 環繞通知:執行askName方法之後返回執行的結果-------
// pointCut0 環繞通知:執行askName方法之後-------
// pointCut0 環繞通知:執行askName方法之後返回執行的結果-------
// pointCut0 後置通知:執行askName([石冬梅])方法之後!
// pointCut0 後置通知:執行askName([石冬梅])方法正確返回之後的結果是:null
}
}
xml的方式進行配置
1、其他一樣,切面進行改變一下
@Component//除了將其註冊爲一個bean以外無其他任何東西,在配置文件裏面將這個切面進行織入
public class ActionsAspect {
public void beforePointCut0(JoinPoint joinPoint,String person){
System.out.println("pointCut0 前置通知:執行" +
joinPoint.getSignature().getName() + "(" + Arrays.toString(joinPoint.getArgs()) + ")"
+ "方法之前:傳入的參數爲:" + person);
}
public void afterPointCut0(JoinPoint joinPoint){
System.out.println("pointCut0 後置通知:執行" +
joinPoint.getSignature().getName() + "(" + Arrays.toString(joinPoint.getArgs()) + ")"
+ "方法之後!");
}
public void afterReturningPointCut0(JoinPoint joinPoint,Object result){
System.out.println("pointCut0 後置通知:執行" +
joinPoint.getSignature().getName() + "(" + Arrays.toString(joinPoint.getArgs()) + ")"
+ "方法正確返回之後的結果是:" + result);
}
public void afterThrowingPointCut0(JoinPoint joinPoint,Exception exception){
System.out.println("pointCut0 後置通知:執行" +
joinPoint.getSignature().getName() + "(" + Arrays.toString(joinPoint.getArgs()) + ")"
+ "方法發生異常: " + exception.getMessage());
}
public Object aroundPointCut0(ProceedingJoinPoint joinPoint){
System.out.println("pointCut0 環繞通知:執行" + joinPoint.getSignature().getName() + "方法之前-------");
Object result = null;
try{
//真正的執行被攔截的方法
result = joinPoint.proceed();
System.out.println("pointCut0 環繞通知:執行" + joinPoint.getSignature().getName() + "方法之後-------");
}catch(Throwable throwable){
//可以進行異常處理
throwable.printStackTrace();
System.out.println("pointCut0 環繞通知:執行" + joinPoint.getSignature().getName() + "方法出現異常之後-------");
}
System.out.println("pointCut0 環繞通知:執行" + joinPoint.getSignature().getName() + "方法之後返回執行的結果-------");
//返回執行的結果
return result;
}
}
2、配置文件用來配置切面
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:cache="http://www.springframework.org/schema/cache"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-4.0.xsd
http://www.springframework.org/schema/cache
http://www.springframework.org/schema/cache/spring-cache-4.0.xsd">
<!--開啓aop的自動代理,相當於 @EnableAspectJAutoProxy 註解,開啓對aop的支持-->
<aop:aspectj-autoproxy/>
<!--配置包掃描,掃描xml配置的類包-->
<context:component-scan base-package="springaop.aop.xml"/>
<!--配置一個切面bean-->
<bean name="actionsAspect" class="springaop.aop.xml.aspects.ActionsAspect"/>
<!--配置aop的根節點-->
<aop:config>
<!--配置一個切面並指定其順序-->
<aop:aspect ref="actionsAspect" order="1">
<!--對一個類型進行增強-->
<aop:declare-parents types-matching="springaop.aop.xml.service.actions.impl.ActionsImpl+"
implement-interface="springaop.aop.xml.service.actionsplus.ActionPlus"
default-impl="springaop.aop.xml.service.actionsplus.impl.ActionPlusImpl"/>
<!--定義一個切點-->
<aop:pointcut id="actionsAskName" expression="execution(* springaop.aop.xml.service.actions.impl.ActionsImpl.askName(..))"/>
<!--定義攔截的方法-->
<aop:before method="beforePointCut0" pointcut="execution(* springaop.aop.xml.service.actions.impl.ActionsImpl.askName(..)) and args(person)"/>
<aop:after method="afterPointCut0" pointcut-ref="actionsAskName"/>
<aop:after-returning method="afterReturningPointCut0" pointcut-ref="actionsAskName" returning="result"/>
<aop:after-throwing method="afterThrowingPointCut0" pointcut-ref="actionsAskName" throwing="exception"/>
<aop:around method="aroundPointCut0" pointcut-ref="actionsAskName"/>
</aop:aspect>
</aop:config>
</beans>
3、一個測試的主類
public class AopApplication {
public static void main(String[] args) throws Exception{
//加載XML配置的spring容器
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:springaop/applicationContext-aop.xml");
//獲取指定的bean
Actions actions = (Actions) context.getBean("actionsImpl");
//不會拋異常,正常的退出
String name = actions.askName("楚雲飛");
//測試增強的方法
ActionPlus actionPlus = (ActionPlus) actions;
actionPlus.match("楚雲飛");
//拋異常,異常退出
name = actions.askName("石冬梅");
// pointCut0 前置通知:執行askName([楚雲飛])方法之前:傳入的參數爲:楚雲飛
// pointCut0 環繞通知:執行askName方法之前-------
// pointCut0 環繞通知:執行askName方法之後-------
// pointCut0 環繞通知:執行askName方法之後返回執行的結果-------
// pointCut0 後置通知:執行askName([楚雲飛])方法正確返回之後的結果是:myName is 楚雲飛
// pointCut0 後置通知:執行askName([楚雲飛])方法之後!
// pointCut0 前置通知:執行askName([石冬梅])方法之前:傳入的參數爲:石冬梅
// pointCut0 環繞通知:執行askName方法之前-------
// java.lang.Exception: 不知道你在說啥!
// pointCut0 環繞通知:執行askName方法出現異常之後-------
// pointCut0 環繞通知:執行askName方法之後返回執行的結果-------
// pointCut0 後置通知:執行askName([石冬梅])方法正確返回之後的結果是:null
// pointCut0 後置通知:執行askName([石冬梅])方法之後!
}
}