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

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

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
		 * <p>包下所有类的类类型
		 */
		public static List<Class<?>> getClasses(String packageName) {}

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

1.解析xml文件

Jar包

解析xml文件用到dom4j技术

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

0.<beans/>根节点

1.<bean/>对象标签

2.<property/>属性标签

3.<scanner/>扫描标签,使用注解时使用

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

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

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

public class Property {
	private String name;//属性的名称
	private String value;//属性值
	private String ref;//要注入的对象
}
/**
 * 获取xml中的信息
 * @author 百逸同学
 *
 */
public class XmlLoad {
	
	/**
	 * <p>创建根节点对象
	 * @param url
	 * <p>xml文件路径
	 * @return rootElm
	 * <p>返回根节点对象
	 */
	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();
		}
	}
	
	/**
	 * <p>获取bean节点和bean的属性
	 * @param url
	 * <p>xml文件路径
	 * @return beanMap
	 * <p>Map<bean名称,bean所有属性>
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	public static Map<String, Bean> getBeanMap(String url) throws Exception{
		Map<String, Bean> beanMap = new HashMap<String,Bean>();
		List<Element> 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<Property> propertyList = getPropertyList(element);
			bean.setPropertyList(propertyList);
			beanMap.put(bean.getId(), bean);
		}
		return beanMap;
	}
	
	/**
	 * 获取bean子节点property
	 * @param element
	 * 当前bean的Element对象
	 * @return propertyList
	 * List<Element>
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	public static List<Property> getPropertyList(Element element) throws Exception{
		List<Property> propertyList = new ArrayList<Property>();
		List<Element> 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
	 * <p>xml文件路径
	 * @return packageName
	 * <p>返回包名
	 */
	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<String, Object> beanInstanceMap = new HashMap<String, Object>();

	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<Class<?>> classes = PackageUtils.getClasses(packageName);//得到指定包中所有类的类类型集合
				classHandler(classes);//调用类处理方法,处理得到的类集合
			}
			/*2.使用xml配置*/
			Map<String, Bean> beanMap = XmlLoad.getBeanMap(this.url);//得到xml文件中配置的bean
			beanMap.forEach((className, bean) -> {//开始循环
				try {
					Class clazz = Class.forName(bean.getClassName());
					Object object = clazz.newInstance();//先实例当前bean,以便接下来操纵对象
					List<Property> propertyList = bean.getPropertyList();//得到当前bean的property
					/*property分为1.普通类型2.对象类型*/
					if (propertyList != null) {
						Map<String, String> proMap = new HashMap<String, String>();//存放普通类型属性的属性名和值
						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<Class<?>> 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...阅读性实在太差。当然,欢迎各位前辈和小伙伴们对该代码有好的建议,或者更好的改进。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值