zoukankan      html  css  js  c++  java
  • 四、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属性放到一个集合中去

  • 相关阅读:
    Caused by: java.lang.ClassNotFoundException: org.apache.http.message.TokenParser
    Uncaught TypeError: (intermediate value)(intermediate value)(intermediate value)(intermediate value)...is not a function
    Spring学习一(依赖注入/Bean/注解等)
    SpringMVC学习五(resultful风格/异常处理/注解)
    SpringMVC学习四(文件上传/拦截器)
    SpringMVC学习三(静态资源/AJAX功能/乱码问题)
    springboot整合websocket
    springboot整合成c3p0
    Dubbo相关
    Cestos秒装jdk
  • 原文地址:https://www.cnblogs.com/ssh-html/p/9656504.html
Copyright © 2011-2022 走看看