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