深入探索spring技术内幕(二): 剖析spring管理Bean的原理与配置

一、前言

spring是如何管理Bean的? 想必这是每一个初学spring的同学想弄清楚的问题, 好吧, 网上百度一下你会得到这样的答案:

服务启动时, 容器会解析配置文件, 并且会通过反射机制实例化配置中所有的类, 然后我们可以通过下面的方法获取Bean:

ApplicationContext ctx = new ClassPathXmlApplicationContext("spring.xml");
MyService myService = (MyService) ctx.getBean("myService");


二、模拟spring管理Bean

如果你只满足于此, 那就太没意思了, 要不咱自己模拟一下看看?


① 准备工作: 导Jar包

dom4j-1.6.1.jar 使用dom4j解析xml

jaxen-1.1-beta-6.jar 这是一个dom4j依赖包

深入探索spring技术内幕(二): 剖析spring管理Bean的原理与配置_第1张图片


② Bean对象定义封装

/**
 * Bean对象定义
 * @author zhangjim
 */
public class BeanDefinition {
	private String id;
	private String className;

	public BeanDefinition(String id, String className) {
		this.id = id;
		this.className = className;
	}

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public String getClassName() {
		return className;
	}

	public void setClassName(String className) {
		this.className = className;
	}
}

③ Bean工厂

import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.XPath;
import org.dom4j.io.SAXReader;
/**
 * Bean的工厂
 * @author zhangjim
 */
public class ClassPathXMLApplicationContext {
	private List<BeanDefinition> beanDefines = new ArrayList<BeanDefinition>();
	private Map<String, Object> sigletons = new HashMap<String, Object>();

	public ClassPathXMLApplicationContext(String filename) {
		this.readXML(filename);
		this.instanceBeans();
	}

	/**
	 * 完成bean的实例化
	 */
	private void instanceBeans() {
		for (BeanDefinition beanDefinition : beanDefines) {
			try {
				if (beanDefinition.getClassName() != null && !"".equals(beanDefinition.getClassName().trim()))
					sigletons.put(beanDefinition.getId(), Class.forName(beanDefinition.getClassName()).newInstance());
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

	}

	/**
	 * dom4j读取xml配置文件
	 * @param filename
	 */
	private void readXML(String filename) {
		SAXReader saxReader = new SAXReader();
		Document document = null;
		try {
			URL xmlpath = this.getClass().getClassLoader().getResource(filename);
			document = saxReader.read(xmlpath);
			Map<String, String> nsMap = new HashMap<String, String>();
			nsMap.put("ns", "http://www.springframework.org/schema/beans");// 加入命名空间
			XPath xsub = document.createXPath("//ns:beans/ns:bean");// 创建beans/bean查询路径
			xsub.setNamespaceURIs(nsMap);// 设置命名空间
			List<Element> beans = xsub.selectNodes(document);// 获取文档下所有bean节点
			for (Element element : beans) {
				String id = element.attributeValue("id");// 获取id属性值
				String clazz = element.attributeValue("class"); // 获取class属性值
				BeanDefinition beanDefine = new BeanDefinition(id, clazz);
				beanDefines.add(beanDefine);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 获取bean实例
	 * @param beanName
	 * @return
	 */
	public Object getBean(String beanName) {
		return this.sigletons.get(beanName);
	}
}

1. 读取配置文件, 数据封装到BeanDefinition

2. 将BeanDefinition对象存入list

3. 遍历list, 通过反射产生对象并存入map

4. 调用getBean方法返回一个对象, 此对象为单例


④ 配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
	<bean id="helloService" class="com.zdp.service.HelloService"></bean>
</beans>

⑤ 测试一下

import com.zdp.myspring.ClassPathXMLApplicationContext;

public class  HelloService{
	
	public void sayHello() {
		System.out.println("say hello..."); 
	}
	
	public static void main(String[] args) {
		ClassPathXMLApplicationContext ctx = new ClassPathXMLApplicationContext("beans.xml");
		HelloService helloService = (HelloService) ctx.getBean("helloService");
		helloService.sayHello();
	}
}


三、spring三种实例化Bean的方式

① 使用类构造器实例化Bean, IoC容器使用默认空构造器

<bean id="helloBean1" class="com.zdp.service.impl.HelloBean"/>


② 使用静态工厂方法实例化Bean

<bean id="helloBean2" class="com.zdp.factory.HelloBeanStaticFactory" factory-method="createHelloBean"/>

public class HelloBeanStaticFactory {
	public static Hello createHelloBean() {
		return new HelloBean();
	}
}


③ 使用实例工厂方法实例化Bean

<bean id="instanceFactory" class="com.zdp.factory.HelloBeanInstanceFactory"/> 
<bean id="helloBean3" factory-bean="instanceFactory" factory-method="createHelloBean"/>

public class HelloBeanInstanceFactory {
	public Hello createHelloBean() {
		return new HelloBean();
	}
}


四、配置spring管理Bean的作用域

① singleton

<bean id="xxx" class="xxx" scope="singleton" />

只有一个对象实例, 默认情况下回再容器启动时初始化Bean, 但我们可以配置延迟初始化Bean


② prototype

<bean id="xxx" class="xxx" scope="prototype" />

每次从容器中获取Bean都是新的对象.


③ request

<bean id="xxx" class="xxx" scope="request" />

作用域基于web下有效


④ session

<bean id="xxx" class="xxx" scope="session" />

作用域基于web下有效


五、spring管理Bean的生命周期

Bean默认是在容器初始化时就创建, 如果配置了延迟加载, 则会延迟Bean的实例化

<bean id="xxx" class="xxx" scope="singleton" lazy-init="true" />   <!-- 只有第一次获取Bean才会初始化Bean -->

<beans default-lazy-init="true">     <!-- 对所有Bean都应用延迟初始化 -->


init和destory:

<bean id="xxx" class="xxx" scope="singleton" init-method="init" destroy-method="destory" />

public class HelloBeanTest {
	@Test
	public void testSayHello() {
		AbstractApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
        Hello hello =(Hello) ctx.getBean("helloBean");  // 调用init
        hello.sayHello();
        ctx.close(); // 调用destory
	}
}





你可能感兴趣的:(深入探索spring技术内幕(二): 剖析spring管理Bean的原理与配置)