美文网首页
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