一、前言
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 } }