IOC的一种简易实现(三)

上一篇:

http://blog.csdn.net/wkgcass/article/details/43969219

展示了全部的功能,不过没有用实例进行进一步说明。

这篇先不上实例,先看看这个工具的内部逻辑。


首先,最重要的是注入入口。

编程中不存在没有逻辑的事情。所以能够使用 new 来完成所有注入,肯定不是通过某个实例的方法完成的注入。

入口就在要被注入的类中!


可以在第一篇看到,需要注入的类要继承AutoWire类。第二篇也写到,@Wire注解不能用new来完成注入。

所以入口在AutoWire中。

AutoWire是一个抽象类,代码很少,只有一个无参构造函数。

代码如下:

/**
 * Used as a super class.
 * <br/> Gives POJO or a class with setters the capability of autowiring.
 * <br/> Simply <b>new</b> a class, then all setters would be automatically called
 * <br/> with corresponding parameters.
 * @author wkgcass
 *
 */
public abstract class AutoWire {
	public static boolean DEBUG_MODE = false;
	
	public AutoWire(){
		//check this class annotation ( IsSingleton Bean )
		Annotation[] anns=this.getClass().getAnnotations();
		
		boolean block=true;
		
		for(Annotation ann : anns){
			if(ann.annotationType()==IsSingleton.class){
				if( ! IOCController.isConstructing(this.getClass())){
					throw new IOCException("Cannot instantiate singleton class :"+this.getClass());
				}else{
					block=false;
				}
			}
			if(ann.annotationType()==Bean.class){
				Bean bean=(Bean)ann;
				if(bean.isSingleton()){
					if(! IOCController.isConstructing(this.getClass())){
						throw new IOCException("Cannot instantiate singleton bean :"+this.getClass());
					}else{
						block=false;
					}
				}
			}
		}
		
		//block
		if(block){
			IOCController.block(this.getClass());
		}
		
		//start constructing
		IOCController.setConstructing(this.getClass());
		
		//setters
		Method[] methods = this.getClass().getMethods();
		for(Method m : methods){
			//is setter
			if(m.getName().startsWith("set") && m.getName().charAt(3)>='A' && m.getName().charAt(3)<='Z'
				&& m.getParameterTypes().length==1 &&m.getReturnType()==Void.TYPE
				){
				IOCController.invokeSetter(this, m);
				
			}
		}
		
		//constructing finished
		IOCController.removeConstructing(this.getClass());
	}
	
}

就是这样。

事实上这个实现是很不完美的。


1.判断要实例化的这个类是否是单例。这儿单例有两类,一种是类单例,一种是bean单例。

由于本工具允许一个类对应多个bean,而且对于工具来说 类,bean的不同定义 是不同的东西

比如:

@Bean(name={"a","b"})
@IsSingleton
class Test{}

可以是Test类,可以是名称为a的bean,可以是名称为b的bean。所以此处一定需要判断到底是在实例化那样“东西”

所以牵涉到判定问题。

由于实例化时不管是 new, Class newInstance(),还是 Constructor.newInstance(...),实际上都和调构造函数一样,

所以,在实例化时,想要知道这是哪个bean,必须要在类外部有一个值来参考。


而问题还没解决。

如果判定一个类是单例,那么很显然,单例不应该允许被“new”出来(单例模式中都是调用static方法获取的)。

所以如果是单例,肯定需要抛出异常。


现在问题又来了~

如果现在new的不是一个单例,但是用到了一个单例,那么会怎样?如果不作处理,单例在实例化时也会抛出异常,因为前面也说到了,new,newInstance其实都是一回事


所以,如果是单例,就得在构造前进行一个标注,“告诉”这个类,“你可以实例化”。开始实例化了再消除这个标注。


而问题又来了~

多线程环境下,假如我有一个类需要使用Test,于是IOCController作出了标注,指示Test可以实例化。而在实例化完成前,另一个线程对这个类进行实例化,此时“标注”依旧存在,也就是说,所谓的“单例”就被破坏了。


于是我们必须指定一个类同时只能有一个在进行实例化,其他的通通block出去,直到实例化结束。


这其实是一种很暴力的方式。不讲理的阻塞,有时候会拖慢系统运行。

在我另一个类似的框架 purify 工程中(它的功能在第一篇的红色文字中能看到)用了另一种非常完美的方式解决了。

单例在实例化开始时检查是否本身已经存在,如果不存在则把自己的引用“告诉”IOCController,这只需要锁住IOCController中存放单例的一个map,完成时就可以释放。

这样,再出现单例出现实例化的尝试就会直接抛出异常。

而在IOCController中使用类似 Double Check Lock 的机制(常常在lazy loading的singleton中使用的一种方法,这里不详细说了,网上很多资料)来获取单例。

用这种新的方法顺带完美解决了循环依赖问题。这个工具在之后版本中也会进行修复的。


现在来说说本工具的循环依赖问题。

由于类实例化时会阻塞其他的实例化尝试,而注入又是在实例化结束前完成的,所以出现循环依赖时会被无限阻塞。

那怎么办呢。于是只能让IOCController在实例化时检测是否现在在构造,如果时则抛出异常。

但是这样又怎么实现循环依赖的注入呢?


这个处理非常奇葩,我想先请看文的各位先别往下看,思考一下该如何处理(如果要求改动尽量少,那么处理方式其实非常明显……)





具体的处理如下:



private static void setFutureTask(final Object target,final Method setter,final String bean){
		Thread t=new Thread(
				new Runnable(){
					public void run(){
						while(!beans.containsKey(bean)){
							try {
								Thread.sleep(1);
							} catch (InterruptedException e) {}
						}
						try{
							setter.invoke(target, beans.get(bean));
						}catch(Exception e){e.printStackTrace();return;}
					}
				}
		);
		
		t.start();
	}
	private static void setFutureTask(final Object target,final Method setter,final Class<?> cls){
		Thread t=new Thread(
				new Runnable(){
					public void run(){
						while(!objects.containsKey(cls)){
							try {
								Thread.sleep(1);
							} catch (InterruptedException e) {}
						}
						try{
							setter.invoke(target, objects.get(cls));
						}catch(Exception e){e.printStackTrace();return;}
					}
				}
		);
		
		t.start();
	}

哈哈……是不是很奇葩……

不过确实能解决,只不过在new后需要Thread.sleep(1)。不用多,只需要1,但是不加就来不及完成注入。

新建一个线程,不停尝试获取单例,获取到时就进行注入。



而在我的新框架中不存在这种问题。由于新框架允许同一个类同时进行多个实例化,所以如果出现循环依赖依旧能够进行注入。而且仅仅需要整个循环链中有一个单例即可(链中没有单例肯定会stack over flow的,这是一个不可能解决的问题,因为每次调用都会新建实例)。


这里作个预告,

我正在做的新框架purify定位依旧是辅助(避免重造轮子)。它旨在减少配置文件的编写。因为许多配置都是重复劳动。(比如你写了filter,还要去web.xml里注册它。写了servlet,还要去注册它。本身是简单的逻辑还是要去applicationContext中注册这个bean...我的新框架就是要避免类似于这样的重复劳动。一句话可以概括,写纯粹的code

目前新框架能进行:

自动获取工程的所有类(包括jar包内的)(其实这是我更早一个工程中用到的功能,这里仅仅是拷过来用。。。)

自动进行各Handler的注册(可以在第二篇文章中看到为何要注册各个Handler),只要你保证工程目录下有这个类即可。

移除了bean的支持,因为spring已经实现了bean了。而且和框架定位(辅助)不符合。

自动进行web.xml中filter,servlet的“注册”,简单来说,大部分情况,你只要写filter或者servlet,其他什么都不用管。


接下来我还想完成这些事情:

struts2配置的简化(spring mvc挺简单的我就不重复劳动了)

aop的实现,依旧不需要任何的注册或者xml编写。已有思路。

持久层(hibernate,mybatis)的配置简化,现在思路还不怎么清晰(希望有人能给我点建议)。。。


当然还得完成的是对这个工具的完善。。因为这个工具支持bean,所以我把它们看成两个工程。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值