544
阿裏雲
技術社區[雲棲]
Spring4.2新特性(一)
1. 簡介.
前些天spring4.2出來了, 從GA開始就一直在跟了, 前2天看完了所有官方Release Notes, 覺得記錄下我比較感興趣的特性.
我看的是4.2GA, 4.2RC3, 4.2RC2, 4.2RC1。4.0和4.1的新特性, 可以看看濤哥的博客。這裏主要是講照官方文檔裏麵列的, changelog裏麵太多了 -.-!
2. 核心改進.
1) @Bean能注解在Java8默認方法上了, 例如:
02 |
public class Main implements DefaultIface {
|
04 |
public String name = "main" ;
|
06 |
public static void main(String[] args) {
|
07 |
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Main. class );
|
09 |
//會有兩個Main實例, 一個是config實例, 用來做配置解析, 一個是我們@Bean注解的實例.
|
10 |
Map<String, Main> bean = context.getBeansOfType(Main. class );
|
11 |
System.out.println(bean);
|
17 |
public String toString() {
|
18 |
return "Main [name=" + name + "]" ;
|
22 |
interface DefaultIface {
|
25 |
default Main getMain() {
|
26 |
Main main = new Main();
|
輸出: {main=Main [name=main], getMain=Main [name=iface]}
可以看到, 我們注解在Java8默認方法上的@Bean注解已經生效了.
2) 配置類上的@Import以前隻能引入配置類(注解了@Configuration等的類), 現在可以引入一般的組件了, 比如啥注解都沒有的類.
01 |
@Import (Main.Dao. class )
|
05 |
public static void main(String[] args) {
|
06 |
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Main. class );
|
08 |
Main.Dao bean = context.getBean(Main.Dao. class );
|
09 |
System.out.println(bean);
|
14 |
public static class Dao {}
|
輸出: 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配置等).
08 |
public Main getMain() {
|
09 |
Main main = new Main();
|
14 |
public static void main(String[] args) {
|
15 |
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Main. class , SubMain. class );
|
17 |
Main bean = context.getBean( "getMain" , Main. class );
|
19 |
//@Order值大的, 會覆蓋值小的, 比如如果submain的order為3, main的order為2時, 輸出submain
|
20 |
System.out.println(bean.name);
|
31 |
public Main getMain() {
|
32 |
Main main = new Main();
|
33 |
main.name = "submain" ;
|
輸出: submain, 可以通過修改Order的值, 來使輸出為 main.
注: 4.2之前, 是根據AnnotationConfigApplicationContext(Main.class, SubMain.class) 初始化時參數的順序來處理的.
4) @Resource注解的元素, 現在可以配合@Lazy, 和@Autowired一樣, 注入代理類, 來代理對應bean的請求.
01 |
@Import (ScopedBean. class )
|
05 |
@Lazy @Resource ScopedBean bean;
|
07 |
public static void main(String[] args) {
|
08 |
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Main. class );
|
10 |
Main bean = context.getBean(Main. class );
|
12 |
//如果bean上沒有@Lazy注解, 則2個獲取的bean是一個實例, 加了@Lazy注解後, 則2次獲取的是2個實例
|
13 |
System.out.println(bean.bean);
|
14 |
System.out.println(bean.bean);
|
20 |
@Scope (value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
|
輸出:
1. 沒加@Lazy時:
com.haogrgr.test.main.ScopedBean@525f1e4e
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)現在可以這麼寫.
具體可以看這篇文章: https://spring.io/blog/2015/02/11/better-application-events-in-spring-framework-4-2
01 |
@Import (AppContextUtil. class )
|
05 |
public static void main(String[] args) {
|
06 |
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Main. class );
|
08 |
Main bean = AppContextUtil.getBean(Main. class );
|
09 |
System.out.println(bean); //輸出:com.haogrgr.test.main.Main$$EnhancerBySpringCGLIB$$10ba9cf8@4ae3c1cd
|
16 |
class AppContextUtil {
|
18 |
private static ApplicationContext context = null ;
|
21 |
public void setApplicationContext(ContextRefreshedEvent eve) {
|
22 |
context = eve.getApplicationContext();
|
25 |
public static <T> T getBean(Class<T> clazz) {
|
26 |
return context.getBean(clazz);
|
EventListener的屬性value和classes一樣, 都是用來指定要處理的事件, condition屬性可以使用spel來過濾event
還一個就是@TransactionalEventListener, 可以方便我在事務周期內處理一些事情, 比如事務提交後觸發某一事件.
一個場景就是, 當插入記錄提交事務後, 異步發送消息到其他係統, 或本地記錄日誌等操作, 現在可以通過TransactionalEventListener來做了.
注: 下麵的代碼僅供參考, 如果要運行, 自己搭一個數據庫環境吧, 這裏隻貼了相關的代碼.
02 |
public class TransactionEventTestService {
|
05 |
private TestMapper mapper;
|
08 |
private ApplicationEventPublisher publisher;
|
11 |
public void addTestModel() {
|
12 |
TestModel model = new TestModel();
|
13 |
model.setName( "haogrgr" );
|
16 |
//如果model沒有繼承ApplicationEvent, 則內部會包裝為PayloadApplicationEvent
|
17 |
//對於@TransactionalEventListener, 會在事務提交後才執行Listener處理邏輯.
|
19 |
//發布事件, 事務提交後, 記錄日誌, 或發送消息等操作
|
20 |
publisher.publishEvent(model);
|
22 |
//當事務提交後, 才會真正的執行@TransactionalEventListener配置的Listener, 如果Listener拋異常, 方法返回失敗, 但事務不會回滾.
|
27 |
public class TransactionEventListener {
|
29 |
@TransactionalEventListener
|
30 |
public void handle(PayloadApplicationEvent<TestModel> event) {
|
31 |
System.out.println(event.getPayload().getName());
|
33 |
//這裏拋出異常, 會導致addTestModel方法異常, 但不會回滾事務.
|
34 |
//注意, ApplicationEventPublisher不能使用線程池, 否則不會執行到這裏
|
35 |
//因為, 包裝類是通過ThreadLocal來判斷當前是否有活動的事務信息.
|
36 |
//TransactionalEventListener.fallbackExecution就是為了決定當當前線程沒有事務上下文時,
|
37 |
//是否還調用 handle 方法, 默認不調用.
|
結果, 當調用addTestModel() 時, 會輸出”haogrgr”。官方說的比較少, 看了下源碼才知道怎麼用, 內部是包裝一下@TransactionalEventListener注解的方法,添加了一個適配器, ApplicationListenerMethodTransactionalAdapter,內部通過TransactionSynchronizationManager.registerSynchronization 注冊一個同步器發布事務時, 記下event, 然後注冊一個同步器TransactionSynchronizationEventAdapter,當事務提交後, TransactionSynchronizationManager會回調上麵注冊的同步適配器,這裏注冊就是放入到一個ThreadLocal裏麵, 通過它來透傳參數。這時,TransactionSynchronizationEventAdapter內部才會真正的去調用handle方法.
6) 提供@AliasFor注解, 來給注解的屬性起別名, 讓使用注解時, 更加的容易理解(比如給value屬性起別名, 更容易讓人理解).
01 |
@MainBean (beanName = "mainbean" )
|
04 |
public static void main(String[] args) {
|
05 |
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Main. class );
|
07 |
String[] beannames = context.getBeanNamesForType(Main. class );
|
09 |
//當加上@AliasFor時, 輸出"mainbean"
|
10 |
//當去掉@AliasFor注解後, 輸出"main"
|
11 |
System.out.println(beannames[ 0 ]);
|
17 |
@Target (ElementType.TYPE)
|
18 |
@Retention (RetentionPolicy.RUNTIME)
|
23 |
@AliasFor (annotation = Component. class , attribute = "value" )
|
24 |
String beanName() default "" ;
|
可以看到, 可以讓注解中讓人困惑的value更加讓人理解, Spring4.2中大量的注解都為value添加了別名.
7) 其他一些的改進, 不細說了, 主要是內部的改進, Java8的Stream, 日期等支持, javax.money等支持,
commons-pool2支持, 腳本加強等, Hibernate5支持, JMS增強 等等等等.
4. 總結
Spring4.2提供了更多的注解支持。
最後更新:2017-05-22 14:32:27