自己實現的一個簡易Spring框架(IoC+AOP)

IoC和AOP是Spring的核心,可以說沒有他們就沒有龐大的Spring家族。我也是心血來潮,自己動手寫了一個簡易的Spring框架。可以通過使用註解來實現IoC容器和AOP。

先說IoC部分吧。源碼下載:http://download.csdn.net/detail/jobsandczj/9841126

IoC

先定義了兩個註解@MyBean和@MyAutowired,用來標記Bean和自動注入的對象。

package mySpring.autowired;

import java.lang.annotation.*;

/**
 * Created by 10033 on 2017/5/9.
 */
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface MyBean {
    String value();
}

package mySpring.autowired;

import java.lang.annotation.*;

@Target({ElementType.FIELD,  ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited  
@Documented 
public @interface MyAutowired {
}

實現思路

我的思路是在一個properties文件裏配置要掃描的包(Resource定位),通過掃描這些包獲得他們的Class對象存入List中(載入和解析),將list中的Class對象轉儲到Map中,以@Bean裏配置的Bean名爲key(註冊),再通過反射將Map裏的Class轉換成Bean(注入)。當然,注入的時候要判斷是否循環依賴,這個我是在注入過程中判斷的,也可以預判,但可能會稍麻煩些。

下面是自動注入類的代碼:

package mySpring.autowired;

/**
 * Created by 10033 on 2017/5/9.
 */

import java.io.IOException;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 自動注入類
 */
public class AutomaticInjection {

    public static void automaticInjection(String key, Map mmp) {
        try {
            List<Class> list = GetClass.getClassList(key);

            for(Class classes:list) {
                //註冊
                Map<String, Object> judgeMap = new HashMap();
                //注入
                injection(mmp,classes,judgeMap);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //注入並判斷是否循環依賴
    private static void injection(Map mmp, Class classes, Map judgeMap)
            throws Exception {
        boolean isExist = classes.isAnnotationPresent(MyBean.class);
        //如果該註解存在
        if(isExist) {
            MyBean myBean = (MyBean) classes.getAnnotation(MyBean.class);
            String beanName= myBean.value(); //獲得bean名稱
            if(null==judgeMap.get(beanName))
                judgeMap.put(beanName,true);
            else { //又返回依賴他
                throw new Exception("循環依賴");
            }

            if(null==mmp.get(beanName)) { //還沒有被注入
                Object beanObj=classes.newInstance(); //獲得bean實例

                Field[] fields=classes.getDeclaredFields();
                boolean fieldExist;
                for(Field field:fields) {
                    fieldExist=field.isAnnotationPresent(MyAutowired.class);

                    if(fieldExist) {
                        String classtype=field.getGenericType().toString();
                        Class fieldClass=Class.forName(classtype.substring(6));

                        //強制設置值 破壞了封裝性
                        field.setAccessible(true);

                        if(fieldClass.isAnnotationPresent(MyBean.class)) {//該屬性依賴其它Bean
                            MyBean tbean = (MyBean) fieldClass.getAnnotation(MyBean.class);
                            injection(mmp,fieldClass,judgeMap);
                            field.set(beanObj, mmp.get(tbean.value()));

                        }

                        else { //該屬性不依賴其它Bean
                            Object object=fieldClass.newInstance();
                            field.set(beanObj, object);
                        }
                    }
                }
                mmp.put(beanName, beanObj);
            }

        }
    }

    public static void reinjection(Map mmp, Class classes, Object obj) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        Field[] fields=classes.getDeclaredFields();
        boolean fieldExist;
        for(Field field:fields) {
            fieldExist=field.isAnnotationPresent(MyAutowired.class);

            if(fieldExist) {
                String classtype=field.getGenericType().toString();
                Class fieldClass=Class.forName(classtype.substring(6));
                field.setAccessible(true);
                //強制設置值 破壞了封裝性
                field.setAccessible(true);

                if(fieldClass.isAnnotationPresent(MyBean.class)) {//該屬性依賴其它Bean
                    MyBean tbean = (MyBean) fieldClass.getAnnotation(MyBean.class);
                    field.set(obj, mmp.get(tbean.value()));

                }else { //該屬性不依賴其它Bean
                    Object object=fieldClass.newInstance();
                    field.set(obj, object);
                }
            }
        }
    }

}
接下來說AOP。

AOP

AOP我是選擇用CGLIB實現的。先是定義了兩個註解。@PointCut,@Ignore。

package mySpring.aop;

import java.lang.annotation.*;

/**
 * Created by 10033 on 2017/5/12.
 */
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface PointCut {
    String value();
}
package mySpring.aop;

import java.lang.annotation.*;

/**
 * Created by 10033 on 2017/5/12.
 */
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Ignore {
}

我只實現了三種通知,Before,After,Surround(前面兩種結合,Spring裏沒有這種。。)。爲每種通知定義一個接口,每個接口都繼承Advice(空接口)。

實現原理

AOP是在完成上述IoC注入後再實現的。就是爲每個Bean生成一個代理類,根據註解信息提供相應的方法攔截操作。Spring的AOP是會形成一條攔截器鏈的,我沒有做得那麼複雜。我寫了一個控制類來進行切面信息判斷來實現正確的攔截(替代攔截器鏈),這個控制器會根據註解選擇正確的操作執行。我將操作也獨立成了一個類,進行解耦。

下面是控制類:

package mySpring.aop;

/**
 * Created by 10033 on 2017/5/12.
 */

import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * 通過註解判斷執行哪個通知
 */
public class ProxyController {

    //沒有類註解
    public static Object doController
            (Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        //有忽視註解
        if(method.isAnnotationPresent(Ignore.class))
            return methodProxy.invokeSuper(o, objects);
        //沒有切入點
        if(!method.isAnnotationPresent(PointCut.class)) {
            return methodProxy.invokeSuper(o, objects);
        }else { //有切入點
            Advice advice=getAdvice(method);
            return doAdvice(o,objects,methodProxy,advice);
        }

    }

    //有類註解
    public static Object doController
        (Object o, Method method, Object[] objects, MethodProxy methodProxy, Advice advice) throws Throwable {
        //有忽視註解
        if(method.isAnnotationPresent(Ignore.class))
            return methodProxy.invokeSuper(o, objects);
        //有切入點
        if(method.isAnnotationPresent(PointCut.class)) {
            Advice advice2=getAdvice(method);
            return doAdvice(o,objects,methodProxy,advice2);
        } else { //沒有切入點
            return doAdvice(o,objects,methodProxy,advice);
        }

    }

    private static Object doAdvice(Object o, Object[] objects, MethodProxy methodProxy, Advice advice) throws Throwable {
        if(advice instanceof AfterAdvice) {
            return Execute.executeAfter(o,objects,methodProxy, (AfterAdvice) advice);
        }else if(advice instanceof BeforeAdvice) {
            return Execute.executeBefore(o,objects,methodProxy, (BeforeAdvice) advice);
        }else if(advice instanceof SurroundAdvice) {
            return Execute.executeSurround(o,objects,methodProxy, (SurroundAdvice) advice);
        }
        return null;
    }

    private static Advice getAdvice(Method method) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        String classPath=method.getAnnotation(PointCut.class).value();
        Advice advice= (Advice) Class.forName(classPath).newInstance();
        return advice;
    }
}
下面是具體操作執行類

package mySpring.aop;

import net.sf.cglib.proxy.MethodProxy;

/**
 * Created by 10033 on 2017/5/12.
 * 執行通知
 */
public class Execute {
    public static Object executeAfter
            (Object o, Object[] objects, MethodProxy methodProxy, AfterAdvice advice) throws Throwable {
        Object object=methodProxy.invokeSuper(o,objects);
        advice.after();
        return object;
    }
    public static Object executeBefore
            (Object o, Object[] objects, MethodProxy methodProxy, BeforeAdvice advice) throws Throwable {
        advice.before();
        Object object=methodProxy.invokeSuper(o,objects);

        return object;
    }
    public static Object executeSurround
            (Object o, Object[] objects, MethodProxy methodProxy, SurroundAdvice advice) throws Throwable {
        advice.before();
        Object object=methodProxy.invokeSuper(o,objects);
        advice.after();
        return object;
    }
}

執行完AOP後,我們要重新進行一遍注入,這就是上面那個自動注入類的reinjection方法要做的事。

啓動Spring也非常簡單,只要通過Class.forName("mySpring.autowired.BeanFactory");加載類就行。當然這麼做有一個壞處就是減少了靈活性,配置文件必須按照嚴格規範。

下面是BeanFactory類:

package mySpring.autowired;


import mySpring.aop.ProxyFactory;

import java.util.HashMap;
import java.util.Map;

/**
 * Created by 10033 on 2017/5/9.
 */
public class BeanFactory {
    public static Map<String, Object> map=new HashMap();
    private static final String KEY="scan.package";
    //初始化IoC容器
    static {
        AutomaticInjection.automaticInjection(KEY,map);
        ProxyFactory.makeProxyBean(map);

        //生成代理後重新注入
        for(String key:map.keySet()) {
            Class c=map.get(key).getClass().getSuperclass();
            try {
                AutomaticInjection.reinjection(map,c,map.get(key));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    public static Object getBean(String name) {
        return map.get(name);
    }
}
獻醜了,但也算了了自己寫一個簡易Spring的心願,很多地方有待改進。望大牛指出。

對了,寫的時候遇到一個問題,那就是CGLIB生成的子類無法獲取到它父類的屬性,也因爲這個瞎忙活了很久,希望有人能爲我解答。


上面的問題已得到解答:



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章