自己实现的一个简易Spring框架(IoC+AOP)

1,195 阅读16分钟

IoC和AOP是spring的核心,可以说没有他们就没有庞大的Spring家族。我也是心血来潮,自己动手写了一个简易的Spring框架。可以通过使用注解来实现IoC容器和AOP。

先说IoC部分吧。源码下载:download.csdn.net/detail/jobs…

IoC

先定义了两个注解@MyBean和@MyAutowired,用来标记Bean和自动注入的对象。

  1. package mySpring.autowired;  
  2.   
  3. import java.lang.annotation.*;  
  4.   
  5. /** 
  6.  * Created by 10033 on 2017/5/9. 
  7.  */  
  8. @Target({ElementType.TYPE})  
  9. @Retention(RetentionPolicy.RUNTIME)  
  10. @Inherited  
  11. @Documented  
  12. public @interface MyBean {  
  13.     String value();  
  14. }  
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();
}
  1. package mySpring.autowired;  
  2.   
  3. import java.lang.annotation.*;  
  4.   
  5. @Target({ElementType.FIELD,  ElementType.METHOD})  
  6. @Retention(RetentionPolicy.RUNTIME)  
  7. @Inherited    
  8. @Documented   
  9. public @interface MyAutowired {  
  10. }  
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(注入)。当然,注入的时候要判断是否循环依赖,这个我是在注入过程中判断的,也可以预判,但可能会稍麻烦些。

下面是自动注入类的代码:

  1. package mySpring.autowired;  
  2.   
  3. /** 
  4.  * Created by 10033 on 2017/5/9. 
  5.  */  
  6.   
  7. import java.io.IOException;  
  8. import java.lang.reflect.Field;  
  9. import java.util.HashMap;  
  10. import java.util.List;  
  11. import java.util.Map;  
  12.   
  13. /** 
  14.  * 自动注入类 
  15.  */  
  16. public class AutomaticInjection {  
  17.   
  18.     public static void automaticInjection(String key, Map mmp) {  
  19.         try {  
  20.             List<Class> list = GetClass.getClassList(key);  
  21.   
  22.             for(Class classes:list) {  
  23.                 //注册  
  24.                 Map<String, Object> judgeMap = new HashMap();  
  25.                 //注入  
  26.                 injection(mmp,classes,judgeMap);  
  27.             }  
  28.         } catch (IOException e) {  
  29.             e.printStackTrace();  
  30.         } catch (ClassNotFoundException e) {  
  31.             e.printStackTrace();  
  32.         } catch (Exception e) {  
  33.             e.printStackTrace();  
  34.         }  
  35.     }  
  36.   
  37.     //注入并判断是否循环依赖  
  38.     private static void injection(Map mmp, Class classes, Map judgeMap)  
  39.             throws Exception {  
  40.         boolean isExist = classes.isAnnotationPresent(MyBean.class);  
  41.         //如果该注解存在  
  42.         if(isExist) {  
  43.             MyBean myBean = (MyBean) classes.getAnnotation(MyBean.class);  
  44.             String beanName= myBean.value(); //获得bean名称  
  45.             if(null==judgeMap.get(beanName))  
  46.                 judgeMap.put(beanName,true);  
  47.             else { //又返回依赖他  
  48.                 throw new Exception( "循环依赖");  
  49.             }  
  50.   
  51.             if(null==mmp.get(beanName)) { //还没有被注入  
  52.                 Object beanObj=classes.newInstance(); //获得bean实例  
  53.   
  54.                 Field[] fields=classes.getDeclaredFields();  
  55.                 boolean fieldExist;  
  56.                 for(Field field:fields) {  
  57.                     fieldExist=field.isAnnotationPresent(MyAutowired.class);  
  58.   
  59.                     if(fieldExist) {  
  60.                         String classtype=field.getGenericType().toString();  
  61.                         Class fieldClass=Class.forName(classtype.substring(6));  
  62.   
  63.                         //强制设置值 破坏了封装性  
  64.                         field.setAccessible(true);  
  65.   
  66.                         if(fieldClass.isAnnotationPresent(MyBean. class)) {//该属性依赖其它Bean  
  67.                             MyBean tbean = (MyBean) fieldClass.getAnnotation(MyBean.class);  
  68.                             injection(mmp,fieldClass,judgeMap);  
  69.                             field.set(beanObj, mmp.get(tbean.value()));  
  70.   
  71.                         }  
  72.   
  73.                         else {  //该属性不依赖其它Bean  
  74.                             Object object=fieldClass.newInstance();  
  75.                             field.set(beanObj, object);  
  76.                         }  
  77.                     }  
  78.                 }  
  79.                 mmp.put(beanName, beanObj);  
  80.             }  
  81.   
  82.         }  
  83.     }  
  84.   
  85.     public static void reinjection(Map mmp, Class classes, Object obj)  throws ClassNotFoundException, IllegalAccessException, InstantiationException {  
  86.         Field[] fields=classes.getDeclaredFields();  
  87.         boolean fieldExist;  
  88.         for(Field field:fields) {  
  89.             fieldExist=field.isAnnotationPresent(MyAutowired.class);  
  90.   
  91.             if(fieldExist) {  
  92.                 String classtype=field.getGenericType().toString();  
  93.                 Class fieldClass=Class.forName(classtype.substring(6));  
  94.                 field.setAccessible(true);  
  95.                 //强制设置值 破坏了封装性  
  96.                 field.setAccessible(true);  
  97.   
  98.                 if(fieldClass.isAnnotationPresent(MyBean.class)) { //该属性依赖其它Bean  
  99.                     MyBean tbean = (MyBean) fieldClass.getAnnotation(MyBean.class);  
  100.                     field.set(obj, mmp.get(tbean.value()));  
  101.   
  102.                 }else { //该属性不依赖其它Bean  
  103.                     Object object=fieldClass.newInstance();  
  104.                     field.set(obj, object);  
  105.                 }  
  106.             }  
  107.         }  
  108.     }  
  109.   
  110. }  
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。

  1. package mySpring.aop;  
  2.   
  3. import java.lang.annotation.*;  
  4.   
  5. /** 
  6.  * Created by 10033 on 2017/5/12. 
  7.  */  
  8. @Target({ElementType.TYPE, ElementType.METHOD})  
  9. @Retention(RetentionPolicy.RUNTIME)  
  10. @Inherited  
  11. @Documented  
  12. public @interface PointCut {  
  13.     String value();  
  14. }  
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();
}
  1. package mySpring.aop;  
  2.   
  3. import java.lang.annotation.*;  
  4.   
  5. /** 
  6.  * Created by 10033 on 2017/5/12. 
  7.  */  
  8. @Target({ElementType.METHOD})  
  9. @Retention(RetentionPolicy.RUNTIME)  
  10. @Inherited  
  11. @Documented  
  12. public @interface Ignore {  
  13. }  
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是会形成一条拦截器链的,我没有做得那么复杂。我写了一个控制类来进行切面信息判断来实现正确的拦截(替代拦截器链),这个控制器会根据注解选择正确的操作执行。我将操作也独立成了一个类,进行解耦。

下面是控制类:

  1. package mySpring.aop;  
  2.   
  3. /** 
  4.  * Created by 10033 on 2017/5/12. 
  5.  */  
  6.   
  7. import net.sf.cglib.proxy.MethodProxy;  
  8.   
  9. import java.lang.reflect.Method;  
  10.   
  11. /** 
  12.  * 通过注解判断执行哪个通知 
  13.  */  
  14. public class ProxyController {  
  15.   
  16.     //没有类注解  
  17.     public static Object doController  
  18.             (Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {  
  19.         //有忽视注解  
  20.         if(method.isAnnotationPresent(Ignore.class))  
  21.             return methodProxy.invokeSuper(o, objects);  
  22.         //没有切入点  
  23.         if(!method.isAnnotationPresent(PointCut.class)) {  
  24.             return methodProxy.invokeSuper(o, objects);  
  25.         }else { //有切入点  
  26.             Advice advice=getAdvice(method);  
  27.             return doAdvice(o,objects,methodProxy,advice);  
  28.         }  
  29.   
  30.     }  
  31.   
  32.     //有类注解  
  33.     public static Object doController  
  34.         (Object o, Method method, Object[] objects, MethodProxy methodProxy, Advice advice) throws Throwable {  
  35.         //有忽视注解  
  36.         if(method.isAnnotationPresent(Ignore.class))  
  37.             return methodProxy.invokeSuper(o, objects);  
  38.         //有切入点  
  39.         if(method.isAnnotationPresent(PointCut.class)) {  
  40.             Advice advice2=getAdvice(method);  
  41.             return doAdvice(o,objects,methodProxy,advice2);  
  42.         } else { //没有切入点  
  43.             return doAdvice(o,objects,methodProxy,advice);  
  44.         }  
  45.   
  46.     }  
  47.   
  48.     private static Object doAdvice(Object o, Object[] objects, MethodProxy methodProxy, Advice advice)  throws Throwable {  
  49.         if(advice instanceof AfterAdvice) {  
  50.             return Execute.executeAfter(o,objects,methodProxy, (AfterAdvice) advice);  
  51.         }else if(advice instanceof BeforeAdvice) {  
  52.             return Execute.executeBefore(o,objects,methodProxy, (BeforeAdvice) advice);  
  53.         }else if(advice instanceof SurroundAdvice) {  
  54.             return Execute.executeSurround(o,objects,methodProxy, (SurroundAdvice) advice);  
  55.         }  
  56.         return null;  
  57.     }  
  58.   
  59.     private static Advice getAdvice(Method method) throws ClassNotFoundException, IllegalAccessException, InstantiationException {  
  60.         String classPath=method.getAnnotation(PointCut.class).value();  
  61.         Advice advice= (Advice) Class.forName(classPath).newInstance();  
  62.         return advice;  
  63.     }  
  64. }  
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;
    }
}
下面是具体操作执行类
  1. package mySpring.aop;  
  2.   
  3. import net.sf.cglib.proxy.MethodProxy;  
  4.   
  5. /** 
  6.  * Created by 10033 on 2017/5/12. 
  7.  * 执行通知 
  8.  */  
  9. public class Execute {  
  10.     public static Object executeAfter  
  11.             (Object o, Object[] objects, MethodProxy methodProxy, AfterAdvice advice) throws Throwable {  
  12.         Object object=methodProxy.invokeSuper(o,objects);  
  13.         advice.after();  
  14.         return object;  
  15.     }  
  16.     public static Object executeBefore  
  17.             (Object o, Object[] objects, MethodProxy methodProxy, BeforeAdvice advice) throws Throwable {  
  18.         advice.before();  
  19.         Object object=methodProxy.invokeSuper(o,objects);  
  20.   
  21.         return object;  
  22.     }  
  23.     public static Object executeSurround  
  24.             (Object o, Object[] objects, MethodProxy methodProxy, SurroundAdvice advice) throws Throwable {  
  25.         advice.before();  
  26.         Object object=methodProxy.invokeSuper(o,objects);  
  27.         advice.after();  
  28.         return object;  
  29.     }  
  30. }  
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类:

  1. package mySpring.autowired;  
  2.   
  3.   
  4. import mySpring.aop.ProxyFactory;  
  5.   
  6. import java.util.HashMap;  
  7. import java.util.Map;  
  8.   
  9. /** 
  10.  * Created by 10033 on 2017/5/9. 
  11.  */  
  12. public class BeanFactory {  
  13.     public static Map<String, Object> map=new HashMap();  
  14.     private static final String KEY="scan.package";  
  15.     //初始化IoC容器  
  16.     static {  
  17.         AutomaticInjection.automaticInjection(KEY,map);  
  18.         ProxyFactory.makeProxyBean(map);  
  19.   
  20.         //生成代理后重新注入  
  21.         for(String key:map.keySet()) {  
  22.             Class c=map.get(key).getClass().getSuperclass();  
  23.             try {  
  24.                 AutomaticInjection.reinjection(map,c,map.get(key));  
  25.             } catch (Exception e) {  
  26.                 e.printStackTrace();  
  27.             }  
  28.         }  
  29.     }  
  30.     public static Object getBean(String name) {  
  31.         return map.get(name);  
  32.     }  
  33. }  
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生成的子类无法获取到它父类的属性,也因为这个瞎忙活了很久,希望有人能为我解答。

上面的问题已得到解答: