纯手写Spring之IoC-----基于XML依赖注入

本文通过手写简易版 Spring IoC 容器,详细介绍了如何基于 XML 配置文件解析 Bean 并实例化对象的过程。从 Maven 项目的搭建到自定义 ClassPathXmlApplicationContext 类的实现,再到最终的测试验证。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

首先我们回忆一下在我们学习spring或者在我们做项目的时候,肯定都用过基于xml配置bean的方式来配置bean,然后在dao层获取sessionFactory、在service层/biz层获取dao对象、在action层、controller层获取service对象。用的时候都知道在xml里面配置一下bean,给它id和class,在需要注入的bean下面配置上property。那么spring是怎么去帮你创建对象的呢?

看下面代码之前我们先来说说原理:

我们的web项目在启动的时候就会去读取spring的配置文件applicationContext.xml,使用类似于dom4j工具将xml里面的内容读取到java代码里面来,然后根据你传递过来的bean的id和xml里面的bean的id进行匹配,如果发现有,就会把创建的对象传递给你。当然了,我们都知道Spring的bean的作用域常用的有两种,一种是Prototype,一种是Singleton。

Prototype:原型模式,容器初始化的时候并不会创建bean对象,而在getBean的时候创建, 如果多次getBean,则会创建多个bean对象。

Singleton:单例模式,

lazy-init为false(默认是false,所以可以不用设置),则 ApplicationContext启动的时候就实例化该Bean,并且将实例化的Bean放在一个map结构的缓存中,下次再使用该Bean的时候, 直接从这个缓存中取

lazy-init为true,则该Bean的实例化是在第一次使用该Bean的时候进行实例化

下面这个例子就是基于原型模式实现的。

新建一个maven项目

maven依赖的包

<dependencies>
		<!-- 引入Spring-AOP等相关Jar -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-core</artifactId>
			<version>3.0.6.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>3.0.6.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-aop</artifactId>
			<version>3.0.6.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-orm</artifactId>
			<version>3.0.6.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjrt</artifactId>
			<version>1.6.1</version>
		</dependency>
		<dependency>
			<groupId>aspectj</groupId>
			<artifactId>aspectjweaver</artifactId>
			<version>1.5.3</version>
		</dependency>
		<dependency>
			<groupId>cglib</groupId>
			<artifactId>cglib</artifactId>
			<version>2.1_2</version>
		</dependency>

		<!-- https://mvnrepository.com/artifact/com.mchange/c3p0 -->
		<dependency>
			<groupId>com.mchange</groupId>
			<artifactId>c3p0</artifactId>
			<version>0.9.5.2</version>
		</dependency>
		<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.37</version>
		</dependency>

		<!-- https://mvnrepository.com/artifact/dom4j/dom4j -->
		<dependency>
			<groupId>dom4j</groupId>
			<artifactId>dom4j</artifactId>
			<version>1.6.1</version>
		</dependency>
		<!-- https://mvnrepository.com/artifact/commons-lang/commons-lang -->
		<dependency>
			<groupId>commons-lang</groupId>
			<artifactId>commons-lang</artifactId>
			<version>2.6</version>
		</dependency>

	</dependencies>

 接口的定义:


public interface UserService {
	public void add();
	public String update();

}

接口的实现类


public class UserServiceImpl implements UserService {

	public void add() {
		System.out.println("执行了add方法");

	}
	public String update() {
		System.out.println("执行了update方法");
		return "abcd";
	}

}

自定义的ClassPathXmlApplicationContext.class

import java.io.InputStream;
import java.util.List;

import org.apache.commons.lang.StringUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

/**
 * 手写Spring IoC xml获取bean方式
 * @author liuz
 *
 */
public class EasyClassPathXmlApplicationContext {
	private String xmlPath;
	public EasyClassPathXmlApplicationContext(String xmlPath) {
		this.xmlPath=xmlPath;
		
	}
	public Object getBean(String beanId) throws Exception{
		//解析xml 获取所有的bean节点信息
		if(beanId.equals("")||StringUtils.isEmpty(beanId)){
			throw new Exception("beanId不能为空!");
			
		}
		List<Element> readXML=readXML();
		if(readXML==null||readXML.isEmpty()){
			throw new Exception("xml中未配置bean!");
		}
		String className=findByElementClass(readXML,beanId);
		if(StringUtils.isEmpty(className)){
			throw new Exception("该bean对象没有配置class");
		}
	
		//3.获取class信息,使用反射初始化
		return newInstance(className);
	}
	/**
	 * 读取xml里面的节点 使用xml文件中bean的id跟传递过俩的id进行比较
	 * @param readXML
	 * @param beanId
	 * @return class 地址
	 * @throws Exception 
	 */
	public String findByElementClass(List<Element> readXML,String beanId) throws Exception{
		for(Element element:readXML){
			//获取属性信息
		String xmlBeanId=element.attributeValue("id");
		if(StringUtils.isEmpty(xmlBeanId)){
			throw new Exception("为获取到bean节点信息");
		}
		if(xmlBeanId.equals(beanId)){
			String xmlClass=element.attributeValue("class");
			return xmlClass;
			//return newInstance(xmlClass);
		}
		}
		return null;
	}
	public Object newInstance(String className) throws ClassNotFoundException,
		InstantiationException, IllegalAccessException{
	Class<?> classInfo=Class.forName(className);
	return classInfo.newInstance();
	}
	/**
	 * 解析xml
	 * @throws DocumentException 
	 */
	@SuppressWarnings("unchecked")
	public List<Element> readXML() throws DocumentException{
		SAXReader saxReader = new SAXReader();
		//1.解析xml文件
		Document document = saxReader.read(getResourceAsStream(xmlPath));
		//读取跟节点
		Element rootElement = document.getRootElement();
		// 获取下面的子节点
		List<Element> elements=rootElement.elements();
		return elements;
		
	}
	/**
	 * 获取当前上下文路径
	 * @param xmlPath
	 * @return
	 */
	public InputStream getResourceAsStream(String xmlPath) {
		return this.getClass().getClassLoader().getResourceAsStream(xmlPath);
	}

}

测试类:

public class Test {
	public static void main(String[] args){
		EasyClassPathXmlApplicationContext app = new EasyClassPathXmlApplicationContext(
				"applicationContext.xml");
		UserService userService;
		try {
			userService = (UserService)app.getBean("userService");
			userService.add();
			userService.update();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	
		
	}

}

运行效果:

源代码下载地址:

链接:https://pan.baidu.com/s/1VsDeLWnHDRtJ4ogITGkMZg 密码:f3i4

具体的实现过程如果看不懂也可以去看余胜军老师的视频。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

sonwing_for

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值