簡單實現類似Spring的Aop原理實現

Somersames發表於2017-04-23

關於的Aop:
用處:可以用在連線資料庫的方面,比如每一次在連線資料庫的時候都需要手動新建一個連線,然後執行資料庫操作,最後再來關閉資料庫的連線來避免資源的消耗。這時候有人就在想那麼可不可以讓系統在我們每一次執行資料庫操作的時候都自動的新建一個連線然後當我們執行完資料庫的連線之後再自動的關閉連線呢。
這裡就需要一個資料庫

Aop的原理例子(會使用到cglib動態代理):
例子:
首先新建三個註解

//定義在類上面標明該類是一個切點
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAspect {
}
// 前置通知註解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Before {
    String value();
}

//後置通知註解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface After {
    String value();
}

新建一個歌曲類

public class Music {
    public void sing(String str){
        System.out.println(str+"唱歌");
    }
}

當然,有人唱個歌就得有人做開始前的準備,例如唱歌前的準備,於是新建一個類表示唱歌前的準備:

@MyAspect // 表明這是一個切點類
public class Player {
    @Before("reflec.aop.cglibtest.Music.sing()")  // 前置通知,當呼叫sing方法被呼叫的時候該方法會被在它之前呼叫
    public void beforeSing() {
        System.out.println("開始唱歌前的準備");
    }

    @After("reflec.aop.cglibtest.Music.sing()") // 同理,在呼叫sing方法之後再來呼叫該方法
    public void afterSing() {
        System.out.println("唱完之後開始評分");
    }
}

那麼當呼叫sing方法的時候我們怎樣呼叫這兩個方法呢,即怎樣織入這兩個方法
這裡就得考慮Cglib動態代理了,cglib依賴asm包,在目標類的基礎上生成一個子類,然後通過子類來實現在目標方法呼叫的時候實現前置或者後置通知。。關於Cglib的這種,我感覺應該是屬於編譯器織入,因為是通過子類生成位元組碼然後進行呼叫。

建立代理類:
這個類的作用只要是通過動態代理來實現那兩個方法的執行

public class CGLIBProxy implements MethodInterceptor {
    private Object target;
    private ProxyUtil proxyUtil ;
    public CGLIBProxy(Object target) throws ClassNotFoundException {
        this.target = target;
        proxyUtil =new ProxyUtil();
    }

    public <T> T getProxy(){
        return (T) new Enhancer().create(this.target.getClass(),this);
    }
    public <T> T getProxy(Class<?> clazz){
        return (T) new Enhancer().create(this.target.getClass(),this);
    }
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        ProxyEntity proxyEntity =new ProxyEntity(proxy,this.target.getClass(),obj,method,args);
        return proxyUtil.generateEntity(proxyEntity);
    }

建立反射類:(即主要是通)

public class Reflect {
    Map<String,String> map ;   //存入的是方法名以及其註解
    Map<String,String> clazzMap;
    public Reflect() throws ClassNotFoundException {
        map=new HashMap<>();
        clazzMap =new HashMap<>();
        getAnnotationClass();
    }

    public Map<String, String> getMap() {  // 這裡返回的是已經全部存好的map方面ProxyUtil使用
        return map;
    }
    @Test
    public  void getAnnotationClass() throws ClassNotFoundException {
        String clazzName="reflec.aop.cglibtest.Player";
        Class<?>  clazz =Class.forName(clazzName,false,Thread.currentThread().getContextClassLoader());   // 這裡為了省事直接動態載入了該類
        if (clazz.isAnnotationPresent(MyAspect.class)) {  //假設是註解類
            Method[] methods =clazz.getDeclaredMethods();   //遍歷方法
            for (Method method :methods) {
                if (method.isAnnotationPresent(Before.class)) {  // 獲取註解
                   Before before =method.getAnnotation(Before.class); 
                    String beforeValue=before.value();  // 獲取註解的值以及當前類的名字方面呼叫方法
                    map.put(method.getName()+ "-"+clazzName+"-"+"before",beforeValue.substring(0,beforeValue.length()-2));
                    // 存入的是方法名和註解名以及執行的順序,這裡為了省事直接就在後面寫了
                if (method.isAnnotationPresent(After.class)) {
                    After after =method.getAnnotation(After.class); /
                    String afterValue=after.value();
                    map.put(method.getName()+ "-"+clazzName+"-"+"after",afterValue.substring(0,afterValue.length()-2));
                }
            }
        }
    }

建立處理這個Cglib裡面的MethodInterceptor介面中的intercept方法的具體類

public class ProxyUtil {

    Reflect reflect;

    public ProxyUtil() throws ClassNotFoundException {
        reflect = new Reflect();
    }

    public void getMethod(String name) {
        Map<String, String> map = new HashMap<>();

    }

    //該方法負責代理
    public Object generateEntity(ProxyEntity proxyEntity) throws Throwable {
        String proxyMethodValue = proxyEntity.getMethod().toString().substring(proxyEntity.getMethod().toString().lastIndexOf(" ") + 1, proxyEntity.getMethod().toString().indexOf("("));
        Map<String, String> methodMap = reflect.getMap();
        for (Map.Entry<String, String> map : methodMap.entrySet()) {
            if (map.getValue().equals(proxyMethodValue)) {
                String[] str = mapKeyDivision(map.getKey());
                if (str[2].equals("before")) {
                    Class<?> clazz = Class.forName(str[1], false, Thread.currentThread().getContextClassLoader()); // 載入該類
                    Method method = clazz.getDeclaredMethod(str[0]);
                    method.invoke(clazz.newInstance(), null); // 反射呼叫方法
                }
            }
        }
        return doAfter(proxyEntity,methodMap); // 處理後置通知
    }
    private Object  doAfter(ProxyEntity proxyEntity,Map<String,String> map) throws Throwable {
        Object object = proxyEntity.getMethodProxy().invokeSuper(proxyEntity.getObject(), proxyEntity.getArgs());  // 呼叫方法
        String proxyMethodValue = proxyEntity.getMethod().toString().substring(proxyEntity.getMethod().toString().lastIndexOf(" ") + 1, proxyEntity.getMethod().toString().indexOf("("));
        for(Map.Entry<String,String> aMap:map.entrySet()){
            if (aMap.getValue().equals(proxyMethodValue)){
                String[] str =mapKeyDivision(aMap.getKey());
                    if(str[2].equals("after")){
                        Class<?> clazz = Class.forName(str[1], false, Thread.currentThread().getContextClassLoader()); // 載入該類
                        Method method = clazz.getDeclaredMethod(str[0]);
                        method.invoke(clazz.newInstance(), null); // 這一步需要原始的類
                    }
                }
            }
        return object;
    }
//分解map裡面的鍵,因為裡面存入了方法和類名以及執行順序
    private String[] mapKeyDivision(String value) {
        String[] str = new String[10];
        str[0] = value.substring(0, value.indexOf("-"));  //註解下面的方法
        str[1] = value.substring(value.indexOf("-") + 1, value.lastIndexOf("-")); //註解所在的類
        str[2]=value.substring(value.lastIndexOf("-")+1,value.length()); //是before還是after
        return str;
    }

最後是一個bean

public class ProxyEntity {
    private final MethodProxy methodProxy;
    private final Class<?> clazz;
    private final Object object;
    private final Method method;
    private final Object[] args;

    public Object getObject() {
        return object;
    }

    public Object[] getArgs() {
        return args;
    }

    public Class<?> getClazz() {
        return clazz;
    }

    public Method getMethod() {
        return method;
    }

    public ProxyEntity(MethodProxy methodProxy, Class<?> clazz, Object object, Method method, Object[] args) {
        this.methodProxy = methodProxy;
        this.clazz = clazz;
        this.object = object;
        this.method = method;
        this.args = args;
    }

    public MethodProxy getMethodProxy() {
        return methodProxy;
    }
}

最後進行測試:

public class CglibTest {

public static void main(String args[]) throws ClassNotFoundException {
    Music music = new Music();
    CGLIBProxy cglibProxy = new CGLIBProxy(music);
    ((Music)cglibProxy.getProxy()).sing("測試的人 ");
}
}

測試結果:
開始唱歌前的準備
唱歌測試的人
唱完之後開始評分

在這個測試中並沒有呼叫Player類裡面的方法卻在執行的時候自動的執行了,這個例子執行簡單的模仿了下Spring的AOP,其實還有好多地方都沒有顧及到

這個類的思路就是先通過反射獲取到切點類,然後將用註解標註的方法名以及註解裡面的值存入一個map,最後在建立一個類用來處理map

Github地址:https://github.com/Somersames…

相關文章