Spring 高级装配

32 篇文章 0 订阅
11 篇文章 0 订阅

Spring 高级装配

标签: Spring


1.处理自动装配时的歧义性

例子:
下面的代码通过注解的方式声明了三个组件分别实现接口Dessert,而setDessert()方法通过@Autowired自动装配。

@Autowired
public void setDessert(Dessert dessert) {
    this.dessert = dessert;
}

@Componenet
public class Cake implememnts Dessert{...}
@Componenet
public class Cookies implememnts Dessert{...}
@Componenet
public class IceCream implememnts Dessert{...}

组件扫描时,Spring将抛出异常,NoUniqueBeanDefinitionException

1.1标示首先的Bean

像下面这样:

@Componenet
@Primary
public class Cake implememnts Dessert{...}

<!-- 通过XML声明IceCream为首选项 -->
<bean id="iceCream" class="IceCream" primary="true"/>
1.2创建自定义的限定符
@Autowired
@Qualifier("cold")//这里的Qualifier表明自动注入时Bean的限定符
public void setDessert(Dessert dessert) {
    this.dessert = dessert;
}

@Component
@Qualifier("cold")//这里的Qualifier表明组件的限定符
public class IceCream implements Dessert {
}

这里某个Bean的限定符不一定是单一的。可以是任意多个。

@Qualifier("cold")
@Qualifier("creamy")
1.3使用自定义的限定注解
@Retention(RUNTIME)
@Target({ TYPE, FIELD, METHOD, CONSTRUCTOR })
@Qualifier
public @interface Cold {
}

2.Bean的作用域

在默认情况下,Spring应用上下文中所有的Bean都是作为单例(Singleton)的形式创建的。也就是说,不管给定的Bean被注入到其他Bean多少次,每次所注入的都是一个实例。

Spring定义了多种作用域,可以基于这些作用域创建Bean,包括:

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

依旧是两种配置方式:基于注解的和基于XML文件的。

  • 基于注解的

使用@Scope注解

@Component
@Scope(scopeName=ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class Cookies implements Dessert {
}
  • 基于XML文件的

使用Bean的scope属性

<bean id="car2" class="test.Car"
        p:name="Ford" p:speed="250" p:price="12340000.00" 
        scope="prototype"/>
2.1使用会话和请求作用
@Component
@Scope(value=WebApplicationContext.SCOPE_SESSION,
    proxyMod=ScopedProxyMode.INTERFACES)
public ShoppingCart cart() {...}

@Component
public class StoreService {
    @Autowired
    public void setShoppingCart(ShoppingCart shoppingCart) {
        this.shoppingCart = shoppingCart;
    }
    ...
}

因为StoreService是一个单例的Bean,会在Spring应用上下文加载的时候创建。当它创建的时候,Spring会试图将ShoppingCart Bean注入到setShoppingCart方法中,但是ShoppingCart Bean是会话作用域,此时并不存在。

系统中将会有多个ShoppingCart实例:每个会话一个。我们并不像让Spring注入某个固定或者随机的ShoppingCart实例到StoreService中,我们希望注入的恰好是当前会话对应的那个。

Spring并不会将实际的ShoppingCart Bean注入到StoreService中,Spring会注入一个到ShoppingCart Bean的代理。这个代理会暴露与ShoppingCart相同的方法。当StoreService调用ShoppingCart的方法时,代理会对其进行懒解析并将调用委托给回话作用域内真正的ShoppingCart Bean

类型proxyMod补充
接口INTERFACES
TARGET_CLASS使用CGLib来生成基于类的代理

请求作用域应该也以作用域代理的方式注入

这里写图片描述

2.2在XML文件中声明作用域代理

要设置代理模式,需要使用Spring aop命名空间的一个新元素。

<bean id="car3" class="test.Car" sco pe="session">
    <aop:scoped-proxy/>
</bean>

设置<aop:scoped-proxy/>下的proxy-target-classfalse,进而要求它生成基于接口的代理

<bean id="wang" class="test.Person" scope="session">
    <aop:scoped-proxy proxy-target-class="false"/>
</bean>

3.运行时注入

Spring提供了两种在运行时求值的方式:

  • 属性占位符
  • Spring表达式语言(SpEL)
3.1注入外部的值
@Configuration
@PropertySource("classpath:car.propertites")
public class ExpressiveConfig {

    @Autowired
    Environment env;

    @Bean
    public Car getCar() {
        return new Car(//检索属性值
                env.getProperty("car.name"),
                env.getProperty("car.speed", Integer.class),
                env.getProperty("car.price", Double.class));
    }
}
3.2了解Spring的Environment
  • String getProperty(String key)
  • String getProperty(String key, String defaultValue)
  • T getProperty(String key, Class type)
  • T getProperty(String key, Class type, T defaultValue)

如果某个属性没有定义,则返回null,这时,指定默认值就起作用了。Environment还可以检查某个属性是否存在,哪些profile处于激活状态以及将某个属性解析为类。

3.3解析属性占位符

在Spring装配中,占位符的形式为使用${...}包裹的属性名称。

<bean id="carXML" class="test.Car">
    <property name="name" value="${car.name}"></property>
    <property name="speed" value="${car.speed}"></property>
    <property name="price" value="${car.price}"></property>
</bean>

附上:配置文件car.propertites里的内容

car.name=wanghao
car.speed=250
car.price=12345.00
3.4使用Spring表达式语言进行装配

Spring 3引入了Spring表达式语言,它够以一种强大和简洁的方式将值装配到Bean属性和构造器参数中,在这个过程中使用表达式会在运行时计算得到值。

  • 表示字面值

用来表示浮点值,字符串,bool值。

#{3.1415}
#{9.87E4}
#{'Hello'}
#{false}
  • 引用Bean,属性和方法

?.运算符能够在在访问它右边的内容时,确保左边的不是null。如果左边的内容时null,它就不会调用右边的内容。

#{car}//引用Bean
#{car.speed}//引用属性 
#{carSelector.selectCar()?.toUpperCase()}//引用方法 
  • 在表达式使用类型
    T()表示括号里的是静态内容。
#{T(java.lang.Math).PI}
#{T(java.lang.Math).random()}
  • SpEL运算符
    用来操作表达式值得SpEL运算符
运算符类型运算符
算数运算+ - * / $ ^
比较运算< > == <= >= lt gt eq le ge
逻辑运算and or not ]
条件运算?:
正则运算mathes
  • 计算集合

操作符[]在集合操作中很强大,来几个例子感受一下。

#{jukebox.songs[3].title}
#{'hello world'[3]}
#{jukebox.songs.?[artist eq 'WangHao']}
对集合过滤
#{jukebox.songs.^[artist eq 'WangHao']}
在集合中查询第一个匹配项
#{jukebox.songs.$[artist eq 'WangHao']}
在集合中查询最后一个匹配项
#{jukebox.songs.![title]}
从集合中选出特定的属性放到另一个集合中

综合例子:

<bean id="spel" class="test.ManyProperty">
    <property name="age" value="#{23}"></property>
    <property name="name" value="#{'Wang' + 'Hao'}"></property>
    <property name="price" value="#{123.45D}"></property>
    <property name="man" value="#{true}"></property>
    <property name="car" value="#{car1}"></property>
    <property name="height" value="#{T(java.lang.Math).PI * 12}"></property>
    <!-- 
    这些多用于后者用于文本等
    eq:等于
    le ge:less equal==>小于等于 大于等于
    lt gt:less than==>小于 大于
     -->
    <property name="testMany" value="#{12 le 12}"></property>
</bean>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值