美文网首页Spring
Spring高级装配之Bean的作用域

Spring高级装配之Bean的作用域

作者: 郭之源 | 来源:发表于2016-08-25 21:05 被阅读1059次

    我们知道在默认情况下,Spring应用上下文中所有Bean都是使用单例(singleton)模式创建的。在大多数情况下,单例Bean是很理想的方案。初始化和垃圾回收对象实例所带来的成本都是很低的。
    但有时候,你可能会发现,所使用的类是易变的(mutable),他们会保持一些状态,因此重用是不安全的。在这种情况下,将class声明为单例的Bean就不是什么好主意了,因为对象会被污染,然后重用被污染的对象时会出现意想不到的问题。
    不要担心,强大的Spring已经给出了解决方案。Spring定义了多种作用域,可以基于这些作用域创建bean,如下:

    作用域名称 描述
    单例(Singleton) 在整个应用中,只会创建一个bean的实例
    原型(Prototype) 每次注入或者通过上下文获取的时候,都会创建一个新的bean实例
    会话(Session) 在Web应用中,为每个会话创建一个bean实例
    请求(Request) 在Web应用中,为每个请求创建一个bean实例

    单例是默认作用域,但正如之前所述,对于易变的类型,这并不合适。其实Spring提供的@Scope注解,它可以与@Component或@Bean一起使用。现在我们就声明NotePad的作用域为原型:

    @Service
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public class NotePad {
    
    }
    

    这里使用的ConfigurableBeanFactory类的SCOPE_PROTOTYPE常量设置了原型作用域。
    如果咱们使用的是显示方式声明的Bean,@Scope也可以与@Bean联合使用:

    @Bean
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public NotePad notePad(){
        return new NotePad();
    }
    

    同样如果你使用的是XML来配置bean的话,可以使用<bean>元素的scope属性来设置作用域:

    <bean id="notePad" class="com.scope.service.NotePad" scope="prototype" />
    

    1.使用会话和请求作用域

    在实际企业级开发过程中,我们常用@Scope来定义Bean的作用域。如用户的购物车信息,如果将购物车类声明为单例(Singleton),那么每个用户都向同一个购物车中添加商品,这样势必会造成混乱;你也许会想到使用原型模式声明购物车,但这样同一用户在不同请求时,所获得的购物车信息是不同的,这也是一种混乱。
    这时候将购物车作用域设置为会话(Session)就再适合不过了:

    @Service
    @Scope(value=WebApplicationContext.SCOPE_SESSION,
        proxyMode=ScopedProxyMode.INTERFACES)
    public class Cart {
    
    }
    

    这里,我们将value的值设置为WebApplicationContext中的SCOPE_SESSION常量(他的值是session)。这样就会告诉Spring每个会话创建一个Cart。
    细心的朋友可能已经发现了,@Scope的里面还有一个属性proxyMode,他被设置成了ScopeProxyMode.INTERFACES。这个属性解决了将会话或请求的bean注入到单例bean中所遇到的问题。在此之前我们先看看这个问题是什么。
    假如我们将Cart bean注入到单例StoreService bean的setter方法中,如下所示:

    @Service
    public class StoreService {
        
        @Autowired
        private Cart cart;
    
    }
    

    因为SroreService是一个单例的bean,会在Spring应用上下文加载的时候创建。当他创建的时候,Spring会试图将Cartbean注入到它的属性中。但是cart bean是会话作用域的,此时并不存在。直到某个用户进入系统之后,才会创建Cart实例。
    另外,系统中将会有多个Cart 实例:每个会话一个。我们不想让Spring注入某个固定的Cart,我们希望Spring注入的是本次会话的Cart bean。
    其实,Spring这时并不会将实际的Cart bean注入到StoreService中,Spring 会注入一个Cart bean 的代理。这个代理会暴露与Cart相同的方法,所以认为他就是一个购物车。当StoreService调用Cart方法时,代理会对其进行赖解析,并调用委托给会话作用域内真正的Cart bean。
    现在,我i们带着对这个作用域的理解,讨论一下proxyMode属性,proxyMode属性被设置成了ScopeProxyMode.INTERFACES,这表明这个代理要实现Cart接口,并调用委托给实现bean。
    如果Cart是接口而不是类也是可以的(最理想的代理模式就是实现接口)。但如果Cart是一个具体类的话,他必须使用CGLib来生成基于类的代理,此时必须要将proxyMode属性设置为ScopeProxyMode.TAGER_CLASS,以此看来要以生成目标类扩展的方式创建代理。
    请求(Request)作用域的装配机制也是这样的。

    2.在XML中声明作用域代理

    在xml中声明代理模式,要使用到<bean>的一个 属性scope;设置代理模式要使用Spring AOP命名空间中的一个新元素:

    <bean id="card" class="com.scope.service.Cart" scope="session"
        <aop:scoped-proxy />
    </bean>
    

    <aop:scope-proxy />是与@scope注解的proxyMode属性功能相同Spring XML配置元素。他会告诉Spring为bean创建一个作用域代理。默认情况下他创建的是CGLib模式的代理。我们也可以指定他创建代理的模式:

    <bean id="card" class="com.scope.service.Cart" scope="session"
        <aop:scoped-proxy proxy-target-class="false"/>
    </bean>
    

    为了使用<aop:scope-proxy>元素,我们必须在XML中配置声明Spring AOP的命名空间:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
        xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:task="http://www.springframework.org/schema/task"
        xmlns:aop="http://www.springframework.org/schema/aop"
        xmlns:cache="http://www.springframework.org/schema/cache"
        xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
            http://www.springframework.org/schema/cache
            http://www.springframework.org/schema/cache/spring-cache.xsd
            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
            http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.0.xsd">
    ...
    </beans>
    

    至此,关于Spring bean作用域的配置我们就大致介绍完了,下节我们会介绍Spring更高级的配置--运行时注入。

    相关文章

      网友评论

        本文标题:Spring高级装配之Bean的作用域

        本文链接:https://www.haomeiwen.com/subject/hkyasttx.html