Skip to content

Latest commit

 

History

History
304 lines (252 loc) · 6.97 KB

3.myioc.md

File metadata and controls

304 lines (252 loc) · 6.97 KB

设计一个IOC

我们要自己设计一个IOC,那么目标是什么呢? 我们的IOC容器要可以存储对象,还要有注解注入的功能即可。

首先设计接口,一个IOC容器中最核心的当属容器接口,来一个Container。

那么容器里应该有什么呢,我想它至少要有存储和移除一个对象的能力,其次可以含括更多的获取和注册对象的方法。

/**
 * IOC容器
 * @author biezhi
 *
 */
public interface Container {

	/**
	 * 根据Class获取Bean
	 * @param clazz
	 * @return
	 */
	public <T> T getBean(Class<T> clazz);
	
	/**
	 * 根据名称获取Bean
	 * @param name
	 * @return
	 */
	public <T> T getBeanByName(String name);
	
	/**
	 * 注册一个Bean到容器中
	 * @param object
	 */
	public Object registerBean(Object bean);
	
	/**
	 * 注册一个Class到容器中
	 * @param clazz
	 */
	public Object registerBean(Class<?> clazz);
	
	/**
	 * 注册一个带名称的Bean到容器中
	 * @param name
	 * @param bean
	 */
	public Object registerBean(String name, Object bean);
	
	/**
	 * 删除一个bean
	 * @param clazz
	 */
	public void remove(Class<?> clazz);
	
	/**
	 * 根据名称删除一个bean
	 * @param name
	 */
	public void removeByName(String name);
	
	/**
	 * @return	返回所有bean对象名称
	 */
	public Set<String> getBeanNames();
	
	/**
	 * 初始化装配
	 */
	public void initWrited();
}

那么我写一个简单的实现代码:

/**
 * 容器简单实现
 * @author biezhi
 */
@SuppressWarnings("unchecked")
public class SampleContainer implements Container {
	
	/**
     * 保存所有bean对象,格式为 com.xxx.Person : @52x2xa
     */
    private Map<String, Object> beans;
    
    /**
     * 存储bean和name的关系
     */
    private Map<String, String> beanKeys;
    
    public SampleContainer() {
    	this.beans = new ConcurrentHashMap<String, Object>();
    	this.beanKeys = new ConcurrentHashMap<String, String>();
    }
	
	@Override
	public <T> T getBean(Class<T> clazz) {
		String name = clazz.getName();
		Object object = beans.get(name);
		if(null != object){
			return (T) object;
		}
		return null;
	}

	@Override
	public <T> T getBeanByName(String name) {
		Object object = beans.get(name);
		if(null != object){
			return (T) object;
		}
		return null;
	}

	@Override
	public Object registerBean(Object bean) {
		String name = bean.getClass().getName();
		beanKeys.put(name, name);
		beans.put(name, bean);
		return bean;
	}
	
	@Override
	public Object registerBean(Class<?> clazz) {
		String name = clazz.getName();
		beanKeys.put(name, name);
		Object bean = ReflectUtil.newInstance(clazz);
		beans.put(name, bean);
		return bean;
	}

	@Override
	public Object registerBean(String name, Object bean) {
		String className = bean.getClass().getName();
		beanKeys.put(name, className);
		beans.put(className, bean);
		return bean;
	}

	@Override
	public Set<String> getBeanNames() {
		return beanKeys.keySet();
	}

	@Override
	public void remove(Class<?> clazz) {
		String className = clazz.getName();
		if(null != className && !className.equals("")){
			beanKeys.remove(className);
			beans.remove(className);
		}
	}

	@Override
	public void removeByName(String name) {
		String className = beanKeys.get(name);
		if(null != className && !className.equals("")){
			beanKeys.remove(name);
			beans.remove(className);
		}
	}

	@Override
	public void initWrited() {
		Iterator<Entry<String, Object>> it = beans.entrySet().iterator();
        while (it.hasNext()) {
			Map.Entry<String, Object> entry = (Map.Entry<String, Object>) it.next();
			Object object = entry.getValue();
			injection(object);
		}
	}

	/**
	 * 注入对象
	 * @param object
	 */
	public void injection(Object object) {
		// 所有字段
	    try {
			Field[] fields = object.getClass().getDeclaredFields();
			for (Field field : fields) {
				// 需要注入的字段
				AutoWrited autoWrited = field.getAnnotation(AutoWrited.class);
			    if (null != autoWrited) {
			    	
			    	// 要注入的字段
			        Object autoWritedField = null;
			        
			        String name = autoWrited.name();
	        		if(!name.equals("")){
	        			String className = beanKeys.get(name);
	        			if(null != className && !className.equals("")){
	        				autoWritedField = beans.get(className);
	        			}
	        			if (null == autoWritedField) {
				            throw new RuntimeException("Unable to load " + name);
				        }
	        		} else {
	        			if(autoWrited.value() == Class.class){
	        				autoWritedField = recursiveAssembly(field.getType());
				        } else {
				        	// 指定装配的类
				    		autoWritedField = this.getBean(autoWrited.value());
				            if (null == autoWritedField) {
				            	autoWritedField = recursiveAssembly(autoWrited.value());
				            }
						}
					}
	        		
			        if (null == autoWritedField) {
			            throw new RuntimeException("Unable to load " + field.getType().getCanonicalName());
			        }
			        
			        boolean accessible = field.isAccessible();
			        field.setAccessible(true);
			        field.set(object, autoWritedField);
			        field.setAccessible(accessible);
			    }
			}
		} catch (SecurityException e) {
        	e.printStackTrace();
        } catch (IllegalArgumentException e) {
        	e.printStackTrace();
        } catch (IllegalAccessException e) {
        	e.printStackTrace();
        }
	}
	
	private Object recursiveAssembly(Class<?> clazz){
    	if(null != clazz){
    		return this.registerBean(clazz);
    	}
    	return null;
    }

}

这里将所有Bean的名称存储在 beanKeys 这个map中,将所有的对象存储在 beans 中,用 beanKeys 维护名称和对象的关系。

在装配的时候步骤如下:

  1. 判断是否使用了自定义命名的对象(是:根据name查找bean)
  2. 判断是否使用了Class类型Bean(是:根据Class查找Bean,如果查找不到则创建一个无参构造函数的Bean)

下面是一个测试:

public class IocTest {

	private static Container container = new SampleContainer();
	
	public static void baseTest(){
		container.registerBean(Lol.class);
		// 初始化注入
		container.initWrited();
		
		Lol lol = container.getBean(Lol.class);
		lol.work();
	}
	
	public static void iocClassTest(){
		container.registerBean(Lol2.class);
		// 初始化注入
		container.initWrited();
		
		Lol2 lol = container.getBean(Lol2.class);
		lol.work();
	}
	
	public static void iocNameTest(){
		container.registerBean("face", new FaceService2());
		container.registerBean(Lol3.class);
		// 初始化注入
		container.initWrited();
		
		Lol3 lol = container.getBean(Lol3.class);
		lol.work();
	}
	
	public static void main(String[] args) {
		baseTest();
		//iocClassTest();
		//iocNameTest();
	}
	
}

输出结果:

剑圣买了5毛钱特效,装逼成功!

代码出处

links