上一篇:
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,所以我把它们看成两个工程。