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();}
3.配置spring管理的bean的作用域
在前面,我们将bean交给spring容器进行管理,在客户端,我们只需要调用getBean方法就能获取beanbean实例。那么两次获取的实例是不是同一个实例呢?我们来测试一下
@Testpublic 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"/>
@Testpublic 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"/>
测试代码:
@Testpublic 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"/>
后,控制台打印结果为:
我们在来修改测试代码如下:
@Testpublic 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("初始化...");}
spring Bean的五种scope
scope用来声明IOC容器中的对象应该处的限定场景或者说该对象的存活空间,即在IOC容器在对象进入相应的scope之前,生成并装配这些对象,在该对象不再处于这些scope的限定之后,容器通常会销毁这些对象。打个比方吧!我们都是处在社会(容器)中,如果把中学教师作为一个类定义,那么当容器初始化这些类之后,中学教师只能局限在中学这个场景中,中学,就可以看做中学教师的scope。
Spring容器最初提供了两种bean的scope类型:singleton和prototype,但发布2.0之后,又引入了另外三种scope类型,即request,session和global session类型。不过这三种类型有所限制,只能在web应用中使用,也就是说,只有在支持web应用的ApplicationContext中使用这三个scope才是合理的。
可以使用bean的singleton或scope属性来指定相应对象的scope,其中,scope属性只能在XSD格式的文档生命中使用,类似于如下代码所演示的形式:
DTD:
<bean id ="mockObject1" class="..." singleton="false" />
XSD:
<bean id ="mockObject1" class="..." scope="prototype" />
注意:这里的singleton和设计模式里面的单例模式不一样,标记为singleton的bean是由容器来保证这种类型的bean在同一个容器内只存在一个共享实例,而单例模式则是保证在同一个Classloader中只存在一个这种类型的实例。
1. singleton
singleton类型的bean定义,在一个容器中只存在一个实例,所有对该类型bean的依赖都引用这一单一实例,这就好像每个幼儿园都会有一个滑梯一样,这个幼儿园的小朋友共同使用这一个滑梯,而对于幼儿园容器来说,滑梯就是一个singleton的bean。
此外,singleton类型的bean定义,从容器启动,到他第一次被请求而实例化开始,只要容器不销毁或退出,该类型的bean的单一实例就会一直存活。
通常情况下,如果你不指定bean的scope,singleton便是容器默认的scope,所以,下面三种配置,形式实际上达成的是同样的效果:
DTD or XSD:
<bean id ="mockObject1" class="..." />
DTD:
<bean id ="mockObject1" class="..." singleton="true" />
XSD:
<bean id ="mockObject1" class="..." scope="singleton" />
2 prototype
scope为prototype的bean,容器在接受到该类型的对象的请求的时候,会每次都重新生成一个新的对象给请求方,虽然这种类型的对象的实例化以及属性设置等工作都是由容器负责的,但是只要准备完毕,并且对象实例返回给请求方之后,容器就不在拥有当前对象的引用,请求方需要自己负责当前对象后继生命周期的管理工作,包括该对象的销毁。也就是说,容器每次返回请求方该对象的一个新的实例之后,就由这个对象“自生自灭”了。
让我们继续幼儿园的比喻,我们今天要分苹果了!将苹果的bean的scope属性声明为prototype,在每个小朋友领取苹果的时候,我们都是发一个新的苹果给他,发完之后,小朋友爱怎么吃就怎么吃,爱什么时候吃什么时候吃,但是注意吃完要把果核扔到垃圾箱哦!对于那些不能共享使用的对象类型,应该将其定义的scope设为prototype,通常,声明为prototype的的bean,都是一些有状态的,比如保存为每个顾客信息的对象。
可以用一下方式定义prototype类型的bean:
DTD:
<bean id ="mockObject1" class="..." singleton="false" />
XSD:
<bean id ="mockObject1" class="..." scope="prototype" />
3 request ,session和global session
这三个类型是spring2.0之后新增的,他们不像singleton和prototype那么通用,因为他们只适用于web程序,通常是和XmlWebApplicationContext共同使用,将在第6章详细讨论,这里简单介绍。
request:
<bean id ="requestPrecessor" class="...RequestPrecessor" scope="request" />
Spring容器,即XmlWebApplicationContext 会为每个HTTP请求创建一个全新的RequestPrecessor对象,当请求结束后,,该对象的生命周期即告结束。当同时有10个HTTP请求进来的时候,容器会分别针对这10个请求创建10个全新的RequestPrecessor实例,且他们相互之间互不干扰,从不是很严格的意义上说,request可以看做prototype的一种特例,除了场景更加具体之外,语意上差不多。
session:
对于web应用来说,放到session中最普遍的就是用户的登录信息,对于这种放到session中的信息,我们我们可以使用如下形式的制定scope为session:
<bean id ="userPreferences" class="...UserPreferences" scope="session" />
Spring容器会为每个独立的session创建属于自己的全新的UserPreferences实例,他比request scope的bean会存活更长的时间,其他的方面真是没什么区别。
global session:
<bean id ="userPreferences" class="...UserPreferences" scope="globalsession" />
global session只有应用在基于porlet的web应用程序中才有意义,他映射到porlet的global范围的session,如果普通的servlet的web 应用中使用了这个scope,容器会把它作为普通的session的scope对待
可分为状态Bean和无状态Bean
pring还支持一种自定义scope类型
spring中的bean是线程安全的吗
Spring并没有对你的bean做任何处理,
bean是不是线程安全,取决于bean的本身,bean本身是我们自己写的代码
Spring中的bean是哪里来的?
初始化,通过扫描,利用反射new出来,并且缓存在ioc容器中
spring中的bean是如何被回收的
GC回收原则,当bean的引用没有指向任何地方的时候
Map<string,object> ioc 本身就是单例,Singleton对象随Spring而生,随容器销毁而回收
Spring中bean的生命周期有
Singleton 单例
Prototype 多例,用的时候会new一个
Session 存在与一个会话中
Request 存在于一次请求
Spring中的bean默认是singleton,全局的