一、前言
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依赖包
② 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
}
}