Spring4.2新特性(一)

已经发在了并发编程网: http://ifeve.com/spring4-2/

1. 简介.

    前些天spring4.2出来了, 从GA开始就一直在跟了, 前2天看完了所有Release Notes, 觉得记录下我比较感兴趣的特性.

    官方的Release Notes:

    https://jira.spring.io/browse/SPR?selectedTab=com.atlassian.jira.jira-projects-plugin:changelog-panel&allVersions=true

    我看的是4.2GA, 4.2RC3, 4.2RC2, 4.2RC1.

    4.0和4.1的新特性, 可以看看涛哥的博客: 

    http://jinnianshilongnian.iteye.com/blog/1989381

    http://jinnianshilongnian.iteye.com/blog/2103752

    这里主要是讲照官方文档里面列的, changelog里面太多了 -.-!

    http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#new-in-4.2

2. 核心改进.

    1) @Bean能注解在Java8默认方法上了, 例如:

1@Configuration 2public class Main implements DefaultIface { 3 4    public String name = "main"; 5 6    public static void main(String[] args) { 7        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Main.class); 8 9        //会有两个Main实例, 一个是config实例, 用来做配置解析, 一个是我们@Bean注解的实例. 10        Map<String, Main> bean = context.getBeansOfType(Main.class); 11        System.out.println(bean); 12 13        context.close(); 14    } 15 16    @Override 17    public String toString() { 18        return "Main [name=" + name + "]"; 19    } 20} 21 22interface DefaultIface { 23 24    @Bean 25    default Main getMain() { 26        Main main = new Main(); 27        main.name = "iface"; 28        return main; 29    } 30}

    输出: {main=Main [name=main], getMain=Main [name=iface]}

    可以看到, 我们注解在Java8默认方法上的@Bean注解已经生效了.

   2) 配置类上的@Import以前只能引入配置类(注解了@Configuration等的类), 现在可以引入一般的组件了, 比如啥注解都没有的类.

1@Import(Main.Dao.class) 2@Configuration 3public class Main { 4 5    public static void main(String[] args) { 6        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Main.class); 7 8        Main.Dao bean = context.getBean(Main.Dao.class); 9        System.out.println(bean); 10 11        context.close(); 12    } 13 14    public static class Dao {} 15}

    输出: com.haogrgr.test.main.Main$Dao@7f77e91b.

    在4.2之前, 会报如下错误: 

1Exception 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. 2Offending resource: class com.haogrgr.test.main.Main$Dao 3    at org.springframework.beans.factory.parsing.FailFastProblemReporter.error(FailFastProblemReporter.java:70) 4    at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.registerBeanDefinitionForImportedConfigurationClass(ConfigurationClassBeanDefinitionReader.java:164) 5    ...

    3)配置类上现在可以注解@Order了, 使其能按预期的顺序来处理, 比如(通过名字来覆盖Bean配置等).

1@Order(2) 2@Configuration 3public class Main { 4 5    String name; 6 7    @Bean 8    public Main getMain() { 9        Main main = new Main(); 10        main.name = "main"; 11        return main; 12    } 13 14    public static void main(String[] args) { 15        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Main.class, SubMain.class); 16 17        Main bean = context.getBean("getMain", Main.class); 18 19        //@Order值大的, 会覆盖值小的, 比如如果submain的order为3, main的order为2时, 输出submain 20        System.out.println(bean.name); 21 22        context.close(); 23    } 24} 25 26@Order(3) 27@Configuration 28class SubMain { 29 30    @Bean 31    public Main getMain() { 32        Main main = new Main(); 33        main.name = "submain"; 34        return main; 35    } 36}

    输出: submain, 可以通过修改Order的值, 来使输出为 main.

    注: 4.2之前, 是根据AnnotationConfigApplicationContext(Main.class, SubMain.class) 初始化时参数的顺序来处理的.

    4) @Resource注解的元素, 现在可以配合@Lazy, 和@Autowired一样, 注入代理类, 来代理对应bean的请求.

1@Import(ScopedBean.class) 2@Configuration 3public class Main { 4 5    @Lazy @Resource ScopedBean bean; 6 7    public static void main(String[] args) { 8        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Main.class); 9 10        Main bean = context.getBean(Main.class); 11 12        //如果bean上没有@Lazy注解, 则2个获取的bean是一个实例, 加了@Lazy注解后, 则2次获取的是2个实例 13        System.out.println(bean.bean); 14        System.out.println(bean.bean); 15 16        context.close(); 17    } 18} 19 20@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) 21class ScopedBean { 22}

    输出: 

    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

1@Import(AppContextUtil.class) 2@Configuration 3public class Main { 4 5    public static void main(String[] args) { 6        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Main.class); 7 8        Main bean = AppContextUtil.getBean(Main.class); 9        System.out.println(bean);//输出:com.haogrgr.test.main.Main$$EnhancerBySpringCGLIB$$10ba9cf8@4ae3c1cd 10 11        context.close(); 12    } 13} 14 15@Component 16class AppContextUtil { 17 18    private static ApplicationContext context = null; 19 20    @EventListener 21    public void setApplicationContext(ContextRefreshedEvent eve) { 22        context = eve.getApplicationContext(); 23    } 24 25    public static <T> T getBean(Class<T> clazz) { 26        return context.getBean(clazz); 27    } 28}

    EventListener的属性value和classes一样, 都是用来指定要处理的事件, condition属性可以使用spel来过滤event

    还一个就是@TransactionalEventListener, 可以方便我在事务周期内处理一些事情, 比如事务提交后触发某一事件.

    一个场景就是, 当插入记录提交事务后, 异步发送消息到其他系统, 或本地记录日志等操作, 现在可以通过TransactionalEventListener来做了. 

    注: 下面的代码仅供参考, 如果要运行, 自己搭一个数据库环境吧, 这里只贴了相关的代码. 

1@Service 2public class TransactionEventTestService { 3 4    @Resource 5    private TestMapper mapper; 6 7    @Resource 8    private ApplicationEventPublisher publisher; 9 10    @Transactional 11    public void addTestModel() { 12        TestModel model = new TestModel(); 13        model.setName("haogrgr"); 14        mapper.insert(model); 15 16        //如果model没有继承ApplicationEvent, 则内部会包装为PayloadApplicationEvent 17        //对于@TransactionalEventListener, 会在事务提交后才执行Listener处理逻辑. 18        // 19        //发布事件, 事务提交后, 记录日志, 或发送消息等操作 20        publisher.publishEvent(model); 21    } 22    //当事务提交后, 才会真正的执行@TransactionalEventListener配置的Listener, 如果Listener抛异常, 方法返回失败, 但事务不会回滚. 23 24} 25 26@Component 27public class TransactionEventListener { 28 29    @TransactionalEventListener 30    public void handle(PayloadApplicationEvent<TestModel> event) { 31        System.out.println(event.getPayload().getName()); 32        //这里可以记录日志, 发送消息等操作. 33        //这里抛出异常, 会导致addTestModel方法异常, 但不会回滚事务. 34        //注意, ApplicationEventPublisher不能使用线程池, 否则不会执行到这里 35        //因为, 包装类是通过ThreadLocal来判断当前是否有活动的事务信息. 36        //TransactionalEventListener.fallbackExecution就是为了决定当当前线程没有事务上下文时,  37        //是否还调用 handle 方法, 默认不调用. 38    } 39}

    结果, 当调用addTestModel() 时, 会输出"haogrgr".

    官方说的比较少, 看了下源码才知道怎么用, 内部是包装一下@TransactionalEventListener注解的方法, 

    添加了一个适配器, ApplicationListenerMethodTransactionalAdapter, 

    内部通过TransactionSynchronizationManager.registerSynchronization 注册一个同步器

    发布事务时, 记下event, 然后注册一个同步器TransactionSynchronizationEventAdapter, 

    当事务提交后, TransactionSynchronizationManager会回调上面注册的同步适配器, 

    这里注册就是放入到一个ThreadLocal里面, 通过它来透传参数.

    这时, TransactionSynchronizationEventAdapter内部才会真正的去调用handle方法.

    6) 提供@AliasFor注解, 来给注解的属性起别名, 让使用注解时, 更加的容易理解(比如给value属性起别名, 更容易让人理解).

1@MainBean(beanName = "mainbean") 2public class Main { 3 4    public static void main(String[] args) { 5        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Main.class); 6 7        String[] beannames = context.getBeanNamesForType(Main.class); 8         9        //当加上@AliasFor时, 输出"mainbean" 10        //当去掉@AliasFor注解后, 输出"main" 11        System.out.println(beannames[0]); 12 13        context.close(); 14    } 15} 16 17@Target(ElementType.TYPE) 18@Retention(RetentionPolicy.RUNTIME) 19@Documented 20@Configuration 21@interface MainBean { 22 23    @AliasFor(annotation = Component.class, attribute = "value") 24    String beanName() default ""; 25}

    可以看到, 可以让注解中让人困惑的value更加让人理解, Spring4.2中大量的注解都为value添加了别名.

    7) 其他一些的改进, 不细说了, 主要是内部的改进, Java8的Stream, 日期等支持, javax.money等支持,  

        commons-pool2支持, 脚本加强等, Hibernate5支持, JMS增强 等等等等.

4. 总结

    Spring4.2提供了更多的注解支持.

    mvc的接下篇.

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

java将前端的json数组字符串转换为列表

记录下在前端通过ajax提交了一个json数组的字符串,在后端如何转换为列表。前端数据转化与请求varcontracts{id:'1',name:'yanggb合同1'},{id:'2',name:'yanggb合同2'},{id:'3',name:'yang