bean的作用域

什么是bean的作用域?

bean的作用域指的就是spring容器创建bean完成后的生命周期,就是从创建到销毁的整个过程。spring默认的作用域是Singleton。

Singleton

**在singleton的作用域下每一个bean实例只会被创建一次,**而spring容器在整个生命周期中都可以使用该实例,因此在默认情况下singleton,spring容器创建bean后,通过代码获取bean,不论多少次都是一个bean实例,可以通过标签中的scope属性来指定一个Bean的作用域

<!--默认情况下不需要使用声明scope属性-->
<bean name="accountDao“scope=”singleton"  class ="com.spring.springiocc.dao.impl.AccountDaoImpl“/>

prototype

除了使用singleton作用域之外,还有一种常见的作用域prototype,表示每次获取bean实例的时候都会创建一个实例对象,类似于new操作符

<bean name="accountDao” scope=“prototype” calss=“com.spring.springioc.dao.impl.AccountDaoImpl"/>

@Test
public void test2(){
	ApplicationContext applicationContext=new classPathXMLApplicationContext("spring/spring-ioc.xml");
	AccountDao accountDao1=applicatonContext.getBean("accountDao");
	AccountDao accountDao2=applicationContext.getBean("accountDao”);
	System.out.println("accountDao1的地址为:“+accountDao1.toString();
	System.out.println("accountDao1的地址为:“+accountDao2.toString();
}

通过结果可以发现是两个不同的实例对象
除了在xml文件中的bean标签中声明bean作用域scope为prototype之外,也可以通过注解声明

@Scope("prototype")
pubic class AccountDaoImpl{

}

Singleton和prototype的特殊场景
一个作用域为Singleton的作用域依赖于一个作用域为prototype作用域的bean时候,如:

<bean name="accountDao" scope="prototype" calss=com.spring.springioc.dao.impl.AccountDaoImpl”/>
<bean name="accountService" class="com.spring.springioc.service.impl.AccountServiceImpl“/>
	<property name="accountDao" ref="accountDao"/>
	<!--使用的是xml文件形式注入依赖,要有对应的setter方法-->
</bean>

这种情况下希望的是每次getBean("accountService”)处理的都是一个新的accountDao实例对象,但是由于accountService的依赖是在bean被创建的时候注入的,而且accountService是一个singleton,整个生命周期中只会创建一个,因此它依赖的accountDao实例对象也只会被注入一次,此后不会再注入任何新的accountDao对象
而我们希望的是每次处理的是一个新的accountDao对象,为了解决这个问题可以放弃使用依赖注入功能,使用代码进行实现
通过实现ApplicationContextAwart接口重写setApplicationContext方法,此时,spring容器在创建AccountServiceImpl实例的时候会自动注入ApplicaitonContext对象,此时通过ApplicationContext获取accountDao实例,保证每次获取的accountDao实例都是新的,对应的实例如下:

<bean name="accountDao" scope="prototype" class="com.spring.springioc.dao.impl.AccountDaoImpl"/>
<bean name="accountService" class="com.spring.springioc.service.impl.AccountServiceImpl"/>

public calss AccountServiceImpl implements AccountService,ApplicationContextAware{
	private ApplicationContext applicationContext;
	@override
	public void doSomething(){
		System.out.println("AccountServiceImpl#doSomething....");
		System.out.println(“getAccountDao" + getAccountDao().toString();
	}
	@override
	public void setApplicatiionContext(ApplicationContext applicationContext) throws BeansException{
			this.applicationContext=applicationContext;
	}
	private AccountDao getAccountDao(){
		return applicationContext.getBean(AccountDao.class);
	}
}

//加载配置文件
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("spring/spring-ioc.xml");
//测试获取不同实例的AccountDao
AccountService accountService= (AccountService) applicationContext.getBean("accountService");
accountService.doSomething();
AccountService accountService1= (AccountService) applicationContext.getBean("accountService");
accountService1.doSomething();

在这里插入图片描述
这种情况下,每次获取到的acountDao都是不相同的,解决了一个singleton实例中包含一个prototype类型的bean的问题,另一种情况是在一个prototype的bean中依赖于一个Singleton作用域的bean,解决的方案相同。让singleton的bean的实现类中重写方法。
注意:当一个bean被设置为prototype之后spring就不会对这个bean的整个生命周期负责,容器在初始化配置或者装饰一个prototype实例之后,将其交给客户端随后就对prototype实例不管不问了,因此要慎重的使用,一般情况下,对有状态的bean都应该使用prototype作用域,对无状态的bean则应该使用singleton作用域。有状态就是该bean有保存信息的能力,不能共享,否则会造成线程安全问题,而无状态则不会保存信息,是线程安全的,可以共享,spring中大部分的bean都是singleton的,整个生命周期只有一个。

Request与Session作用域

spring中针对web程序引入了request和session两种作用域
对于request作用域,每次http请求到达应用程序的时候,Spring容器就会创建一个新的request作用域的bean,并且该bean实例仅仅在当前http request内有效,整个请求过程中也只会使用相同的bean实例,因此可以根据需要放心的更改所创建实例的内部状态。而其他http请求则会创建引得bean实例互不干扰,当处理请求结束,request作用域的bean实例将会被销毁。如:在接收参数的时候需要一个bean实例来装载一些参数,显然每次请求的参数都是不一样的,因此就希望bean实例每次都是足够的新而且只会在request作用域范围内有效。
对于Session作用域,每次创建一个显得http session的时候就会创建一个Session作用域的bean,并且该实例会伴随着会话的存在而存在

@Component
@Scope(value = "singleton")
public class SingletonBean {
    //......
}

@Component
@Scope(value = "prototype" , proxyMode = ScopedProxyMode.TARGET_CLASS)
public class PrototypeBean {
    //......
}

@Component
@Scope(value = "request" , proxyMode = ScopedProxyMode.TARGET_CLASS)
public class RequestBean {
    //......
}

@Component
@Scope(value = "session" , proxyMode = ScopedProxyMode.TARGET_CLASS)
public class SessionBean {
    //........
}

在这里创建了4个不同作用域的Bean,并使用注解的方式进行开发,@Component表明他们是组件类型,需要spring容器帮忙创建@scope表明了是什么作用域,除了sigletonBean之外其他Bean还使用proxyMode指明了是哪一种代理模式创建的,这里没有接口,因此使用CGLib代理生成,接着需要在xml添加包扫描

<!-- 包扫描 -->
<context:component-scan  base-package="com.zejian.spring.dto" />

使用springMVC创建web访问层

@Controller
public class BookController
{
    @Autowired
    private RequestBean requestBean;
    @Autowired
    private SessionBean sessionBean;
    @Autowired
    private PrototypeBean prototypeBean;
    @Autowired
    private SingletonBean singletonBean;


    @RequestMapping(value = "/test")
    public void test()
    {
        print();
    }

    public void print() {
        System.out.println("first  time singleton is :" + singletonBean);
        System.out.println("second time singleton is :" + singletonBean);

        System.out.println("first  time prototype is :" + prototypeBean);
        System.out.println("second time prototype is :" + prototypeBean);

        System.out.println("first  time requestBean is :" + requestBean);
        System.out.println("second time requestBean is :" + requestBean);

        System.out.println("first  time sessionBean is :" + sessionBean);
        System.out.println("second time sessionBean is :" + sessionBean);

        System.out.println("===========================================");
    }

启动之后进行访问,使用浏览器连续访问两次,结果为:

first  time singletonBean is :com.zejian.spring.dto.SingletonBean@2ebdd720
second time singletonBean is :com.zejian.spring.dto.SingletonBean@2ebdd720
first  time prototypeBean is :com.zejian.spring.dto.PrototypeBean@1ed53cde
second time prototypeBean is :com.zejian.spring.dto.PrototypeBean@35c052be
first  time requestBean is :com.zejian.spring.dto.RequestBean@15b9dfe1
second time requestBean is :com.zejian.spring.dto.RequestBean@15b9dfe1
first  time sessionBean is :com.zejian.spring.dto.SessionBean@5b355dae
second time sessionBean is :com.zejian.spring.dto.SessionBean@5b355dae
===========================================
first  time singletonBean is :com.zejian.spring.dto.SingletonBean@2ebdd720
second time singletonBean is :com.zejian.spring.dto.SingletonBean@2ebdd720
first  time prototypeBean is :com.zejian.spring.dto.PrototypeBean@7775fd09
second time prototypeBean is :com.zejian.spring.dto.PrototypeBean@79b20d97
first  time requestBean is :com.zejian.spring.dto.RequestBean@7d8d9679
second time requestBean is :com.zejian.spring.dto.RequestBean@7d8d9679
first  time sessionBean is :com.zejian.spring.dto.SessionBean@5b355dae
second time sessionBean is :com.zejian.spring.dto.SessionBean@5b355dae
===========================================

可以看见,两次访问创建的singleton作用域的都是同一个对象,而两次请求的prototype作用域是两个不同的实例,而request作用域是两个不同bean,但是session作用域是两个相同的bean,因为对于Sessionbean来说因为是在同一个浏览器中进行的访问,属于同一个会话因此sessionbean中的实例都是同一个对象
当在零一浏览器进行访问的时候,会发现不同会话中的SessionBean实例是不同的。为什么需要在 其他三种bean作用域上设置代理模式?实际上这个问题的本质与前面Singleton作用域的bean依赖于Prototype作用域的bean原理是相同的,Prototype前面分析过了使用注解的时候也要声明代理模式,主要分析request和session作用域
**因为spring容器只会在创建bean实例的时候帮助我们注入实例bean所依赖的其他bean实例,**而且只会注入一次,这不是request和session作用域想看到的,因为它们需要在不同的场景下注入新的实例对象而不是唯一不变的实例对象,为了解决这种困境,放弃直接在xml文件中注入bean实例,使用java代码的方式(实现applicationContextAware接口)或者注解的方式(@Autowired)注入
使用@Autowired为什么会每次注入新的对象呢?使用了Autowired之后并在bean的声明中声明了动态代理模式。spring容器能够通过代理的方式生成新的实例bean,来满足创建新实例的需求,在程序运行的时候,当一个方法调用到该代理对象的时候,Spring容器便城市在当前的请求或者会话中获取目标对象bean,如果已经存在bean就使用这个bean,否则代理方法将会创建新实例bena处理请求或者会话,这里指的是一次http请求或者一次会话的过程,如果希望request和 session作用域通过xml配置文件的方式声明的时候,必须要在标签中放置aop:scoped-proxy作为子标签,这与注解和动态代理的模式相同,

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值