天天看點

Spring4.2新特性(一)

1. 簡介.

2. 核心改進.

1) @bean能注解在java8預設方法上了, 例如:

<code>01</code>

<code>@configuration</code>

<code>02</code>

<code>public</code> <code>class</code> <code>main </code><code>implements</code> <code>defaultiface {</code>

<code>03</code>

<code>04</code>

<code>    </code><code>public</code> <code>string name = </code><code>"main"</code><code>;</code>

<code>05</code>

<code>06</code>

<code>    </code><code>public</code> <code>static</code> <code>void</code> <code>main(string[] args) {</code>

<code>07</code>

<code>        </code><code>annotationconfigapplicationcontext context = </code><code>new</code><code>annotationconfigapplicationcontext(main.</code><code>class</code><code>);</code>

<code>08</code>

<code>09</code>

<code>        </code><code>//會有兩個main執行個體, 一個是config執行個體, 用來做配置解析, 一個是我們@bean注解的執行個體.</code>

<code>10</code>

<code>        </code><code>map&amp;lt;string, main&amp;gt; bean = context.getbeansoftype(main.</code><code>class</code><code>);</code>

<code>11</code>

<code>        </code><code>system.out.println(bean);</code>

<code>12</code>

<code>13</code>

<code>        </code><code>context.close();</code>

<code>14</code>

<code>    </code><code>}</code>

<code>15</code>

<code>16</code>

<code>    </code><code>@override</code>

<code>17</code>

<code>    </code><code>public</code> <code>string tostring() {</code>

<code>18</code>

<code>        </code><code>return</code> <code>"main [name="</code> <code>+ name + </code><code>"]"</code><code>;</code>

<code>19</code>

<code>20</code>

<code>}</code>

<code>21</code>

<code>22</code>

<code>interface</code> <code>defaultiface {</code>

<code>23</code>

<code>24</code>

<code>    </code><code>@bean</code>

<code>25</code>

<code>    </code><code>default</code> <code>main getmain() {</code>

<code>26</code>

<code>        </code><code>main main = </code><code>new</code> <code>main();</code>

<code>27</code>

<code>        </code><code>main.name = </code><code>"iface"</code><code>;</code>

<code>28</code>

<code>        </code><code>return</code> <code>main;</code>

<code>29</code>

<code>30</code>

輸出: {main=main [name=main], getmain=main [name=iface]}

可以看到, 我們注解在java8預設方法上的@bean注解已經生效了.

2) 配置類上的@import以前隻能引入配置類(注解了@configuration等的類), 現在可以引入一般的元件了, 比如啥注解都沒有的類.

<code>@import</code><code>(main.dao.</code><code>class</code><code>)</code>

<code>public</code> <code>class</code> <code>main {</code>

<code>        </code><code>main.dao bean = context.getbean(main.dao.</code><code>class</code><code>);</code>

<code>    </code><code>public</code> <code>static</code> <code>class</code> <code>dao {}</code>

輸出: com.haogrgr.test.main.main$dao@7f77e91b.

在4.2之前, 會報如下錯誤:

exception in thread “main” org.springframework.beans.factory.parsing.beandefinitionparsingexception: configuration problem: com.haogrgr.test.main.main$dao was @import’ed but is not annotated with @configuration nor does it declare any @bean methods; it does not implement importselector or extend importbeandefinitionregistrar. update the class to meet one of these requirements or do not attempt to @import it.

offending resource: class com.haogrgr.test.main.main$dao

at org.springframework.beans.factory.parsing.failfastproblemreporter.error(failfastproblemreporter.java:70)

at org.springframework.context.annotation.configurationclassbeandefinitionreader.registerbeandefinitionforimportedconfigurationclass(configurationclassbeandefinitionreader.java:164)

3)配置類上現在可以注解@order了, 使其能按預期的順序來處理, 比如(通過名字來覆寫bean配置等).

<code>@order</code><code>(</code><code>2</code><code>)</code>

<code>    </code><code>string name;</code>

<code>    </code><code>public</code> <code>main getmain() {</code>

<code>        </code><code>main.name = </code><code>"main"</code><code>;</code>

<code>        </code><code>annotationconfigapplicationcontext context = </code><code>new</code> <code>annotationconfigapplicationcontext(main.</code><code>class</code><code>, submain.</code><code>class</code><code>);</code>

<code>        </code><code>main bean = context.getbean(</code><code>"getmain"</code><code>, main.</code><code>class</code><code>);</code>

<code>        </code><code>//@order值大的, 會覆寫值小的, 比如如果submain的order為3, main的order為2時, 輸出submain</code>

<code>        </code><code>system.out.println(bean.name);</code>

<code>@order</code><code>(</code><code>3</code><code>)</code>

<code>class</code> <code>submain {</code>

<code>31</code>

<code>32</code>

<code>33</code>

<code>        </code><code>main.name = </code><code>"submain"</code><code>;</code>

<code>34</code>

<code>35</code>

<code>36</code>

輸出: submain, 可以通過修改order的值, 來使輸出為 main.

注: 4.2之前, 是根據annotationconfigapplicationcontext(main.class, submain.class) 初始化時參數的順序來處理的.

4) @resource注解的元素, 現在可以配合@lazy, 和@autowired一樣, 注入代理類, 來代理對應bean的請求.

<code>@import</code><code>(scopedbean.</code><code>class</code><code>)</code>

<code>    </code><code>@lazy</code> <code>@resource</code> <code>scopedbean bean;</code>

<code>        </code><code>main bean = context.getbean(main.</code><code>class</code><code>);</code>

<code>        </code><code>//如果bean上沒有@lazy注解, 則2個擷取的bean是一個執行個體, 加了@lazy注解後, 則2次擷取的是2個執行個體</code>

<code>        </code><code>system.out.println(bean.bean);</code>

<code>@scope</code><code>(value = configurablebeanfactory.scope_prototype)</code>

<code>class</code> <code>scopedbean {</code>

輸出:

1. 沒加@lazy時:

com.haogrgr.test.main.scopedbean@525f1e4e

2. 加了@lazy後:

com.haogrgr.test.main.scopedbean@6293abcc

com.haogrgr.test.main.scopedbean@7995092a

可以看到, 主要是為了友善實作scope代理(或延遲擷取, 比如注入時還沒初始化等)情況, 也就是當singleton引用prototype時, 就需要@lazy.

5) application event那套現在提供注解支援了, 比如以前常用的appcontextutil(擷取context, 提供靜态方法擷取bean)現在可以這麼寫.

具體可以看這篇文章: http://spring.io/blog/2015/02/11/better-application-events-in-spring-framework-4-2

<code>@import</code><code>(appcontextutil.</code><code>class</code><code>)</code>

<code>        </code><code>main bean = appcontextutil.getbean(main.</code><code>class</code><code>);</code>

<code>        </code><code>system.out.println(bean);</code><code>//輸出:com.haogrgr.test.main.main$$enhancerbyspringcglib$$10ba9cf8@4ae3c1cd</code>

<code>@component</code>

<code>class</code> <code>appcontextutil {</code>

<code>    </code><code>private</code> <code>static</code> <code>applicationcontext context = </code><code>null</code><code>;</code>

<code>    </code><code>@eventlistener</code>

<code>    </code><code>public</code> <code>void</code> <code>setapplicationcontext(contextrefreshedevent eve) {</code>

<code>        </code><code>context = eve.getapplicationcontext();</code>

<code>    </code><code>public</code> <code>static</code> <code>&amp;lt;t&amp;gt; t getbean(class&amp;lt;t&amp;gt; clazz) {</code>

<code>        </code><code>return</code> <code>context.getbean(clazz);</code>

eventlistener的屬性value和classes一樣, 都是用來指定要處理的事件, condition屬性可以使用spel來過濾event

還一個就是@transactionaleventlistener, 可以友善我在事務周期内處理一些事情, 比如事務送出後觸發某一事件.

一個場景就是, 當插入記錄送出事務後, 異步發送消息到其他系統, 或本地記錄日志等操作, 現在可以通過transactionaleventlistener來做了.

注: 下面的代碼僅供參考, 如果要運作, 自己搭一個資料庫環境吧, 這裡隻貼了相關的代碼.

<code>@service</code>

<code>public</code> <code>class</code> <code>transactioneventtestservice {</code>

<code>    </code><code>@resource</code>

<code>    </code><code>private</code> <code>testmapper mapper;</code>

<code>    </code><code>private</code> <code>applicationeventpublisher publisher;</code>

<code>    </code><code>@transactional</code>

<code>    </code><code>public</code> <code>void</code> <code>addtestmodel() {</code>

<code>        </code><code>testmodel model = </code><code>new</code> <code>testmodel();</code>

<code>        </code><code>model.setname(</code><code>"haogrgr"</code><code>);</code>

<code>        </code><code>mapper.insert(model);</code>

<code>        </code><code>//如果model沒有繼承applicationevent, 則内部會包裝為payloadapplicationevent</code>

<code>        </code><code>//對于@transactionaleventlistener, 會在事務送出後才執行listener處理邏輯.</code>

<code>        </code><code>//</code>

<code>        </code><code>//釋出事件, 事務送出後, 記錄日志, 或發送消息等操作</code>

<code>        </code><code>publisher.publishevent(model);</code>

<code>    </code><code>//當事務送出後, 才會真正的執行@transactionaleventlistener配置的listener, 如果listener抛異常, 方法傳回失敗, 但事務不會復原.</code>

<code>public</code> <code>class</code> <code>transactioneventlistener {</code>

<code>    </code><code>@transactionaleventlistener</code>

<code>    </code><code>public</code> <code>void</code> <code>handle(payloadapplicationevent&amp;lt;testmodel&amp;gt; event) {</code>

<code>        </code><code>system.out.println(event.getpayload().getname());</code>

<code>        </code><code>//這裡可以記錄日志, 發送消息等操作.</code>

<code>        </code><code>//這裡抛出異常, 會導緻addtestmodel方法異常, 但不會復原事務.</code>

<code>        </code><code>//注意, applicationeventpublisher不能使用線程池, 否則不會執行到這裡</code>

<code>        </code><code>//因為, 包裝類是通過threadlocal來判斷目前是否有活動的事務資訊.</code>

<code>        </code><code>//transactionaleventlistener.fallbackexecution就是為了決定當目前線程沒有事務上下文時,</code>

<code>37</code>

<code>        </code><code>//是否還調用 handle 方法, 預設不調用.</code>

<code>38</code>

<code>39</code>

結果, 當調用addtestmodel() 時, 會輸出”haogrgr”。官方說的比較少, 看了下源碼才知道怎麼用, 内部是包裝一下@transactionaleventlistener注解的方法,添加了一個擴充卡, applicationlistenermethodtransactionaladapter,内部通過transactionsynchronizationmanager.registersynchronization 注冊一個同步器釋出事務時, 記下event, 然後注冊一個同步器transactionsynchronizationeventadapter,當事務送出後, transactionsynchronizationmanager會回調上面注冊的同步擴充卡,這裡注冊就是放入到一個threadlocal裡面, 通過它來透傳參數。這時,transactionsynchronizationeventadapter内部才會真正的去調用handle方法.

6) 提供@aliasfor注解, 來給注解的屬性起别名, 讓使用注解時, 更加的容易了解(比如給value屬性起别名, 更容易讓人了解).

<code>@mainbean</code><code>(beanname = </code><code>"mainbean"</code><code>)</code>

<code>        </code><code>string[] beannames = context.getbeannamesfortype(main.</code><code>class</code><code>);</code>

<code>        </code><code>//當加上@aliasfor時, 輸出"mainbean"</code>

<code>        </code><code>//當去掉@aliasfor注解後, 輸出"main"</code>

<code>        </code><code>system.out.println(beannames[</code><code>0</code><code>]);</code>

<code>@target</code><code>(elementtype.type)</code>

<code>@retention</code><code>(retentionpolicy.runtime)</code>

<code>@documented</code>

<code>@interface</code> <code>mainbean {</code>

<code>    </code><code>@aliasfor</code><code>(annotation = component.</code><code>class</code><code>, attribute = </code><code>"value"</code><code>)</code>

<code>    </code><code>string beanname() </code><code>default</code> <code>""</code><code>;</code>

可以看到, 可以讓注解中讓人困惑的value更加讓人了解, spring4.2中大量的注解都為value添加了别名.

7) 其他一些的改進, 不細說了, 主要是内部的改進, java8的stream, 日期等支援, javax.money等支援,

commons-pool2支援, 腳本加強等, hibernate5支援, jms增強 等等等等.

4. 總結

spring4.2提供了更多的注解支援。