手写一个spring容器,实现IOC、DI

spring是一站式轻量级的开源框架,“容器”是这个框架特征之一。有了这个容器,对象不再是我们主动创建,而是交给spring容器管理,从而实现程序的解耦。spring容器的核心就是IOC(控制反转)和DI(依赖注入),基于这两个核心,今天自己封装一个spring容器。

手写一个spring容器,实现IOC、DI_第1张图片

0.用到的工具类

工具类所需的Jar包

第一个工具类

public class CommonUtils {
	/**
	 * 字符串首字母转小写
	 * @return
	 */
	public static String lowerFirst(String oldStr) {
		char[] chars = oldStr.toCharArray();
		chars[0] += 32;
		return String.valueOf(chars);
	}

	/**
	 * 将map集合封装到bean
	 * @param map
	 * @param object
	 */
	@SuppressWarnings("rawtypes")
	public static void toBean(Map map, Object object) {
		try {
			BeanUtils.populate(object, map);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}
}

第二个工具类是用于扫描包的下的类,由于能力有限,所以参照博客:https://blog.csdn.net/u013871439/article/details/70231288。这里就放其中两个方法,具体实现请点链接。

public class PackageUtils {

                /**
		 * 从包package中获取所有的Class
		 * 
		 * @param packageName
		 * @return classes
		 * 

包下所有类的类类型 */ public static List> getClasses(String packageName) {} /** * 以文件的形式来获取包下的所有Class * * @param packageName * @param packagePath * @param recursive * @param classes */ private static void findAndAddClassesInPackageByFile(String packageName, String packagePath, final boolean recursive, List> classes) {} }

1.解析xml文件

Jar包

解析xml文件用到dom4j技术

我在test.xml使用到的标签有:

0.根节点

1.对象标签

2.属性标签

3.扫描标签,使用注解时使用

创建两个实体类,用于存放xml文件中的节点和属性信息

节省篇幅只展示字段...

public class Bean {
	private String id;//bean的id
	private String className;//类名+包名
	private List propertyList;//属性集合
}

public class Property {
	private String name;//属性的名称
	private String value;//属性值
	private String ref;//要注入的对象
}
/**
 * 获取xml中的信息
 * @author 百逸同学
 *
 */
public class XmlLoad {
	
	/**
	 * 

创建根节点对象 * @param url *

xml文件路径 * @return rootElm *

返回根节点对象 */ public static Element CreateRootElement(String url) { try { SAXReader saxReader = new SAXReader(); Document document = saxReader.read(new File(url)); Element rootElm = document.getRootElement(); return rootElm; } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(); } } /** *

获取bean节点和bean的属性 * @param url *

xml文件路径 * @return beanMap *

Map * @throws Exception */ @SuppressWarnings("unchecked") public static Map getBeanMap(String url) throws Exception{ Map beanMap = new HashMap(); List elements = CreateRootElement(url).elements("bean"); for (Element element : elements) { Bean bean = new Bean(); if(element.attribute("id") == null){ throw new Exception("bean属性id找不到"); } else { bean.setId(element.attribute("id").getValue()); } if(element.attribute("class") == null){ throw new Exception("bean属性class找不到"); } else { bean.setClassName(element.attribute("class").getValue()); } List propertyList = getPropertyList(element); bean.setPropertyList(propertyList); beanMap.put(bean.getId(), bean); } return beanMap; } /** * 获取bean子节点property * @param element * 当前bean的Element对象 * @return propertyList * List * @throws Exception */ @SuppressWarnings("unchecked") public static List getPropertyList(Element element) throws Exception{ List propertyList = new ArrayList(); List elements = element.elements("property"); for (Element element1 : elements) { Property property = new Property(); if(element1.attribute("name") == null){ throw new Exception("property属性name找不到"); } else { property.setName(element1.attribute("name").getValue()); } if(element1.attribute("value") == null){ property.setValue(null); } else { property.setValue(element1.attribute("value").getValue()); } if(element1.attribute("ref") == null){ property.setRef(null); } else { property.setRef(element1.attribute("ref").getValue()); } propertyList.add(property); } return propertyList; } /** * 获取扫描标签中的包名 * @param url *

xml文件路径 * @return packageName *

返回包名 */ public static String getPackageNameByXML(String url){ Element element = CreateRootElement(url).element("scanner"); if(element == null){ return null; } String packageName= element.attribute("base-package").getValue(); return packageName; } }

XmlLoad.java不做过多说明,就是获取xml中所配置的信息,具体使用方法请看dom4j的API

2.创建我们的容器工厂

//工厂的接口
public interface BeanFactory {
	
	Object getBean(String beanId) throws Exception;
	
	@SuppressWarnings("rawtypes")
	Object getBean(Class clazz) throws Exception;
	
}
/**
 * 容器工厂实现类
 * @author 百逸同学
 *
 */
public class BeanFactoryImpl implements BeanFactory {

	private String url; // xml文件路径
	/**
	 * 存放对象
	 * Map<对象名,对象>
	 * 必须为static
	 */
	private static Map beanInstanceMap = new HashMap();

	public BeanFactoryImpl() {
		super();
	}

	public BeanFactoryImpl(String url) {
		super();
		this.url = url;
		/*
		 * 在构造方法调用实例化方法,实例当前类时就实例容器中的对象
		 * 就像使用spring时, new ClassPathXmlApplicationContext("applicationContext.xml");
		 */
		instance();

	}
	
	/**
	 * 从容器中取出对象
	 */
	@Override
	public Object getBean(String beanId) throws Exception {
		Object object = null;
		if (beanInstanceMap.containsKey(beanId)) {
			object = beanInstanceMap.get(beanId);
		} else {
			throw new Exception("找不到类");
		}
		return object;
	}
	
	@SuppressWarnings("rawtypes")
	@Override
	public Object getBean(Class clazz) throws Exception {
		Object object = null;
		if (beanInstanceMap.containsKey(clazz.getSimpleName().toLowerCase())) {
			object = beanInstanceMap.get(clazz.getSimpleName().toLowerCase());
		} else {
			throw new Exception("找不到类");
		}
		return object;
	}
	
	/**
	 * 实例化对象,该方法在构造方法中调用(构造方法有说明)
	 * 分两种方式
	 * 1.使用注解
	 * 2.使用xml配置
	 */
	@SuppressWarnings({ "rawtypes" })
	private void instance() {
		try {
			/*1.使用注解 */
			String packageName = XmlLoad.getPackageNameByXML(this.url);//获取xml文件中要扫描的包名
			if (packageName != null) {
				List> classes = PackageUtils.getClasses(packageName);//得到指定包中所有类的类类型集合
				classHandler(classes);//调用类处理方法,处理得到的类集合
			}
			/*2.使用xml配置*/
			Map beanMap = XmlLoad.getBeanMap(this.url);//得到xml文件中配置的bean
			beanMap.forEach((className, bean) -> {//开始循环
				try {
					Class clazz = Class.forName(bean.getClassName());
					Object object = clazz.newInstance();//先实例当前bean,以便接下来操纵对象
					List propertyList = bean.getPropertyList();//得到当前bean的property
					/*property分为1.普通类型2.对象类型*/
					if (propertyList != null) {
						Map proMap = new HashMap();//存放普通类型属性的属性名和值
						for (Property property : propertyList) {
							if (property.getValue() != null) {//当property为普通类型
								proMap.put(property.getName(), property.getValue());
							}
							if (property.getRef() != null) {//当property为对象类型
								if (beanInstanceMap.get(property.getRef()) != null) {
									Object diBean = beanInstanceMap.get(property.getRef());//从容器中获取要注入的对象
									PropertyDescriptor pd = new PropertyDescriptor(property.getName(),//通过Java内省机制注入
											object.getClass());
									pd.getWriteMethod().invoke(object, diBean);
								}
							}
						}
						CommonUtils.toBean(proMap, object);//用工具类把普通类型的属性Map(proMap)注入到对象中
					}
					beanInstanceMap.put(bean.getId(), object);//最后把处理过的对象添加到容器
				} catch (Exception e) {
					e.printStackTrace();
				}

			});
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 类处理方法
	 * 当使用注解时,处理指定包下的带有JavaBean(自定义)注解的类
	 * 以及该类下面带有Resource(JDK)注解的属性
	 * @param classes
	 */
	public void classHandler(List> classes) {
		try {
			Object object = null;
			for (Class class1 : classes) {
				JavaBean annotation = class1.getAnnotation(JavaBean.class);
				if (annotation != null) {
					object = class1.newInstance();
					Field[] fields = class1.getDeclaredFields();
					for (Field field : fields) {
						Resource annotation1 = field.getAnnotation(Resource.class);
						/*如果当前字段有Resource注解,则通过属性名来获取容器中的对象	*/
						if (annotation1 != null) {
							Object diBean = beanInstanceMap.get(field.getName());
							if (diBean != null) {
								PropertyDescriptor pd = new PropertyDescriptor(field.getName(), object.getClass());
								pd.getWriteMethod().invoke(object, diBean);
							}
						}
					}

				}
				if (!annotation.value().trim().equals("")) {//自定义注解JavaBean有Value可以自定义类的名称,如果有则按自定义
					beanInstanceMap.put(annotation.value(), object);
					return;
				}
				beanInstanceMap.put(CommonUtils.lowerFirst(class1.getSimpleName()), object);//没有自定义名称则类名首字母转小写
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

应该不用做过多解释了,代码注释的比较清楚

3.总结

上面的代码模拟了spring帮我们创建对象以及管理对象的过程,对个人来说意义还是蛮大的。写完对spring框架有了一个新的认识,也不再觉得spring框架抽象难以理解。编码过程中用到的大量反射,让我使用反射的技术有所提高,也再一次感受到Java反射机制的强大与神奇。同时,对面向对象的理解更加深入。

当然,上面的代码封装的并不好。规范也较差,如if套for又套if...阅读性实在太差。当然,欢迎各位前辈和小伙伴们对该代码有好的建议,或者更好的改进。

你可能感兴趣的:(spring框架)