文档章节

Spring4.2新特性(一)

德胜
 德胜
发布于 2015/08/17 12:44
字数 1627
阅读 4986
收藏 15

已经发在了并发编程网: 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默认方法上了, 例如:

@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的接下篇.

© 著作权归作者所有

共有 人打赏支持
德胜
粉丝 56
博文 31
码字总数 41512
作品 0
长沙
加载中

评论(3)

德胜
德胜

引用来自“西夏一品堂”的评论

ImportBeanDefinitionRegistrar 这个接口是干啥的?
再多说点吧, Spring解析Java配置类的时候, 会判断类是不是标注了@Import注解, 然后会判断, 如果Import注解的value是ImportBeanDefinitionRegistrar类型, 会存到一个变量, 后面初始化bean工程完成后, 会回调ImportBeanDefinitionRegistrar.
德胜
德胜

引用来自“西夏一品堂”的评论

ImportBeanDefinitionRegistrar 这个接口是干啥的?
用来手动注册bean定义的, 可以实现类似于Mybatis-Spring提供的扫描Mapper接口并注册其bean定义, 事实上@MapperScan注解就@Import了MapperScannerRegistrar这个类, 而这个类实现了上面的接口, 来扫描Mapper并注册bean定义.
西夏一品堂
西夏一品堂
ImportBeanDefinitionRegistrar 这个接口是干啥的?
Fastjson、Jackson与SpringMVC整合的MessageConverter配置

1.Jackson maven依赖 springmvc-servlet.xml中配置 2.FastJson 由于FastJson针对Spring4.2以后进行特殊优化,具体如图 所以FastJson可以分为Spring4.2及以下配置和Spring4.2以上的不同配置 ...

楠木楠
2016/11/30
1K
0
SpringBoot跨域问题解决方案

一。允许全部请求跨域许可的代码: 需要继承WebMvcConfigurerAdapter类   public class MyWebAppConfigurer extends WebMvcConfigurerAdapter{ } View Code 二。有针对性的配置: 同样需要继...

java~nick
2017/12/20
0
0
springmvc multipart/form-data controller不能获取表单的值

该配置的都配置了但是还是不能获取表单的值 去掉multipart/form-data controller可以获取 jar包已经导入了 我用的是ssm (spring4.2) maven springMVC.xml...

sami110
2016/09/19
1K
4
Spring4+SpringMVC+Hibernate4整合,你可能会遇到的问题

1、java.lang.ClassNotFoundException: org.springframework.orm.hibernate4.LocalSessionFactoryBean 缺少spring-orm jar包,如下: <dependency> <groupId>org.springframework</groupId> ......

阿阿阿阿阿局
2016/08/16
98
0
Spring测试框架springContextTest

ssh版本:spring4.2/struts2.3/hibernate4;除了一些必要的jar包外,使用spring自带的测试框架,还要额外导入spring-test-4.2.2.jar。 junit使用的是junit4.7,相关测试代码如下: package ...

liuhuics10
2015/12/09
438
0

没有更多内容

加载失败,请刷新页面

加载更多

WinDbg

参考来自:http://www.cnit.net.cn/?id=225 SRV*C:\Symbols*http://msdl.microsoft.com/download/symbols ctrl + d to open dump_file Microsoft (R) Windows Debugger Version 6.12.0002.633......

xueyuse0012
今天
2
0
OSChina 周五乱弹 —— 想不想把92年的萝莉退货

Osc乱弹歌单(2018)请戳(这里) 【今日歌曲】 @罗马的王:分享松澤由美的单曲《地球ぎ》 很久没看圣斗士星矢了 《地球ぎ》- 松澤由美 手机党少年们想听歌,请使劲儿戳(这里) @开源中国首...

小小编辑
今天
14
1
springBoot条件配置

本篇介绍下,如何通过springboot的条件配置,控制Bean的创建 介绍下开发环境 JDK版本1.8 springboot版本是1.5.2 开发工具为 intellij idea(2018.2) 开发环境为 15款MacBook Pro 前言 很多时候,...

贺小五
今天
1
0
javascript source map 的使用

之前发现VS.NET会为压缩的js文添加一个与文件名同名的.map文件,一直没有搞懂他是用来做什么的,直接删除掉运行时浏览器又会报错,后来google了一直才真正搞懂了这个小小的map文件背后的巨大...

粒子数反转
昨天
1
0
谈谈如何学Linux和它在如今社会的影响

昨天,还在农耕脑力社会,今天已经人工智能技术、大数据、信息技术的科技社会了,高速开展并迅速浸透到当今科技社会的各个方面,Linux日益成为人们信息时代的到来,更加考验我们对信息的处理程...

linux-tao
昨天
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部