关于IoC的简单想法,以及循环依赖的处理。
关于IoC
前言: IoC是Spring框架的重要内容。希望通过对它的简单模拟能够更加深入的了解Spring的内涵。感谢一直以来为我引路的朱洪教主。
1 什么是IoC
控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体,将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。—来自度娘
度娘的意思很难懂,在这里我就说说我自己的理解。IoC的意思就是说,当一个类需要被实例化的时候,并不是通过我们编程人员自己通过new()方法来实现的,而是交给第三方来完成的。并且这个类被销毁的时候也应该是由第三方来完成的。(之所以是第三方,是因为Java虚拟机也是一方)。
2 IoC的技术核心
首先我们必须知道自己需要产生的对象是什么,并且能够让第三方去知道,这个对象将会第三方自动生成。
因此,我们可以采用某种方式告知第三方。故,注解和XML文件将是我们的好帮手。在这里,我只实现注解方式来完成IoC的操作。
最重要的一点是,我们希望第三方产生的对象是一个类的代理对象。这样,关于IoC才能真真正正的实现解耦。并且只有AOP与IoC相结合才能高效的完成工作。
关于AOP的相关讨论,我已经给出,请参考这里: AOP的简单讨论.
在AOP中虽然已经完成了代理对象的注解自动化产生,但是所有代理对象中所有成员的值都是0或者NULL。这是不完整的。IoC的核心在于注入,注入就是把值(可以是对象,可以是基本八大类型以及String类型)赋值给对象的成员。 也就是说,单说IoC是不完整的。
与AOP相同的是,我们需要包扫描技术。
3 IoC的简单实现
由于IoC是基于AOP的基础之上来完成的,所以在这里已经在AOP中出现的相关的代码我不会给出。由于AOP我们已经是完成了自动生成拦截器的地步,因此我们需要改变的只是原来工厂中的某些方法。我们原来在AOP中自动生成代理的AotoProxyFactory类已经可以正式改名字了,我们可以叫它BeanFactory。关于BeanFactory类我们这样说,它是对AotoProxyFactory的补充。
下面,我先给出完成该功能的相关注解
//该类作用于除基本八大类型String类型,之外的类 @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface Autowire { public String beanName() default ""; } //将会被扫描到的类 @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface Compent { public String beanName() default ""; } //基本八大类和string类 @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface Value { public String value(); public Class<?> klass(); }
从上述注解可以看出,我们定义Compent注解使的这个类被扫描到,并且为其生成代理对象,并且它将被注入。
我们定义的Autowire注解,将会告知这个Compent注解存在类,他需要一个同样被IoC所生成的类的对象作为成员。(这里存在一个问题,后面将会说到)。最后,Value注解为我们将基本八大类型以及String类注入进对象中。
//该类作用于方法 @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Bean { public String beanName() default ""; }
关于Bean注解,我想多说一些
我们通过Bean注解为方法上添加注解,这是为了生成那些没有办法在类上添加Compent注解的类。例如我们的复杂类需要一个Map作为对象,我们是没有办法在Map的源代码上添加注解的,它的源代码不可更改。并且如果是方法上的注解,我们就一定要调用反射机制的invoke方法,反射执行该方法的。作为方法,参数是一个很重要的标志,如果参数是我们BeanFactory内部Map内的对象,这当然是合理的。但如果说该类的对象还没有被放到BeanMap中去,那显然是会出现问题的。
我们给出如下类,他描述了一个Method的基本信息:
public class BeanMetohdDefination { private Method method; private Class<?> returnType; private Parameter[] parameters; private Object object; private String nick; BeanMetohdDe 1b5d7 fination() { } BeanMetohdDefination(Method method, Class<?> returnType, Parameter[] parameters, Object object, String nick) { this.method = method; this.returnType = returnType; this.parameters = parameters; this.object = object; this.nick = nick; } String getNick() { return nick; } Method getMethod() { return method; } Class<?> getReturnType() { return returnType; } Parameter[] getParameters() { return parameters; } Object getObject() { return object; } }
我们在给一个List,这个List存储了当前不能直接执行的方法的一个列表,并且先把无参方法全部执行完成之后,在考虑去执行相关带参的方法。
完成上述的问题后,我们已经生产了很多没有完全注入好的对象(已经注入好的只有成员都是基本八大类型的对象,部分成员为基本八大类型的对象只注入一部分(Value注解的作用))。
在这里总结以下,用来告诉第三方需要产生对象的注解是Compent和Bean注解。而Autowire注解才是注入注解,这个注解告诉了我们对象间的注入关系。并且只有在所有对象都被生成了才能完成整个的注入。也就是先产生,后注入。
注入是最后完成的。
关于我们在将完成对象的注入时,我们先需要思考这一点,如果我们注入一个复杂类,它需要另一个复杂类,但是该复杂类还需要另外一个复杂类来注入。这个时候我们就需要先完成另一个复杂类的注入,此处就需要一个递归程序(这是是有保障的,因为你不可能产生几百万个对象)。
比如这样:
图中的箭头代表着,类之间的注入关系,箭头指的类被箭头尾部装入。
上述的图还有一种很可怕的走向, 由于采用递归,就有下面的一个问题,当一个类是的成员是另一个类,另一个类的成员是该类,那我们的递归就成了无限递归。比如说这样:
下图给了循环依赖的两个测试类,这是最简单的注入关系,但同时也最能说明问题。
@Compent public class ClassA { @Autowire private ClassB b; public ClassA() { } public void showClassB(){ System.out.println("ClassA" + b); } @Override public String toString() { return "ClassA " + super.toString(); } }
@Compent public class ClassB { @Autowire private ClassA a; public ClassB() { } @Bean public ClassA getClassA(ClassA a){ this.a = a; return a; } public ClassA getClassA(){ return this.a; } @Override public String toString() { return "ClassB " + super.toString(); } }
因此,我 们不妨给一个标记,当我们注入时,如果它被注入,或者正在注入,我们就记它为false,表示它不能注入了,在递归注入之前,先查看它能不能注入,如果被标记为false,那就直接结束这次递归调用。这样就能够避免循环依赖。这样:
当有了标志后,上图就变成了第一个图。这样就能完成循环依赖。
这里是整个IoC的最后部分,为了完成它,我们做了很多工作。当外部调用getBean方法时候,我们完成注入。下面的类没有处理Value注解。有兴趣的可以实现一下。在这里就不给出了。
public class BeanFactory { //真正的bean private HashMap<String, MeProxy> beanMap; //bean的昵称 private HashMap<String, String> beanNameMap; public BeanFactory() { beanMap = new HashMap<String, MeProxy>(); beanNameMap = new HashMap<String, String>(); } public <T> T getBean(Class<?> klass){ MeProxy meProxy = getOwerMeProxy(klass); if (meProxy.isInjection() == false) { injectBean(klass, meProxy.getObject()); } return meProxy.getProxy(); } public <T> T getBean(String beanName){ Class<?> klass = null; try { klass = Class.forName(beanNameMap.get(beanName)); } catch (Exception e) { new Exception("昵称不存在" + beanName); e.printStackTrace(); } return getBean(klass); } private void injectBean(Class<?> klass, Object object){ Field[] fields = klass.getDeclaredFields(); MeProxy meProxy = getOwerMeProxy(klass); for (Field field : fields) { if (!field.isAnnotationPresent(Autowire.class)) { continue; } meProxy.setInjection(true); MeProxy fieldProxy = getOwerMeProxy(field.getType()); if (fieldProxy.isInjection() == false) { injectBean(field.getType(), fieldProxy.getObject()); } field.setAccessible(true); try { //这里的对象,必须是原对象,不能是代理对象。 field.set(object, fieldProxy.getProxy()); } catch (Exception e) { e.printStackTrace(); } } meProxy.setInjection(true); } public void packSannerIntercept(String packageName){ new PackageSanner() { @Override public void doSanner(Class<?> klass) { if(klass.getAnnotation(Ascept.class) == null){ return; } Method[] methods = klass.getMethods(); for (int i = 0; i < methods.length; i++) { Method method = methods[i]; After after = method.getAnnotation(After.class); Before before = method.getAnnotation(Before.class); ExceptionIntercept exception = method.getAnnotation(ExceptionIntercept.class); if (after != null) { dealAfter(after ,method); } if (before != null) { dealBefore(before,method); } if (exception != null) { dealException(exception,method); } } } }.packageSanner(packageName); } private void dealAfter(After after, Method method){ Class<?> klass = after.klass(); String string = after.methodName(); Class<?>[] paraType = after.paraKlass(); MeProxy meProxy= getOwerMeProxy(klass); Method methodsClass; try { methodsClass = klass.getMethod(string, paraType); meProxy.addIntercepter(methodsClass, new AfterMethodIntercepter(method)); } catch (Exception e) { e.printStackTrace(); } } /** * * @param before * @param method * @throws Throwable * * @tip * 通过拦截器的方法的参数类型得到需要拦截的方法的精确定位 <p> * 根据拦截器的相关定义,其前置拦截必须要求的参数是拦截方法的参数 */ private void dealBefore(Before before, Method method){ String[] strings = before.methodNames(); Class<?> klass = before.klass(); MeProxy meProxy= getOwerMeProxy(klass); for (int i = 0; i < strings.length; i++) { try { Method methodsClass = klass.getMethod(strings[i], method.getParameterTypes()); meProxy.addIntercepter(methodsClass, new BeforeMethodIntercepter(method)); } catch (Exception e) { e.printStackTrace(); } } } private void dealException(ExceptionIntercept exception, Method method){ Class<?> klass = exception.klass(); String string = exception.methodName(); Class<?>[] paraType = exception.paraKlass(); MeProxy meProxy= getOwerMeProxy(klass); Method methodsClass; try { methodsClass = klass.getMethod(string, paraType); meProxy.addIntercepter(methodsClass, new ExceptionMethodIntercepter(method)); } catch (Exception e) { e.printStackTrace(); } } private MeProxy getOwerMeProxy(Class <?> klass){ MeProxy meProxy= beanMap.get(klass.getName()); if (meProxy == null) { try { throw new BeanNotExist(klass.getName() + "的对象不存在"); } catch (BeanNotExist e) { e.printStackTrace(); } } return meProxy; } public void packSanner(String packageName){ List<BeanMetohdDefination> methodList = new ArrayList<>(); new PackageSanner() { @Override public void doSanner(Class<?> klass) { Compent compent = klass.getDeclaredAnnotation(Compent.class); if (compent == null) { return; }else{ String str = compent.beanName(); if (str.length() > 0) { beanNameMap.put(str, klass.getName()); } createBean(null,klass); } Method[] methods = klass.getDeclaredMethods(); for (Method method : methods) { try { invokeBeanMethod(method, methodList); } catch (Exception e) { e.printStackTrace(); } } } }.packageSanner(packageName); for (BeanMetohdDefination beanMetohdDefination : methodList) { try { Object Object = invokeMuliParaMethod(beanMetohdDefination.getMethod(), beanMetohdDefination.getParameters(), beanMetohdDefination.getObject()); createBean(Object, beanMetohdDefination.getReturnType()); } catch (Exception e) { e.printStackTrace(); } } } private void invokeBeanMethod(Method method, List<BeanMetohdDefination> methodList) throws Exception { Class<?> klass = method.getReturnType(); Bean bean = method.getAnnotation(Bean.class); if (klass.equals(void.class) || bean == null) { return; } String name = bean.beanName(); Parameter[] parameters = method.getParameters(); Object object = beanMap.get(method.getDeclaringClass().getName()).getObject(); if (parameters.length <= 0) { Object result = method.invoke(object); createBean(result,method.getReturnType()); } else { methodList.add(new BeanMetohdDefination(method, klass, parameters, object, name)); } } private void createBean(Object obj, Class<?> klass) { MeProxyFactory meProxyFactory = new MeProxyFactory(); try { if(obj == null){ meProxyFactory.getCGLProxy(klass); }else{ meProxyFactory.getCGLProxy(obj); } } catch (Throwable e) { e.printStackTrace(); } beanMap.put(klass.getName(), meProxyFactory.getMeProxy()); } private Object invokeMuliParaMethod(Method method, Parameter[] parameters, Object object) throws Exception { int paraCount = parameters.length; Object[] paras = new Object[paraCount]; for (int index = 0; index < paraCount; index++) { Parameter parameter = parameters[index]; String className = parameter.getType().getName(); MeProxy meProxy = beanMap.get(className); Object beanObject = meProxy.getObject(); if (beanObject != null) { paras[index] = beanObject; } } return method.invoke(object, paras); } }
以下是测试
@Ascept public class Text { public Text() { } @After(klass = Student.class, methodName = "seek", paraKlass = { Object.class }) public Class<?>[] funcyin(String name){ Class<?>[] klass = new Class[4]; System.out.println("seek方法的后置拦截"); return klass; } @Before(klass = Student.class, methodNames = {"seek"}, beanName = "") public boolean func(Object obj){ System.out.println("2这是前置拦截"); System.out.println((String)obj); return true; } @Before(klass = Student.class, methodNames = {"seek"}, beanName = "") public boolean funct(Object obj){ System.out.println("1这是前置拦截"); System.out.println((String)obj); return true; } @Before(klass = Student.class, methodNames = {"remove"}, beanName = "") public boolean functt(Object obj){ System.out.println("3这是前置拦截"); return true; } public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { BeanFactory aoto = new BeanFactory(); aoto.packSanner("com.ioc.text"); aoto.packSannerIntercept("com.ioc.text"); Student student = aoto.getBean(Student.class); ClassA a = aoto.getBean(ClassA.class); ClassB b = aoto.getBean(ClassB.class); a.showClassB(); System.out.println(b); System.out.println(a); student.seek("111"); }
结果如图:
观察到,ClassB的地址,与a.showClassB的地址值是一样的,也就说,我们的确完成了相关的操作。
最后,我们总结一下如下东西。
第一,无参构造的必要性。我们的IoC的对象,是通过调用无参的构造方法来反射生成的。我们可以思考以下,对于上述的ClasA和ClassB,如果只有单参构造,那么真的是神仙都没办法正确使用这两个类。
第二,get和set方法,这是对字段赋值的强力方法。
第三,this指针的作用,在AOP中,我们发现this指针指向了被代理对象的空间,这样,我们的代理才是真的有意义的。我们是真的在原来代码不变的情况下,然后通过代理来有条件的操纵原来空间。
- 点赞
- 收藏
- 分享
- 文章举报
- Installshield关于.NET安装时需要重启动的处理办法,以及延伸出的重启后继续安装的安装包的一点想法
- 20130910 一些想法,关于项目中异常处理的解决方案,以及Elmah
- 关于Qt的事件循环以及QEventLoop的简单使用
- 关于Qt的事件循环以及QEventLoop的简单使用(QEventLoop::quit()能够终止事件循环,事件循环是可以嵌套的)
- 关于Qt的事件循环以及QEventLoop的简单使用
- Installshield关于.NET安装时需要重启动的处理办法,以及延伸出的重启后继续安装的安装包的一点想法
- 关于Qt的事件循环以及QEventLoop的简单使用
- 关于大批量数据处理的一些简单想法
- 关于Qt的事件循环以及QEventLoop的简单使用
- 关于.NET安装时需要重启动的处理办法,以及延伸出的重启后继续安装的安装包的一点想法
- spring5 源码深度解析— IOC 之 循环依赖处理
- Spring对加载的bean之间循环依赖的处理
- shiro(四) 关于session:验证登录时候如何处理session以及其他
- ios中关于NSString 的retainCount和循环引用的处理方式
- java 关于Spring中Aop的简单理解以及SpringBoot如何添加Aop实现步骤
- 关于日期,java处理日期间隔的方法(简单实用)
- My.Ioc 代码示例——避免循环依赖
- Android开发,关于依赖库和JAR包的区别以及使用多个依赖库可能出现的问题(SlidingMenu、SwipeBackLib)
- 关于Java反射机制中的Field简单尝试与示例以及说明
- 关于for循环内查询处理的解决方案