spring 理解Spring AOP 一個簡單的約定遊戲

  應該說AOP原理是Spring技術中最難理解的一個部分,而這個約定遊戲也許會給你不少的幫助,經過這個約定遊戲,就能夠理解Spring AOP的含義和實現方法,也能幫助讀者更好地運用Spring AOP到實際的編程當中,這對於正確理解Spring AOP是十分重要的

約定規則

  代碼清單:定義Interceptor接口
package com.ssm.chapter11.game;

public interface Interceptor {

    public void before(Object obj);

    public void after(Object obj);

    public void afterReturning(Object obj);

    public void afterThrowing(Object obj);

}

 

  這裏是一個攔截接口,能夠對它建立實現類。若是使用過Spring AOP,你就會發現筆者的定義和Spring AOP定義的消息是如此相近。

  代碼清單:ProxyBeanFactory的getBean方法
package com.ssm.chapter11.game;

public class ProxyBeanFactory {
    public static <T> T getBean(T obj, Interceptor interceptor) {
        return (T) ProxyBeanUtil.getBean(obj, interceptor);
    }
}

  具體類ProxyBeanUtil的getBean方法的邏輯不須要去理會,由於這是筆者須要去完成的內容。可是做爲讀者,你要知道當使用了這個方法後,存在以下約定java

  當一個對象經過ProxyBeanFactory的getBean方法定義後,擁有這樣的約定。
  (1)Bean必須是一個實現了某一個接口的對象。
  (2)最早會執行攔截器的before方法。
  (3)其次執行Bean的方法(經過反射的形式)。
  (4)執行Bean方法時,不管是否產生異常,都會執行after方法。
  (5)執行Bean方法時,若是不產生異常,則執行afterReturning方法;若是產生異常,則執行afterThrowing方法。
  這個約定實際已經十分接近Spring AOP對咱們的約定,因此這個約定十分重要,其流程如圖所示。

 



讀者的代碼

  上面筆者給出了接口和獲取Bean的方式,同時也給出了具體的約定,這個時候讀者能夠根據約定編寫代碼,好比打印一個角色信息。
  代碼清單:RoleService接口
package com.ssm.chapter11.game.service;

import com.ssm.chapter11.game.pojo.Role;

public interface RoleService {
    public void printRole(Role role);
}

 

  代碼清單:RoleServiceImpl
package com.ssm.chapter11.game.service.impl;

import com.ssm.chapter11.game.pojo.Role;
import com.ssm.chapter11.game.service.RoleService;

public class RoleServiceImpl implements RoleService {

    // @Override
    public void printRole(Role role) {
        System.out.println("{id =" + role.getId() + ", roleName=" + role.getRoleName() + ", note=" + role.getNote() + "}");
    }

}

 

  代碼清單:角色攔截器RoleInterceptor
package com.ssm.chapter11.game.interceptor;

import com.ssm.chapter11.game.Interceptor;

public class RoleInterceptor implements Interceptor {

    // @Override
    public void before(Object obj) {
        System.out.println("--before-準備打印角色信息");
    }

    // @Override
    public void after(Object obj) {
        System.out.println("-after-已經完成角色信息的打印處理");
    }

    // @Override
    public void afterReturning(Object obj) {
        System.out.println("-afterReturning-剛剛完成打印功能,一切正常。");
    }

    // @Override
    public void afterThrowing(Object obj) {
        System.out.println("-afterThrowing-打印功能執行異常了,查看一下角色對象爲空了嗎?");
    }

}

 


  它編寫了圖中描述流程的各個方法,這個時候你能夠清楚地知道代碼將按照流程圖的流程執行。注意,你並不須要知道筆者如何實現,你只須要知道咱們之間的約定便可
  代碼清單:測試約定流程
package com.ssm.chapter11.game.main;

import com.ssm.chapter11.game.Interceptor;
import com.ssm.chapter11.game.ProxyBeanFactory;
import com.ssm.chapter11.game.interceptor.RoleInterceptor;

import com.ssm.chapter11.game.pojo.Role;
import com.ssm.chapter11.game.service.RoleService;
import com.ssm.chapter11.game.service.impl.RoleServiceImpl;

public class GameMain {

    public static void main(String[] args) {
        RoleService roleService = new RoleServiceImpl();
        Interceptor interceptor = new RoleInterceptor();
        RoleService proxy = ProxyBeanFactory.getBean(roleService, interceptor);
        Role role = new Role(1L, "role_name_1", "role_note_1");
        proxy.printRole(role);
        System.out.println("############## 測試 afterthrowing方法###############");
        role = null;
        proxy.printRole(role);
    }

}

 

筆者的代碼

  上面的代碼都基於動態代理模式。
  下面展現經過JDK動態代理實現上述流程的代碼,如代碼清單所示。
  代碼清單:使用動態代理實現流程
package com.ssm.chapter11.game;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

class ProxyBeanUtil implements InvocationHandler {

    //被代理對象
    private Object obj;
    // 攔截器
    private Interceptor interceptor = null;

    /**
     * 獲取動態代理對象.
     *
     * @param obj         被代理對象
     * @param interceptor 攔截器
     * @param aroundFlag  是否啓用around方法
     * @return 動態代理對象
     */
    public static Object getBean(Object obj, Interceptor interceptor) {
        //使用當前類,做爲代理方法,此時被代理對象執行方法的時候,會進入當前類的invoke方法裏
        ProxyBeanUtil _this = new ProxyBeanUtil();
        //保存被代理對象
        _this.obj = obj;
        //保存攔截器
        _this.interceptor = interceptor;
        //生成代理對象,並綁定代理方法
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), _this);
    }

    /**
     * 代理方法
     *
     * @param proxy  代理對象
     * @param method 當前調度方法
     * @param args   參數
     * @return 方法返回
     * @throws Throwable 異常
     */
    // @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object retObj = null;
        //是否產生異常
        boolean exceptionFlag = false;
        //before方法
        interceptor.before(obj);
        try {
            //反射原有方法
            retObj = method.invoke(obj, args);
        } catch (Exception ex) {
            exceptionFlag = true;
        } finally {
            //after方法
            interceptor.after(obj);
        }
        if (exceptionFlag) {
            //afterThrowing方法
            interceptor.afterThrowing(obj);
        } else {
            //afterReturning方法
            interceptor.afterReturning(obj);
        }
        return retObj;
    }
}

 

  上面的代碼使用了動態代理,因爲這段代碼的重要性,這裏有必要討論其實現過程。
  首先,經過getBean方法保存了被代理對象、攔截器(inter-ceptor)和參數(args),爲以後的調用奠基了基礎。而後,生成了JDK動態代理對象(proxy),同時綁定了ProxyBeanUtil的一個實例做爲其代理類,這樣當代理對象調用方法的時候,就會進入到ProxyBeanUtil實例的invoke方法中,因而焦點又到了invoke方法上。
  在invoke方法中,筆者將攔截器的方法按照流程圖實現了一遍,其中設置了異常標誌(exceptionFlag),經過這個標誌就能判斷反射原有對象方法的時候是否發生了異常,這就是讀者的代碼可以按照流程打印的緣由。可是,因爲動態代理和反射的代碼會比較抽象,更多的時候大部分的框架只會告訴你流程圖和具體的流程方法的配置,就像筆者以前只是給出約定而已,相信有心的讀者已經明白這句話的意思了,這就是說Spring框架也是這樣作的。 
 
相關文章
相關標籤/搜索