天一博客
天一博客
EasyJF官方博客
我的首页
文章
相册
圈子
留言
管理
 
    当前所在页面:首页>>文章>>IOC容器,其实很简单
IOC容器,其实很简单
    作者:天一 来源:www.easyjf.com 发表时间:2007-12-17

 
 

前几天看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也就那么回事,我们都能写,哈哈!

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

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

相关附件下载: src.rar

 
 

(阅读 )   评论数(:2)
评论】 【收藏】
评论:共2条
真有这么简单吗?
评论人: 匿名用户     评论时间: 2007-12-19 14:15:31
原理就是这样,很简单!
评论人: 天一     评论时间: 2007-12-19 17:53:33

发表评论:
发表人:
评论: 
    
 
关于我们 | 诚聘英才 | 联系我们 | 广告业务 | 网站地图 | 法律声明

EasyJF开源团队版权所有  建议使用1024*768分辨率