IOC容器,其实很简单

canca16年前 (2008-03-28)Spring284
 

前几天看easyjweb代码,看到容器那块,顺便自己也写了个简单的IOC容器练练手。今天正好有时间就写出来大家看看,我也好冒充一下高手。

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

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

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

代码如下:

package com.easyjf.minicontainer; import java.util.Collection; import java.util.List; import java.util.Map; public interface Container { void init(); Object getBean(String name); Object getBean(Class type); List getBeans(String name); List getBeans(Class type); Collection getBeanName(); boolean hasBean(Class clz); boolean hasBean(String name); void registBean(Class clz); }

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

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

package com.easyjf.minicontainer.impl; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.apache.log4j.Logger; import com.easyjf.minicontainer.Container; import com.easyjf.minicontainer.annotation.Inject; public class DefaultContainer implements Container { private static final Logger logger = Logger.getLogger(DefaultContainer.class); private final Map objMap = new HashMap(); public Object getBean(String name) { return objMap.get(name); } public Object getBean(Class type) { Iterator it = this.objMap.values().iterator(); while(it.hasNext()){ Object obj = it.next(); if(type.isAssignableFrom(obj.getClass())){ return obj; } } return null; } public Collection getBeanName() { return null; } public List getBeans(String name) { return null; } public List getBeans(Class type) { return null; } public boolean hasBean(Class clz) { if(this.getBean(clz)!=null){ return true; } return false; } public boolean hasBean(String name){ if(this.getBean(name)!=null){ return true; } return false; } public void init() { Config config = new Config(); config.init(this); refresh(); } public void registBean(Class clz){ String name = clz.getCanonicalName(); try { if(!Modifier.isAbstract(clz.getModifiers())&&!Modifier.isInterface(clz.getModifiers())){ logger.debug("加载了类:"+name); Object obj = clz.newInstance(); objMap.put(name, obj); }else{ Inject inject = (Inject)clz.getAnnotation(Inject.class); String taget = inject.target(); if("".equals(taget)){ throw new RuntimeException( "接口必须指定目标类!"); } Class tagetClz = Class.forName(taget); Object tagetObj = tagetClz.newInstance(); logger.debug("加载了类:"+name); objMap.put(name, tagetObj); } } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public void refresh(){ Iterator it = this.objMap.values().iterator(); while(it.hasNext()){ try { Object obj = it.next(); String name = obj.getClass().getCanonicalName(); Field[] fields = obj.getClass().getDeclaredFields(); for(Field field : fields){ Annotation inject = field.getAnnotation(Inject.class); if(inject!=null){ Object arg = this.getBean(field.getType()); if(arg==null){ throw new RuntimeException( "无法加载"+field.getType().getCanonicalName()+"!"); } String fieldName = field.getName(); String methodName = "set" +fieldName.substring(0, 1).toUpperCase() +fieldName.substring(1, fieldName.length()); Method method; method = obj.getClass() .getDeclaredMethod(methodName, arg.getClass()); if(method != null){ method.invoke(obj, arg); }else{ throw new RuntimeException("无法加载" +obj.getClass().getCanonicalName() +"."+field.getName() +",找不到该字段的set方法!"); } } } } catch (SecurityException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } } }

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

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

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

代码如下:

package com.easyjf.minicontainer.impl; import java.io.File; import java.lang.annotation.Annotation; import java.util.Map; import org.apache.log4j.Logger; import com.easyjf.minicontainer.Container; import com.easyjf.minicontainer.annotation.Inject; public class Config { private static final Logger logger = Logger.getLogger(Config.class); private String packagePath; private Container container; public void init(Container container){ this.container = container; loadClass(); } public void loadClass(){ loadClassFromDir(getPackagePath()); } public void loadClassFromFile(String path){ logger.debug("加载类:"+path); logger.debug(path.endsWith(".class")); if(path.endsWith(".class")){ String className = path.replace(packagePath, ""); className = className.replace("/", "."); className = className.substring(1, className.length()); className = className.substring(0, className.indexOf(".class")); logger.debug(className); try { Class clz = Class.forName(className); Annotation inject = clz.getAnnotation(Inject.class); if(inject!=null){ container.registBean(clz); } } catch (ClassNotFoundException e) { e.printStackTrace(); } } } public void loadClassFromDir(String path){ logger.debug("从文件夹"+path+"加载类"); File file = new File(path); logger.debug("找到目录:"+path); if(file.isDirectory()){ String[] paths = file.list(); for(String filePath : paths){ logger.debug("找到子目录:"+filePath); loadClassFromDir(path+"/"+filePath); } }else{ loadClassFromFile(path); } } public String getPackagePath(){ String appPath = 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; return appPath; } }

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

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

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

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

附件中我会给出完整代码的下载,里边还包含了一个测试实例。

 

本文转自:http://www.easyjf.com/blog/html/20071217/884740.html

相关文章

Spring OpenSessionInView

OpenSessionInViewFilter是Spring提供的一个针对Hibernate的一个支持类,其主要意思是在发起一个页面请求时打开Hibernate的Session,一直保持这个...

Spring RMI 支持

RMI是从JDK 1.1开始就出现的API功能,它让客户端在使用远程对象所提供的服务时,就如何使用本地对象一样,然而RMI在使用时必须一连串繁复的手续,像是服务介 面在定义时必须继承java.rmi....

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。