标签: Spring
例子:
下面的代码通过注解的方式声明了三个组件分别实现接口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
。
像下面这样:
@Componenet
@Primary
public class Cake implememnts Dessert{...}
@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")
@Retention(RUNTIME)
@Target({ TYPE, FIELD, METHOD, CONSTRUCTOR })
@Qualifier
public @interface Cold {
}
在默认情况下,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 {
}
使用Bean的scope属性
@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来生成基于类的代理 |
请求作用域应该也以作用域代理的方式注入
要设置代理模式,需要使用Spring aop
命名空间的一个新元素。
设置
下的proxy-target-class
为false
,进而要求它生成基于接口的代理
Spring提供了两种在运行时求值的方式:
@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));
}
}
如果某个属性没有定义,则返回null
,这时,指定默认值就起作用了。Environment
还可以检查某个属性是否存在,哪些profile处于激活状态以及将某个属性解析为类。
在Spring装配中,占位符的形式为使用${...}
包裹的属性名称。
附上:配置文件car.propertites
里的内容
car.name=wanghao
car.speed=250
car.price=12345.00
Spring 3引入了Spring表达式语言,它够以一种强大和简洁的方式将值装配到Bean属性和构造器参数中,在这个过程中使用表达式会在运行时计算得到值。
用来表示浮点值,字符串,bool值。
#{3.1415}
#{9.87E4}
#{'Hello'}
#{false}
?.
运算符能够在在访问它右边的内容时,确保左边的不是null
。如果左边的内容时null
,它就不会调用右边的内容。
#{car}//引用Bean
#{car.speed}//引用属性
#{carSelector.selectCar()?.toUpperCase()}//引用方法
#{T(java.lang.Math).PI}
#{T(java.lang.Math).random()}
运算符类型 | 运算符 |
---|---|
算数运算 | + - * / $ ^ |
比较运算 | < > == <= >= 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]}
从集合中选出特定的属性放到另一个集合中
综合例子:
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>