自己实现的一个简易Spring框架(IoC+AOP)
2017-05-12 21:25
781 查看
IoC和AOP是Spring的核心,可以说没有他们就没有庞大的Spring家族。我也是心血来潮,自己动手写了一个简易的Spring框架。可以通过使用注解来实现IoC容器和AOP。
先说IoC部分吧。源码下载:http://download.csdn.net/detail/jobsandczj/9841126
实现思路:
我的思路是在一个properties文件里配置要扫描的包(Resource定位),通过扫描这些包获得他们的Class对象存入List中(载入和解析),将list中的Class对象转储到Map中,以@Bean里配置的Bean名为key(注册),再通过反射将Map里的Class转换成Bean(注入)。当然,注入的时候要判断是否循环依赖,这个我是在注入过程中判断的,也可以预判,但可能会稍麻烦些。
下面是自动注入类的代码:
我只实现了三种通知,Before,After,Surround(前面两种结合,Spring里没有这种。。)。为每种通知定义一个接口,每个接口都继承Advice(空接口)。
实现原理:
AOP是在完成上述IoC注入后再实现的。就是为每个Bean生成一个代理类,根据注解信息提供相应的方法拦截操作。Spring的AOP是会形成一条拦截器链的,我没有做得那么复杂。我写了一个控制类来进行切面信息判断来实现正确的拦截(替代拦截器链),这个控制器会根据注解选择正确的操作执行。我将操作也独立成了一个类,进行解耦。
下面是控制类:
执行完AOP后,我们要重新进行一遍注入,这就是上面那个自动注入类的reinjection方法要做的事。
启动Spring也非常简单,只要通过Class.forName("mySpring.autowired.BeanFactory");加载类就行。当然这么做有一个坏处就是减少了灵活性,配置文件必须按照严格规范。
下面是BeanFactory类:
对了,写的时候遇到一个问题,那就是CGLIB生成的子类无法获取到它父类的属性,也因为这个瞎忙活了很久,希望有人能为我解答。
上面的问题已得到解答:
先说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生成的子类无法获取到它父类的属性,也因为这个瞎忙活了很久,希望有人能为我解答。
上面的问题已得到解答:
相关文章推荐
- 实现一个简易的IoC框架(上)(此篇与Spring.NET无关,为自己手写IoC框架)
- 写一个自己的Spring框架——简单实现IoC容器功能
- 实现一个简易的IoC框架(上)(此篇与Spring.NET无关,为自己手写IoC框架)
- 自己实现一个IOC框架
- (精简)Spring框架的IoC(替代工厂类实现方法)和AOP(定义规则,约定大于配置)
- 在.Net中实现自己的简易AOP
- 根据数组的原理,自己实现一个简易版的ArrayList
- Spring.NET学习笔记3——实现一个简易的IoC框架(练习篇)(转)
- 自己动手写一个web框架(二):实现AOP
- 初步理解spring ioc原理(读完可自己实现依赖注入部分的spring框架)
- 自己模拟一个简易的spring框架
- Spring.NET学习笔记3——实现一个简易的IoC框架(练习篇)
- Spring.NET学习笔记3——实现一个简易的IoC框架(练习篇) Level 100
- 实现一个简易IOC容器
- Spring框架的IOC/DI的自己实现
- 自己来实现一个简易的OCR
- Spring.NET学习笔记3——实现一个简易的IoC框架(练习篇)
- 自己实现一个IOC(控制翻转,DI依赖注入)容器
- 合格前端系列第三弹-实现一个属于我们自己的简易MVVM库
- 自己实现一个简易的SpringMVC