已經發在了併發編程網: http://ifeve.com/spring4-2/html
1. 簡介.java
前些天spring4.2出來了, 從GA開始就一直在跟了, 前2天看完了全部Release Notes, 以爲記錄下我比較感興趣的特性.spring
官方的Release Notes:shell
我看的是4.2GA, 4.2RC3, 4.2RC2, 4.2RC1.編程
4.0和4.1的新特性, 能夠看看濤哥的博客: 併發
http://jinnianshilongnian.iteye.com/blog/1989381mvc
http://jinnianshilongnian.iteye.com/blog/2103752 app
這裏主要是講照官方文檔裏面列的, changelog裏面太多了 -.-!異步
http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#new-in-4.2
2. 核心改進.
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; } }
輸出: {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 {} }
輸出: 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; } }
輸出: 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 { }
輸出:
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); } }
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 方法, 默認不調用. } }
結果, 當調用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 ""; }
能夠看到, 可讓註解中讓人困惑的value更加讓人理解, Spring4.2中大量的註解都爲value添加了別名.
7) 其餘一些的改進, 不細說了, 主要是內部的改進, Java8的Stream, 日期等支持, javax.money等支持,
commons-pool2支持, 腳本增強等, Hibernate5支持, JMS加強 等等等等.
4. 總結
Spring4.2提供了更多的註解支持.
mvc的接下篇.