四、spring中高级装配(2)

  这个是接着上一篇写的,这章内容较多,分开来记录一下。。。

三、处理自动装配的歧义性

  自动装配让spring完全负责bean引用注入到构造参数和属性中,不过,仅有一个bean匹配所需的结果时,自动装配才是有效的。如果不仅有一个bean能够匹配的话,这种歧义性会阻碍spring自动装配属性、构造参数或方法参数。虽然这种歧义性十分罕见,但是我看了spring解决方法后,感觉spring提供的是一种这类问题的解决办法,显然在这里主要学习的是这种解决此类问题的思想。

//这里是提供了这种特殊情况的demo,有一个方法,需要自动装配Dessert接口
@Autowired
public void setDessert(Dessert dessert){
  this.dessert = dessert;    
} 

//但是这个Dessert接口却有三个实现类,这就有点尴尬了,spring自动装配的时候到底要装配哪个实现类呢..... spring会报NoUniqueBeanDefinitionException错误的

@Component
public class Cake implements Dessert{......}

@Component
public class Cookies implements Dessert{......}

@Component
public class IceCream implements Dessert{......}

//使用@Primary 注解标注首选bean
@Primary
@Component
public class IceCream implements Dessert{......}

<bean id="iceCream" class="com.desserteater.IceCream" primary="true" />

//但是两个同时加上@Primary注解呢!!!spring中会有@Qualifier注解
@Autowired
@Qualifier("iceCream")
public void setDessert(Dessert dessert){
  this.dessert = dessert;    
} 

//但是这种方式使用的是bean ID,限制符与要注入的bean是紧耦合的,对类名称的改动都会导致限定符失效,但是spring中允许为bean设置自己的限定符
@Component
@Qualifier("cold")
public class IceCream implements Dessert{......}

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

//但是如果是两个实现类具有相同特点的限制符呢!!!这个考虑的也太全面了吧,程序员就是要有这种精神的,哈哈哈,就是下面这种情况呢.....

@Component
@Qualifier("cold")
@Qualifier("creamy")
public class IceCream implements Dessert{......}

@Component
@Qualifier("cold")
@Qualifier("fruity")
public class Popsicle implements Dessert{......}

//Java 中不允许出现在同一个条目上出现相同类型的多个注解的,唉,终极办法:自定义限制符注解,这个好牛叉牛叉...
//这个对应着就是@Qualifier("cold")
@Target({ElementType.CONSTRUCAOR, ElementType.FILELD, ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
Public @interface Cold{}

//这个对应着就是@Qualifier("creamy")
@Target({ElementType.CONSTRUCAOR, ElementType.FILELD, ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
Public @interface Creamy{}

//这个对应着就是@Qualifier("fruity")
@Target({ElementType.CONSTRUCAOR, ElementType.FILELD, ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
Public @interface Fruity{}

//大功告成,这就彻底解决那个问题了,你在有多少的我也不怕了,嘿嘿嘿,完美的解决这个问题了,spring中太美妙了,这种思想真的让人受益匪浅
@Component
@Cold
@Creamy
public class IceCream implements Dessert{......}

@Component
@Cold
@Fruity
public class Popsicle implements Dessert{......}

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

四、bean的作用域

   在默认情况下,spring应用上下文中所有的bean都是以单例的形式创建的,也就是说,不管给定的一个bean被注入到其他bean多少次,每次所注入的都是同一个实例。大多数情况下,单例bean是很理想的方案。初始化和垃圾回收对线实例所带来的成本只留给一些小规模的任务,在这些任务中,让对象保持无状态并且在应用中反复用这些对象可能并不合理。

spring中为解决这个问题,定义了多种作用域,可以基于这些作用域创建bean:

1)单例(Singleton):在整个应用中,只创建bean的一个实例

2)原型(Prototype):每次注入或者通过spring应用上下文获取的时候,都会创建一个新的bean的实例

3)会话(Session):在web应用中,为每个会话创建一个bean实例

4)请求(Request):在web应用中,为每个请求创建一个bean实例

//Prototype 原型作用域的配置方式
@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class Notepad{......}

<bean id="notepad" class="com.myapp.Notepad" scope="prototype" />

//Session 会话和 Request 请求作用域
//在电商领域的 处理购物车bean 会话作用域是最合适的
@Component
@Scope(
value=WebApplicationContext.Scope.SESSION,
ProxyMode=ScopedProxyMode.INTERFACES)
Public ShoppingCart cart(){......}

@Component
Public class StoreService(){
  
  //StoreService是一个单例bean,会在spring应用上下文加载的时候创建,当它创建的时候,会试图将ShoppingCart bean注入到set方法中,但是ShoppingCart bean是会话作用域,此时并不存在,直到某个用户进入系统,创建会话之后才会出现ShoppingCart 的实例
  @Autowired
  public void setShoppingCart(ShoppingCart shoppingCart){
    this.shoppingCart = shoppingCart;
  }
} 

/*
这里需要详细讲解一下ProxyMode
系统中,会有多个ShoppingCart 的实例,每个用户一个,我们希望当StoreService处理购物车的时候,它所使用的ShoppingCart 实例恰好是当前会话所对应的那一个

spring并不会将实际的ShoppingCart bean注入到StoreService中,spring会注入一个到ShoppingCart bean的代理,这个代理会暴露于ShoppingCart 相同的方法,StoreService会认为它就是一个购物车

当StoreService 调用 ShoppingCart 的方法时,代理会对其进行懒解析并调用委托给会话作用域内真正的ShoppingCart bean

这个东西好牛气的样子,这里涉及到了代理模式
*/

//注意:ScopedProxyMode.INTERFACES 表明这个代理要实现ShoppingCart接口,并将调用委托给实现bean
//注意:ScopedProxyMode.TARGET_CLASS 实现的是一个具体的类的话,spring必须使用CGLib 来生成基于类的代理

//XML中实现作用域的配置 这里用到了AOP Spring中面向切面
<bean id="cart" class="com.myapp.ShoppingCart">
  <aop:scoped=proxy  /> //spring会默认CGLib创建目标类的代理
</bean>

//这种 proxy-target-class="false" spring会创建基于接口的代理
<bean id="cart" class="com.myapp.ShoppingCart">
  <aop:scoped=proxy proxy-target-class="false"  /> 
</bean>

 五、运行时值注入

这节主要讲的就是spring中的表达式语言SpEL,其他的基本上没有特别重要的,值得思考的知识点。

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

(1)属性占位符(Property placeholder)

(2)Spring 表达式语言(SpEL)

属性占位符的语法是:${.....}

主要来说一下Spring 表达式语言(SpEL)

1、SpEL拥有很多特性,主要包括:

1)使用bean的ID来应用bean

2)调用方法和访问对象的属性

3)对值进行算术、关系和逻辑运算

4)正则表达式匹配

5)集合操作

2、SpEL样例

1)表示String值、浮点数、Boolean值

#{3.14159}

#{9.87E4}  对应着 98700

#{“hello”}

#{true}

2)引用bean、属性和方法

#{sgtPeppers}

#{sgtPeppers.artist}

#{sgtPeppers.selectArtist()}

#{sgtPeppers.selectArtist().toUpperCase()}

#{sgtPeppers.selectArtist()?.toUpperCase()}  避免出现空值,出现?避免出现空值

3)在表达式中使用类型

T{java.lang.Math}

T{java.lang.Math}.PI

T{java.lang.Math}.random()

4)SpEL运算符

#{2*T{java.lang.Math}.PI*circle.radius}

#{T(java.lang.Math).PI*circle.radius^2}

#{disc.title + 'by' + disc.artist}

#{counter.total == 100}

#{counter.total eq 100}

#{score > 100 ? "winner" : "loser"}

5)计算正则表达式

#{admin.email matches '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9._]+\\.com'}

6)计算集合

#{jukebox.songs[4].title}

#{jukebox.songs[T(java.lang.Math).random * jukebox.songs.size()].title}

#{jukebox.songs.?[artist eq 'Aerosmith']} 过滤歌曲,得到所有属性为Aerosmith的歌曲的小的集合

#{jukebox.songs.^[artist eq 'Aerosmith']}  查询集合中第一个属性为Aerosmith的歌曲

#{jukebox.songs.![title]} 投影运算符,将集合中的特定的属性放到一个新集合中去

#{jukebox.songs.?[artist eq 'Aerosmith']}.![title] 混合使用,把作者是Aerosmith的title属性放到一个集合中去

posted @ 2018-09-16 21:15  ssc在路上  阅读(201)  评论(0编辑  收藏  举报