在Spring中,那些组成应用程序的主体及由Spring IoC容器所管理的对象,被称之为bean。简单地讲,bean就是由IoC容器初始化、装配及管理的对象,除此之外,bean就与应用程序中的其他对象没有什么区别了。而bean的定义以及bean相互间的依赖关系将通过配置元数据来描述。
Spring中bean对象默认都是单例的,同一个beanname获取的bean对象都单例的。对于Web应用来说,Web容器对于每个用户请求都创建一个单独的Sevlet线程来处理请求,引入Spring框架之后,每个Action都是单例的,那么对于Spring托管的单例Service Bean,Spring的单例是基于BeanFactory也就是Spring容器的,单例Bean在此容器内只有一个,Java的单例是基于JVM,每个JVM内只有一个实例。
Aware接口从字面上翻译过来是感知捕获的含义。单纯的bean(未实现Aware系列接口)是没有知觉的;实现了Aware系列接口的bean可以访问Spring容器。这些Aware系列接口增强了Spring bean的功能,但是也会造成对Spring框架的绑定,增大了与Spring框架的耦合度。(Aware是“意识到的,察觉到的”的意思,实现了Aware系列接口表明:可以意识到、可以察觉到)
接口源码:
public interface Aware{
}
可以发现该接口并没有定义任何方法,所以这只是一个标识接口,该接口的子接口如下:
可以发现子接口,都以Aware结尾,那这些子接口有什么作用呢?
先进入子接口一探究竟
public interface ApplicationEventPublisherAware extends Aware {
void setApplicationEventPublisher(ApplicationEventPublisher var1);
}
public interface MessageSourceAware extends Aware {
void setMessageSource(MessageSource var1);
}
我们发现每个子接口都定义了set方法。而方法中的形参类别是接口Aware前面的内容,也就是当前Bean需要感知的内容。所以我们需要在Bean中声明相关的成员变量来接收。
/**
* 实现了
* ApplicationContextAware
* BeanClassLoaderAware
* BeanFactoryAware
* BeanNameAware
* 接口
*/
public class User implements ApplicationContextAware,BeanClassLoaderAware,BeanFactoryAware,BeanNameAware{
private int id;
private String name;
// 保存感知的信息
private String beanName;
// 保存感知的信息
private BeanFactory beanFactory;
// 保存感知的信息
private ApplicationContext ac;
// 保存感知的信息
private ClassLoader classLoader;
public BeanFactory getBeanFactory() {
return beanFactory;
}
public ApplicationContext getAc() {
return ac;
}
public ClassLoader getClassLoader() {
return classLoader;
}
public User(){
System.out.println("User 被实例化");
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getBeanName() {
return beanName;
}
/**
* 自定义的初始化方法
*/
public void start(){
System.out.println("User 中自定义的初始化方法");
}
@Override
public String toString() {
return "User [id=" + id + ", name=" + name + ", beanName=" + beanName + "]";
}
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
System.out.println(">>> setBeanClassLoader");
this.classLoader = classLoader;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println(">>> setApplicationContext");
this.ac = applicationContext;
}
@Override
public void setBeanName(String name) {
System.out.println(">>> setBeanName");
this.beanName = name;
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println(">>> setBeanFactory");
this.beanFactory = beanFactory;
}
}
测试类
@Test
public void test1() {
ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml");
User user = ac.getBean(User.class);
System.out.println("beanFactory:"+user.getBeanFactory());
System.out.println("beanName:"+user.getBeanName());
System.out.println("applicationContext:"+user.getAc());
System.out.println("classLoader:"+user.getClassLoader());
System.out.println(user);
}
有时候,我们并没有实现那些接口,我们可以除去哪些接口,针对Bean的单例和非单例来描述下bean的生命周期。
当scope=“singleton”,即默认情况下,会在启动容器时(即实例化容器时)时实例化。但我们可以指定Bean节点的lazy-init="true"来延迟初始化bean,这时候,只有在第一次获取bean时才会初始化bean,即第一次请求该bean时才初始化。如下配置:
<bean id="serviceImpl" class="cn.csdn.service.ServiceImpl" lazy-init="true"/>
如果想对所有的默认返利bean都应用延迟初始化,可以在根节点beans设置default-lazy-init属性为true,如下所示:
<beans default-lazy-init="true">
默认情况下,Spring在读取xml文件的时候,就会创建对象。在创建对象的时候先调用构造器,然后调用init-method属性值中所指定的方法。对象在被销毁的时候,会调用destroy-method属性值中所指定的方法(例如调用Container.destroy()方法的时候)。写一个测试类,代码如下:
public class LifeBean {
private String name;
public LifeBean(){
System.out.println("LifeBean()构造函数");
}
public String getName() {
return name;
}
public void setName(String name) {
System.out.println("setName()");
this.name = name;
}
public void init(){
System.out.println("this is init of lifeBean");
}
public void destory(){
System.out.println("this is destory of lifeBean " + this);
}
}
beans.xml配置如下:
<bean id="life_singleton" class="com.bean.LifeBean" scope="singleton"
init-method="init" destroy-method="destory" lazy-init="true"/>
测试代码:
public class LifeTest {
@Test
public void test() {
AbstractApplicationContext container =
new ClassPathXmlApplicationContext("life.xml");
LifeBean life1 = (LifeBean)container.getBean("life");
System.out.println(life1);
container.close();
}
}
Prototype作用域和singleton刚刚好相反,singleton只会返回同一个对象,Prototype则是返回一个bean对应多个对象实例,Prototype作用域的bean会导致在每次对这个bean请求(将它注入到另一个bean中,或者以程序的方式调用getBean()方法)时都会创建一个新的bean实例,相当于执行newXxxBean()。Prototypes 原型类型,它在我们创建容器时并没有实例化,只会在我们获取bean的时候才会去创建一个对象,而且我们每次创建的对象都不是同一个对象,根据需要,对有状态的bean应该使用Prototype作用域,对无状态的的bean则可以使用singleton作用域。
来看看Prototype作用域的差别,其余代码同上 ,在。beans.xml中把scope改成prototype
<bean id="life_prototype" class="cn.hsd.mybatis.entity.LifeBean" scope="prototype"
init-method="init" destroy-method="destroy" lazy-init="true"/>
测试代码:
@Test
public void test3(){
AbstractApplicationContext container = new ClassPathXmlApplicationContext("beans.xml");
LifeBean life1 = (LifeBean) container.getBean("life_singleton");
LifeBean life2 = (LifeBean) container.getBean("life_singleton");
System.out.println(life1==life2);//true
LifeBean life3 = (LifeBean)container.getBean("life_prototype");
LifeBean life4 = (LifeBean)container.getBean("life_prototype");
System.out.println(life3==life4);//false
container.close();
}
可以发现,对于作用域为prototype的bean,其destroy方法并没有被调用。如果bean的scope设为prototype时,当容器关闭时,destroy方法不会被调用。对于prototype作用域的bean,有一点非常重要,那就是Spring不能对一个prototype bean的整个生命周期负责:容器在初始化、配置、装饰或者是装配完一个prototype实例后,将它交给客户端,随后就对该prototype实例不闻不问了。
ln(life3==life4);//false
container.close();
}
可以发现,对于作用域为prototype的bean,其destroy方法并没有被调用。如果bean的scope设为prototype时,当容器关闭时,destroy方法不会被调用。对于prototype作用域的bean,有一点非常重要,那就是Spring不能对一个prototype bean的整个生命周期负责:容器在初始化、配置、装饰或者是装配完一个prototype实例后,将它交给客户端,随后就对该prototype实例不闻不问了。
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://blog.csdn.net/weixin_44932487/article/details/123036878
内容来源于网络,如有侵权,请联系作者删除!