IOC容器简单实现(转)

这是个很简单的IOC容器,基本功能有:自动加载所有使用了@Inject注解的类,然后注入每个类里边使用了@Inject注解的字段。

原理很简单,就是扫描classes目录下的所有文件和子目录文件,如果是.class文件就检查是否有@Inject这个注解,有就加载,然后在分析这个类,看字段有没有使用了@Inject这个注解的,有就尝试注入(注入是在所有类加载完之后进行的)。

首先,是一个接口:Container.java,定义了容器的一些方法。

代码如下:

packagecom.easyjf.minicontainer;

importjava.util.Collection;importjava.util.List;

importjava.util.Map;

publicinterfaceContainer{

voidinit();

ObjectgetBean(Stringname);

ObjectgetBean(Classtype);

ListgetBeans(Stringname);

ListgetBeans(Classtype);

CollectiongetBeanName();

booleanhasBean(Classclz);

booleanhasBean(Stringname);

voidregistBean(Classclz);

}

这里定义的是容器的通用方法,比如从容器获取一个bean,向容器中注册一个bean等。

接下来是容器类的实现,这个容器很简单,代码集中在初始化过程,初始化的时候会加载类,并注入某些字段。

packagecom.easyjf.minicontainer.impl;

importjava.lang.annotation.Annotation;

importjava.lang.reflect.Field;

importjava.lang.reflect.InvocationTargetException;

importjava.lang.reflect.Method;

importjava.lang.reflect.Modifier;

importjava.util.Collection;

importjava.util.HashMap;

importjava.util.Iterator;

importjava.util.List;

importjava.util.Map;

importorg.apache.log4j.Logger;

importcom.easyjf.minicontainer.Container;

importcom.easyjf.minicontainer.annotation.Inject;

publicclassDefaultContainerimplementsContainer{

privatestaticfinalLoggerlogger=Logger.getLogger(DefaultContainer.class);

privatefinalMapobjMap=newHashMap();

publicObjectgetBean(Stringname){

returnobjMap.get(name);

}

publicObjectgetBean(Classtype){

Iteratorit=this.objMap.values().iterator();

while(it.hasNext()){

Objectobj=it.next();

if(type.isAssignableFrom(obj.getClass())){

returnobj;

}

}

returnnull;

}

publicCollectiongetBeanName(){

returnnull;

}

publicListgetBeans(Stringname){

returnnull;

}

publicListgetBeans(Classtype){

returnnull;

}

publicbooleanhasBean(Classclz){

if(this.getBean(clz)!=null){

returntrue;

}

returnfalse;

}

publicbooleanhasBean(Stringname){

if(this.getBean(name)!=null){

returntrue;

}

returnfalse;

}

publicvoidinit(){

Configconfig=newConfig();

config.init(this);

refresh();

}

publicvoidregistBean(Classclz){

Stringname=clz.getCanonicalName();

try{

if(!Modifier.isAbstract(clz.getModifiers())&&!Modifier.isInterface(clz.getModifiers())){

logger.debug("加载了类:"+name);

Objectobj=clz.newInstance();

objMap.put(name,obj);

}else{

Injectinject=(Inject)clz.getAnnotation(Inject.class);

Stringtaget=inject.target();

if("".equals(taget)){

thrownewRuntimeException(

"接口必须指定目标类!");

}

ClasstagetClz=Class.forName(taget);

ObjecttagetObj=tagetClz.newInstance();

logger.debug("加载了类:"+name);

objMap.put(name,tagetObj);

}

}catch(InstantiationExceptione){

e.printStackTrace();

}catch(IllegalAccessExceptione){

e.printStackTrace();

}catch(ClassNotFoundExceptione){

e.printStackTrace();

}

}

publicvoidrefresh(){

Iteratorit=this.objMap.values().iterator();

while(it.hasNext()){

try{

Objectobj=it.next();

Stringname=obj.getClass().getCanonicalName();

Field[]fields=obj.getClass().getDeclaredFields();

for(Fieldfield:fields){

Annotationinject=field.getAnnotation(Inject.class);

if(inject!=null){

Objectarg=this.getBean(field.getType());

if(arg==null){

thrownewRuntimeException(

"无法加载"+field.getType().getCanonicalName()+"!");

}

StringfieldName=field.getName();

Stringmethodname="set"

+fieldName.substring(0,1).toUpperCase()

+fieldName.substring(1,fieldName.length());

Methodmethod;

method=obj.getClass()

.getDeclaredMethod(methodName,arg.getClass());

if(method!=null){

method.invoke(obj,arg);

}else{

thrownewRuntimeException("无法加载"

+obj.getClass().getCanonicalName()

+"."+field.getName()

+",找不到该字段的set方法!");

}

}

}

}catch(SecurityExceptione){

e.printStackTrace();

}catch(NoSuchMethodExceptione){

e.printStackTrace();

}catch(IllegalArgumentExceptione){

e.printStackTrace();

}catch(IllegalAccessExceptione){

e.printStackTrace();

}catch(InvocationTargetExceptione){

e.printStackTrace();

}

}

}

}

这里主要说说registBean(Classclz)和refresh()这两个方法。registBean(Classclz)是由另外一个类来调用的,这里主要说说这个方法的功能。这个方法接受一个Class类型的参数,然后判断这个Class是不是接口或者抽象类,如果是就根据其@Inject注解中的target值来找到一个类,并初始化这个类。如果不是就直接实例化并且放入容器中。

refresh()方法则是在加载完需要加载的类之后开始执行注入操作。通过迭代容器中的所有bean,来扫描每一个bean,看看是否有需要注入的字段(即使用了@Inject注解的字段),然后执行这个字段的setter方法,实现注入(未实现构造子注入)。

接下来是Config类,这个类的功能主要是扫面classes目录下的文件,找到所有class文件并尝试交给DefaultContainer来加载。

代码如下:

packagecom.easyjf.minicontainer.impl;

importjava.io.File;

importjava.lang.annotation.Annotation;

importjava.util.Map;

importorg.apache.log4j.Logger;

importcom.easyjf.minicontainer.Container;

importcom.easyjf.minicontainer.annotation.Inject;

publicclassConfig{

privatestaticfinalLoggerlogger=Logger.getLogger(Config.class);

privateStringpackagePath;

privateContainercontainer;

publicvoidinit(Containercontainer){

this.container=container;

loadClass();

}

publicvoidloadClass(){

loadClassFromDir(getPackagePath());

}

publicvoidloadClassFromFile(Stringpath){

logger.debug("加载类:"+path);

logger.debug(path.endsWith(".class"));

if(path.endsWith(".class")){

StringclassName=path.replace(packagePath,"");

className=className.replace("/",".");

className=className.substring(1,className.length());

className=className.substring(0,className.indexOf(".class"));

logger.debug(className);

try{

Classclz=Class.forName(className);

Annotationinject=clz.getAnnotation(Inject.class);

if(inject!=null){

container.registBean(clz);

}

}catch(ClassNotFoundExceptione){

e.printStackTrace();

}

}

}

publicvoidloadClassFromDir(Stringpath){

logger.debug("从文件夹"+path+"加载类");

Filefile=newFile(path);

logger.debug("找到目录:"+path);

if(file.isDirectory()){

String[]paths=file.list();

for(StringfilePath:paths){

logger.debug("找到子目录:"+filePath);

loadClassFromDir(path+"/"+filePath);

}

}else{

loadClassFromFile(path);

}

}

publicStringgetPackagePath(){

StringappPath=Config.class.getResource("/").getPath();

appPath=appPath.substring(1,appPath.length()-1);

logger.debug("包路径:"+appPath);

if(appPath.contains("%20")){

appPath=appPath.replaceAll("%20","");

}

this.packagePath=appPath;

returnappPath;

}

}

这个类有三个重要方法:getPackagePath()、loadClassFromDir(Stringpath)、loadClassFromFile(Stringpath)。

getPackagePath()功能是获取当前应用的classes目录所处的绝对路径。loadClassFromDir(Stringpath)方法是从一个目录中加载类,这里有一个递归调用,从而遍历所有子目录。loadClassFromFile(Stringpath)的功能就是从一个文件中加载类。

三个类,实现一个简单的IOC,相信大家都能看懂,其实IOC也就那么回事,我们都能写,哈哈!

代码很少,也写得比较粗糙,问题也很多,见笑了!

完整代码请下载,里边还包含了一个测试实例。

地址:http://www.java3z.com/cwbwebhome/article/article5/5880.html?id=1737

相关推荐