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 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!