1.spring管理bean的原理
spring是如何帮我们创建和管理bean的呢?
首先读取配置文件获取bean对象。可以通过dom4j来读取配置文件,获取文档下所有bean节点,bean节点的id和class的属性值构造出bean对象,将所有bean对象装到一个List集合中。
然后完成bean的实例化,将bean节点的id属性作为key,bean对象作为值,存到map中。
最后我们就可以通过map获取bean实例了。
具体代码如下所示:
package junit.test;
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;
/**
* 传智博客版容器
* @author Lee
*
*/
public class ItcastClassPathXMLApplicationContext {
private List<BeanDefinition> beanDefines = new ArrayList<BeanDefinition>();
private Map<String, Object> sigletons = new HashMap<String, Object>();
/**
*
* @param filename 指定配置文件的名称
*/
public ItcastClassPathXMLApplicationContext(String filename) {
this.readXML(filename);
this.instanceBeans();
}
/**
* 完成Baen的实例化
*/
private void instanceBeans() {
for(BeanDefinition beanDefinition : beanDefines){
try {
if(beanDefinition.getClassName() != null && !"".equals(beanDefinition.getClassName()));
sigletons.put(beanDefinition.getId(), Class.forName(beanDefinition.getClassName()).newInstance());
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 使用dom4j读取spring配置文件
* @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); // 设置命名空间
@SuppressWarnings("unchecked")
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);
}
}
2.spring的三种实例化bean的方式
1.使用类构造器实例化
<bean id="personService" class="cn.itcast.service.impl.PersonServiceBean"/>
2.使用静态工厂方法实例化
在工厂类里面有一个静态工厂方法,这个静态工厂方法专门用于创建bean对象的。在配置文件bean节点里面,class指定的就是工厂类,由factory-method制定静态工厂方法。
<bean id="personService2" class="cn.itcast.service.impl.PersonServiceBeanFactory" factory-method="createPersonServiceBean"/>
package cn.itcast.service.impl;
public class PersonServiceBeanFactory {
public static PersonServiceBean createPersonServiceBean() {
return new PersonServiceBean();
}
}
3.使用实例工厂方法实例化
在配置文件里面,先实例化工厂,然后使用工厂bean,调用它里面的专门用来创建bean的工厂方法来创建bean.
<bean id="personServiceFactory" class="cn.itcast.service.impl.PersonServiceBeanFactory"/> <!-- 将工厂类交给bean管理,实例化工厂类 -->
<bean id="personService3" factory-bean="personServiceFactory" factory-method="createPersonService"/> <!-- 通过工厂里面的方法来创建bean -->
public PersonServiceBean createPersonService() {
return new PersonServiceBean();
}
最后我们来测试一下,测试代码如下:
@Test
public void instanceSpring() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
PersonService personService = (PersonService)ctx.getBean("personService");
personService.save();
}
@Test
public void instanceSpring2() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
PersonService personService = (PersonService)ctx.getBean("personService2");
personService.save();
}
@Test
public void instanceSpring3() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
PersonService personService = (PersonService)ctx.getBean("personService3");
personService.save();
}
测试结果如下:
三种实例化都可以,绝大部分都使用第一种实例化方案。
3.配置spring管理的bean的作用域
在前面,我们将bean交给spring容器进行管理,在客户端,我们只需要调用getBean方法就能获取beanbean实例。那么两次获取的实例是不是同一个实例呢?我们来测试一下
@Test
public void instanceSpring4() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
PersonService personService1 = (PersonService)ctx.getBean("personService");
PersonService personService2 = (PersonService)ctx.getBean("personService");
System.out.println(personService1 == personService2);;
}
控制台打印结果为true.则说明,默认情况下,这是一个单实例。
如果我们想每个getBean方法都获取到一个新的实例,这个时候我们该怎么做呢?这个就涉及到bean的作用域。
.singleton
在每个Spring IoC容器中一个bean定义只有一个对象实例。默认情况下会在容器启动时初始化bean,但我们可以指定Bean节点的lazy-int="true"来延迟初始化bean,这时候,只有第一次获取bean会才初始化bean。如:
<bean id="personService" class="cn.itcast.service.impl.PersonServiceBean" lazy-init="true"/>
如果想对所有bean都应用延迟初始化,可以在根节点beans设置default-lazy-init="true",如下
<beans default-lazy-init="true" ...>
如果我们没有设置作用域的时候,默认的就是singleton作用的范围
.prototype
每次从容器获取bean都是新的对象。
通过scope来指定作用于范围。如:
<bean id="personService" class="cn.itcast.service.impl.PersonServiceBean" scope="prototype"/>
@Test
public void instanceSpring4() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
PersonService personService1 = (PersonService)ctx.getBean("personService");
PersonService personService2 = (PersonService)ctx.getBean("personService");
System.out.println(personService1 == personService2);;
}
这个时候控制台打印出来的就是false.表示每调用一次getBean方法,它都会返回一个新的bean对象。
4.spring管理的bean的生命周期
当bean的作用域范围为单实例时,在什么时候进行实例化的呢?
在默认构造函数中输出一句话,就可以知道了。
public PersonServiceBean() {
System.out.println("我被初始化了...");
}
所有的bean节点:
<bean id="personService" class="cn.itcast.service.impl.PersonServiceBean"/>
<bean id="personService2" class="cn.itcast.service.impl.PersonServiceBeanFactory"
factory-method="createPersonServiceBean"/>
<bean id="personServiceFactory" class="cn.itcast.service.impl.PersonServiceBeanFactory"/>
测试代码:
@Test
public void instanceSpring() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
//PersonService personService = (PersonService)ctx.getBean("personService");
//personService.save();
}
后台打印结果为:
所以当作用于为singleton时,bean是在bean容器实例化时实例化的。
那么作用域为prototype时,bean是在什么时候实例化的呢?
将配置文件修改为:
<bean id="personService" class="cn.itcast.service.impl.PersonServiceBean" scope="prototype"/>
<bean id="personService2" class="cn.itcast.service.impl.PersonServiceBeanFactory"
factory-method="createPersonServiceBean" scope="prototype"/>
<bean id="personServiceFactory" class="cn.itcast.service.impl.PersonServiceBeanFactory" scope="prototype"/>
后,控制台打印结果为:
我们在来修改测试代码如下:
@Test
public void instanceSpring() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
PersonService personService = (PersonService)ctx.getBean("personService");
//personService.save();
}
后台打印结果为:
所以作用域为prototype时,bean在调用getBean方法时初始化。
建议少用初始化延迟,这样不用等到运行期才能发现错误。
初始化资源与销毁bean
如果我们要对某些资源初始化,可以在bean中通过init-method来指定初始化方法
<bean id="personService" class="cn.itcast.service.impl.PersonServiceBean" init-method="init"/>
在bean实例中有该方法:
public void init() {
System.out.println("初始化...");
}
后台打印如下图:
可以在bean节点中用destroy-method来指定销毁方法,在关闭spring容器使就会调用该方法了。
<bean id="personService" class="cn.itcast.service.impl.PersonServiceBean"
init-method="init" destroy-method="destroy"/>
public void destroy(){
System.out.println("销毁...");
}
@Test
public void instanceSpring() {
AbstractApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
ctx.close();
//PersonService personService = (PersonService)ctx.getBean("personService");
//personService.save();
}
后台打印结果如下:
八月 07, 2015 8:04:01 下午 org.springframework.context.support.AbstractApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@47c5ee67: display name [org.springframework.context.support.ClassPathXmlApplicationContext@47c5ee67]; startup date [Fri Aug 07 20:04:01 CST 2015]; root of context hierarchy
八月 07, 2015 8:04:01 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [beans.xml]
八月 07, 2015 8:04:01 下午 org.springframework.context.support.AbstractApplicationContext obtainFreshBeanFactory
信息: Bean factory for application context [org.springframework.context.support.ClassPathXmlApplicationContext@47c5ee67]: org.springframework.beans.factory.support.DefaultListableBeanFactory@58cfbd2d
八月 07, 2015 8:04:01 下午 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
信息: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@58cfbd2d: defining beans [personService,personService2,personServiceFactory,personService3]; root of factory hierarchy
我被实例化了...
初始化...
八月 07, 2015 8:04:01 下午 org.springframework.context.support.AbstractApplicationContext doClose
信息: Closing org.springframework.context.support.ClassPathXmlApplicationContext@47c5ee67: display name [org.springframework.context.support.ClassPathXmlApplicationContext@47c5ee67]; startup date [Fri Aug 07 20:04:01 CST 2015]; root of context hierarchy
八月 07, 2015 8:04:01 下午 org.springframework.beans.factory.support.DefaultSingletonBeanRegistry destroySingletons
信息: Destroying singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@58cfbd2d: defining beans [personService,personService2,personServiceFactory,personService3]; root of factory hierarchy
销毁...
JDWP exit error JVMTI_ERROR_WRONG_PHASE(112): on checking for an interface [../../../src/share/back/util.c:1311]