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

来源:互联网 发布:青铜 知乎 编辑:程序博客网 时间:2024/05/16 14:28

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@Documentedpublic @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@Documentedpublic @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@Documentedpublic @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生成的子类无法获取到它父类的属性,也因为这个瞎忙活了很久,希望有人能为我解答。


上面的问题已得到解答:



1 0