Spring4.2新特性(一)
1. 簡介.
前些天spring4.2出來了, 從GA開始就一直在跟了, 前2天看完了所有官方Release Notes, 覺得記錄下我比較感興趣的特性.
我看的是4.2GA, 4.2RC3, 4.2RC2, 4.2RC1。4.0和4.1的新特性, 可以看看濤哥的博客。這里主要是講照官方文檔里面列的, changelog里面太多了 -.-!
核心改進. </p>
1) @Bean能注解在Java8默認方法上了, 例如:
@Configuration public class Main implements DefaultIface {
public String name = "main";
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Main.class); //會有兩個Main實例, 一個是config實例, 用來做配置解析, 一個是我們@Bean注解的實例. Map<String, Main> bean = context.getBeansOfType(Main.class); System.out.println(bean); context.close();
}
@Override public String toString() {
return "Main [name=" + name + "]";
} }
interface DefaultIface {
@Bean
default Main getMain() {
Main main = new Main();
main.name = "iface";
return main;
}
}</pre>
輸出: {main=Main [name=main], getMain=Main [name=iface]}
可以看到, 我們注解在Java8默認方法上的@Bean注解已經生效了.
2) 配置類上的@Import以前只能引入配置類(注解了@Configuration等的類), 現在可以引入一般的組件了, 比如啥注解都沒有的類.
@Import(Main.Dao.class) @Configuration public class Main {public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Main.class); Main.Dao bean = context.getBean(Main.Dao.class); System.out.println(bean); context.close(); } public static class Dao {}
}</pre>
輸出: 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配置等).
@Order(2) @Configuration public class Main {String name; @Bean public Main getMain() { Main main = new Main(); main.name = "main"; return main; } public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Main.class, SubMain.class); Main bean = context.getBean("getMain", Main.class); //@Order值大的, 會覆蓋值小的, 比如如果submain的order為3, main的order為2時, 輸出submain System.out.println(bean.name); context.close(); }
}
@Order(3) @Configuration class SubMain {
@Bean public Main getMain() { Main main = new Main(); main.name = "submain"; return main; }
}</pre>
輸出: submain, 可以通過修改Order的值, 來使輸出為 main.
注: 4.2之前, 是根據AnnotationConfigApplicationContext(Main.class, SubMain.class) 初始化時參數的順序來處理的.
4) @Resource注解的元素, 現在可以配合@Lazy, 和@Autowired一樣, 注入代理類, 來代理對應bean的請求.
@Import(ScopedBean.class) @Configuration public class Main {@Lazy @Resource ScopedBean bean; public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Main.class); Main bean = context.getBean(Main.class); //如果bean上沒有@Lazy注解, 則2個獲取的bean是一個實例, 加了@Lazy注解后, 則2次獲取的是2個實例 System.out.println(bean.bean); System.out.println(bean.bean); context.close(); }
}
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) class ScopedBean { }</pre>
輸出:
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)現在可以這么寫.
具體可以看這篇文章: http://spring.io/blog/2015/02/11/better-application-events-in-spring-framework-4-2
@Import(AppContextUtil.class) @Configuration public class Main {public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Main.class); Main bean = AppContextUtil.getBean(Main.class); System.out.println(bean);//輸出:com.haogrgr.test.main.Main$$EnhancerBySpringCGLIB$$10ba9cf8@4ae3c1cd context.close(); }
}
@Component class AppContextUtil {
private static ApplicationContext context = null; @EventListener public void setApplicationContext(ContextRefreshedEvent eve) { context = eve.getApplicationContext(); } public static <T> T getBean(Class<T> clazz) { return context.getBean(clazz); }
}</pre>
EventListener的屬性value和classes一樣, 都是用來指定要處理的事件, condition屬性可以使用spel來過濾event
還一個就是@TransactionalEventListener, 可以方便我在事務周期內處理一些事情, 比如事務提交后觸發某一事件.
一個場景就是, 當插入記錄提交事務后, 異步發送消息到其他系統, 或本地記錄日志等操作, 現在可以通過TransactionalEventListener來做了.
注: 下面的代碼僅供參考, 如果要運行, 自己搭一個數據庫環境吧, 這里只貼了相關的代碼.
@Service public class TransactionEventTestService {@Resource private TestMapper mapper; @Resource private ApplicationEventPublisher publisher; @Transactional public void addTestModel() { TestModel model = new TestModel(); model.setName("haogrgr"); mapper.insert(model); //如果model沒有繼承ApplicationEvent, 則內部會包裝為PayloadApplicationEvent //對于@TransactionalEventListener, 會在事務提交后才執行Listener處理邏輯. // //發布事件, 事務提交后, 記錄日志, 或發送消息等操作 publisher.publishEvent(model); } //當事務提交后, 才會真正的執行@TransactionalEventListener配置的Listener, 如果Listener拋異常, 方法返回失敗, 但事務不會回滾.
}
@Component public class TransactionEventListener {
@TransactionalEventListener public void handle(PayloadApplicationEvent<TestModel> event) { System.out.println(event.getPayload().getName()); //這里可以記錄日志, 發送消息等操作. //這里拋出異常, 會導致addTestModel方法異常, 但不會回滾事務. //注意, ApplicationEventPublisher不能使用線程池, 否則不會執行到這里 //因為, 包裝類是通過ThreadLocal來判斷當前是否有活動的事務信息. //TransactionalEventListener.fallbackExecution就是為了決定當當前線程沒有事務上下文時, //是否還調用 handle 方法, 默認不調用. }
}</pre>
結果, 當調用addTestModel() 時, 會輸出”haogrgr”。官方說的比較少, 看了下源碼才知道怎么用, 內部是包裝一下@TransactionalEventListener注解的方法,添加了一個適配器, ApplicationListenerMethodTransactionalAdapter,內部通過TransactionSynchronizationManager.registerSynchronization 注冊一個同步器發布事務時, 記下event, 然后注冊一個同步器TransactionSynchronizationEventAdapter,當事務提交后, TransactionSynchronizationManager會回調上面注冊的同步適配器,這里注冊就是放入到一個ThreadLocal里面, 通過它來透傳參數。這時,TransactionSynchronizationEventAdapter內部才會真正的去調用handle方法.
6) 提供@AliasFor注解, 來給注解的屬性起別名, 讓使用注解時, 更加的容易理解(比如給value屬性起別名, 更容易讓人理解).
@MainBean(beanName = "mainbean") public class Main {public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Main.class); String[] beannames = context.getBeanNamesForType(Main.class); //當加上@AliasFor時, 輸出"mainbean" //當去掉@AliasFor注解后, 輸出"main" System.out.println(beannames[0]); context.close(); }
}
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Configuration @interface MainBean {
@AliasFor(annotation = Component.class, attribute = "value") String beanName() default "";
}</pre>
可以看到, 可以讓注解中讓人困惑的value更加讓人理解, Spring4.2中大量的注解都為value添加了別名.
7) 其他一些的改進, 不細說了, 主要是內部的改進, Java8的Stream, 日期等支持, javax.money等支持,
commons-pool2支持, 腳本加強等, Hibernate5支持, JMS增強 等等等等.
4. 總結
Spring4.2提供了更多的注解支持。
原創文章,轉載請注明: 轉載自并發編程網 – ifeve.com
本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!