spring的bean

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]




  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Zerlinda_Li

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

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

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

打赏作者

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

抵扣说明:

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

余额充值