美文网首页
Spring实战——高级装配

Spring实战——高级装配

作者: WhyDoWeLive | 来源:发表于2019-07-14 21:45 被阅读0次
    @profile("场景标识名")

    不同的环境中(开发环境、生产环境、QA环境)用到的bean可能不同,那么在运行时我们如何在Spring中根据环境,决定创建哪个bean和不创建哪个bean?使得同一个部署单元(war文件)能够适用于所有环境,无须重新构建

    通过在JavaConfig中声明@Bean的类或函数上添加@profile("场景标识名"),此时,除非对应场景的profile处于激活状态,否则对应的@Bean不会被创建。
    注意,没有指定profile的Bean始终都会被创建,与激活哪个profile没有关系。

    //插曲:看一下注解是怎么定义的
    @Conditional(........)
    public  @interface Profile{}
    
    @Conditional(条件)——条件化创建bean

    假设你希望应用的类路径下包含特定库才创建bean;或者希望在某个特定bean也声明了之后才创建等等。

    处理自动装配的歧义性

    当有多个Bean满足注入条件时,Spring 会抛出异常。Spring提供了两种解决方案@Primary和@Qualifier。

    • @Primary
      即标识某个bean为首选bean。缺点是如有多个首选bean,则会再次产生歧义
    • @Qualifier()
      方式1 : 可直接通过@Qualifier(beanId)指定我要注入哪个bean。缺点是类名重构后自动装配会失败
      方式2 : 自定义限定符。即在声明@Bean时使用@Qualifier(自定义限定符),然后再注入时,加上@Qualifier(自定义限定符)即可。缺点是,若有多个@Bean定义时才用相同的@Qualifier,则又会冲突。注意,不允许使用多个@Qualifer叠加
      方式3 : 扩展方式2,自定义用@Qualifier注解的注解,这样在方式2就可以叠加多个自定义注解不断的缩小范围或叫做保持唯一
    ...
    @Qualifier
    public @interface Cold{}
    
    ...
    @Qualifier
    public @interface Creamy{}
    

    这样在声明@Bean和注入时,通过叠加多个如上的注解,保证满足条件的bean只有一个。

    Bean的作用域

    默认情况下,Spring应用上下文中所有的bean都是单例bean。
    有时,有些class会保持一些状态并且是易变,因此重用是不安全的,所以不适合作为单例bean

    Spring定义了多种作用域用于创建bean:

    • 单例:整个应用中只创建bean的一个实例
    • 原型:每次注入或者通过Spring应用上下文获取的时候,都会创建新的bean实例
    • 会话:在Web应用中,为每个会话创建一个bean实例
    • 请求:在Web应用中,为每个请求创建一个bean实例

    单例是默认作用域,通过@Scope(作用域类型)可更改Bean的作用域,可与@Component和@Bean一起用

    会话作用域

    在典型的电子商务应用中,可能会有一个bean代表用户的购物车。如果购物车是单例的话,那么将导致所有的用户都会向同一个购物车添加商品。另一方面,如果购物车是原型作用域的,那么在应用中某一个地方往购物车添加商品,在应用的另外一个地方可能就不可用了,因为在这里注入的是另外一个原型作用域的购物车。

    因此,需要会话作用域:为每个会话创建一个ShoppingCart,在每个会话中bean相当于单例的。

    使用方式一般为:

    @Scope(
        value=WebApplicationContext.SCOPE_SESSION, 
        proxyMode=ScopedProxyMode.INTERFACES/TARGET_CLASS
    )
    

    为什么使用proxyMode?
    假设有个StoreService类依赖会话作用域的ShoppingCart。
    首先,直到有用户进入系统,创建了会话之后,才会出现ShoppingCart实例,因此这之前,是不存在ShoppingCart的实例的。
    其次,每个用户都有一个ShoppingCart,但我们不想让Spring注入某个特定的ShoppingCart到StoreService,而是希望当StoreService处理购物车功能时,它所使用的ShoppingCart实例恰好是当前会话所对应的那一个。

    解决办法:动态代理模式
    向StoreService注入一个ShoppingCart的代理,当StoreService调用ShoppingCart的方法时,代理对其解析并将调用委托给会话作用域内真正的ShoppingCart Bean

    proxyMode的两个可选参数分别为实现接口的JDK Proxy 代理和通过继承的CGLib代理

    属性占位符、SpEL(Spring表达式语言)

    可用于运行时注入值,注意不是对象,怎么做先不记录了,知道就好了。

    public Person getParent()
    {
        //下面两个参数时硬编码的,通过属性占位符或SpEL可运行时注入
        return new Person("wxs", "sheep");
    }
    

    相关文章

      网友评论

          本文标题:Spring实战——高级装配

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