当前位置: 首页 > 科技观察

Spring Framework 6正式发布,携JDK 17&Jakarta EE开启新篇章

时间:2023-03-13 19:39:55 科技观察

SpringFramework6正式发布,与JDK17&JakartaEETitleLink专栏[YourBatman]-资讯/新特性、[YourBatman]-Spring技术栈新特性源码com/yourbatman/FXP-java-ee程序员专用网盘公益上线,注册送1G超小容量,帮你练减法https://wangpan.yourbatman.cnJava开发软件包(Mac)https://wangpan.yourbatman.cn/s/rEH0提取码:javakit女娲工程http://152.136.106.14:8761版本协议[MacOS13.0.1],[IDEA2022.2.4]前言在云原生发展的势头下,Spring也被打上了标签重,被Quarkus和Micronaut等新兴框架嘲讽为“又便宜又老”。不管你能不能和蔼可亲,一开始Spring就是以轻量级出圈(接口21就是证据),横扫JavaEE。作者年初的文章有一个“预测”,Spring团队将在2022年大动作,从年初到年底,可以说是呼声已久时间:SprngFramework6终于GA了(同时SpringBoot和SpringCloud前后都会发布RELEASE版本)。SprngFramework5于2017年9月发布,距今已有5年多了。作为Spring技术栈的基础:此次Spring框架的大版本升级是阻塞的,不向后兼容。值得注意的是,本文不试图解释为什么SpringFramework将JDK的基线从JDK8跳到了JDK17,并放弃了javax启用新的jakarta命名空间,这是另外一个系列话题。本文仅尝试介绍新功能。what'snew(新功能)老规矩,刷新我们关心的功能。最低要求JDK17SpringFramework6建立在JDK17之上。换句话说,如果你想使用SpringFramework6,你的JDK环境至少需要JDK17。就市场份额而言,JDK8其实已经跌到了第二位。那句老话“你想做什么就做什么,我用Java8”终将成为历史。春天来了这一次引领潮流。问题:为什么Spring团队选择了更高版本的JDK17而不是同样是LTS版本的JDK11?你不怕犯错吗?现在我创建一个SpringFramework6项目(基于maven):确定点后,添加依赖:启动Spring容器的代码:/***此处添加备注**@authorYourBatman*@since0.0.1*/@ComponentScanpublicclassApplication{publicstaticvoidmain(String[]args){ApplicationContextcontext=newAnnotationConfigApplicationContext(Application.class);System.out.println(context.getBean(DemoConfiguration.class));}}运行程序成功!当然,如果你不信邪,坚持用JDK8运行这个程序,那么你会得到这样的结果:从JavaEE到JakartaEE,javax命名空间已经成为历史,之后所有,现在快到2023年了。这一次,Spring团队也跟随了JDK,彻底抛弃了javax命名空间,拥抱了JakartaEE。JakartaEE估计很多读者可能没听说过,没关系!关于JavaEE和JakartaEE的“恩怨(历史渊源)”,有兴趣的一定要看看作者的这个系列:【YourBatman】-JavaEE,我跟你说清楚。从JakartaEE9开始,使用了新的jakarta.*命名空间。这次推荐从JakartaEE10入手。对应的技术主要有:JakartaServlet6.0JakartaServletJSPJSTL3.0JakartaValidation3.0JakartaWebSocket2.1JakartaPersistence3.1JakartaJMS3.1JakartaJSON2.1JakartaJSONBind3.0JakartaActivation2.1JakartaMail2.1JakartaTransaction2.0JakartaWSRS3.1JakartaXMLSOAP3.0JakartaXMLWS4.0另外,之前JDK中内置的一些JavaEE注解现在已经改变了它们的“包名”,比如有代表性的:JSR-330的@Inject、JSR250的@PostConstruct、@Predestroy及其常用的使用@Resource注释。LocalVariableTableParameterNameDiscoverer被标记为过时LocalVariableTableParameterNameDiscoverer是ParameterNameDiscoverer的一个实现类,用于查找参数名称。是Spring的经典实现,早在SpringFramework2.0就出现了。我们知道,java代码编译后,默认不会保留参数名,它使用了LocalVariableTable+ASM字节码技术来实现对参数名的查找。直到SpringFramework4.0(该版本开始支持JDK8)才出现了它的替代品:StandardReflectionParameterNameDiscoverer,它是基于JDK8标准的参数化实现的。JDK支持在编译时加入"-parameters参数,保留方法参数的名称。长期以来,SpringFramework为了考虑兼容性,只能降低LocalVariableTableParameterNameDiscoverer的优先级,但它并不"敢"killit显然这次不一样了,已经被标记为obsolete了:根据SpringOSS标准,标记为obsolete的class会在下一个medium版本中被移除。为此,为了防止“误用”,如果Spring发现你在运行过程中使用了这个类,你将收到如下warn警告:Usingdeprecated'-debug'fallbackforparameternameresolution。用'-parameters'代替编译受影响的代码或者避免它的自省:这才是一个优秀的框架应该有的样子:已经完成了太多的非功能性需求,而且很难出错。ListenableFuture被标记为已过时。JDK本来就有Future,后来Spring做了一个增强版的ListenableFuture。直到Java8的出现:有了CompletableFuture,就不用再用ListenableFuture了。不,这次我把它拿下来了。除了ListenableFuture本身,它的相关类都被标记为过时,比如:ListenableFutureCallback、SuccessCallback、FailureCallback等。移除CommonsMultipart等类。Spring-web一直支持两种上传文件的方式:基于ApacheCommonsFileuplod库的CommonsMultipartResolver方案。基于标准Servlet规范的StandardServletMultipartResolver方案对应MultipartResolver接口的两个实现类,如下图注(6之前的版本有两个实现):从这个版本(SpringFramework6)开始,ApacheCommons-based实现方案正式退出历史舞台,相关类也从源码中删除。此后,MultipartResolver只有且只有一个实现:值得一提的是:起初Spring框架推荐基于ApacheCommons库(即CommonsMultipartResolver)的解决方案上传文件,因为那样会有一个基于Servlet的解决方案的性能存在大问题;但是随着Servlet的更新(从Servlet3.0开始,javax.servlet.http.Part技术不再有性能问题),问题已经解决。HttpMethod不再是一个枚举,而是一个类。HttpMethod是web开发中比较常用的一个类。这一次,从enum->classtype的变化在大多数情况下是兼容的。只有在一些特殊情况下才需要注意。是的(比如不能再用switch,需要改成ifelse做分支逻辑)。PS:HttpMethod改成类后,重写了hashcode和equals方法,所以等价==比较不会有问题,请放心吃SpringFramework5.x版本:SpringFramework6版本:RestTemplate最低要求HttpClient5.xRestTemplate是spring-web对http请求的抽象。其底层实现技术可以是ApacheHttpClient、OkHttp、JDK实现等,具体使用的技术由ClientHttpRequestFactory的实现类决定。这次SpringFramework6针对的是Apache的实现,彻底抛弃了ApacheHttpClient4.x,拥抱了ApacheHttpClient5。虽然底层实现发生了变化,但是如果你的代码是针对Spring的RestTemplate编写的,它可以毫无察觉。仅标记@RequestMapping注解不再被扫描为Controller。你好!超级加享受!超级加享受!让我重复三遍,我明白我所知道的一切。在之前版本的SpringFramework中,spring-web会扫描标有@Controller或@RequestMapping的注解作为控制器:这个动作看似合理,使用方便,但在SpringCloud场景下却很“烦人”:@FeignClient+APIJar包是当前微服务通信的一种典型用法。在SpringBoot统一包扫描的背景下,大多数团队@EnableFeignClients也采用了统一的扫描策略,但这就是“灾难”的开始:很容易扫描一个@FeignClient接口作为控制器,从而暴露其他所有接口,除了大大降低启动速度和造成URL冲突外,还带来了重大的安全隐患。PS:虽然一般公司都会在脚手架层面默默解决这个问题,但据我所知,大部分团队都没有关注这个问题。比如作者写道:这样就可以了,SpringFramework6会帮我们解决掉这个麻烦,它的判断逻辑是这样的:controller只识别@Controller注解。这让我想起有同学用@Bean声明一个controller,现在不行了(大概率是误打了,因为class上有@RequestMapping注解),需要注意到。GenericApplicationContext支持AOT支持AOT是Spring拥抱Native和云原生的基础。当然,这也是它必须至少基于JDK17构建的原因之一。GraalVM为GraalVM原生图像提供了一流的支持。下一篇再说说SpringBoot3.0.0。PathMatchingResourcePatternResolver使用NIO和module进行扫描,加速Spring的扫描。一直是容器启动慢,甚至没有的重要原因之一。SpringFramework6版本对此做了很大的优化。其实早在SpringFramework5就开始使用索引方式进行扫描优化,效果还是比较显着的。但是这个方法不是100%通用的,用起来也不是很方便,所以没有掀起什么波澜(默认是不开启的)。这次不一样了:可选项变成了必选项,而且是唯一的选项。在此之前,PathMatchingResourcePatternResolver只能通过扫描xxx路径下的所有文件来发现bean(同步阻塞IO)。多模块是JDK瘦身的一种方式。本次我们将利用多模块+GraalVM的双重优势,大大加快扫描速度。核心代码在这里:强依赖千分尺的可观测性在SpringFramework6)的几个(不是全部)子项目中使用千分尺直接观测。例如:spring-web模块现在强烈依赖包io.micrometer:micrometer-observation来完成(编译)工作:micrometer之前只在SpringBoot中使用,现在SpringFramework的一些子项目也连接上了,所以观察会更直接更全面,这就是生态整合能力。总结作为Java领域最流行的框架(没有之一),SpringFramework拥有非常庞大的用户群和项目历史。这些历史现在看来是它的优点,但有时也会成为更重的包袱。Spring团队自然能感知到“危机”,于是就有了SpringNative项目来回应“还能吃饭吗?”这次SpringFramework6直接从JDK17开始,对GraalVM原生镜像提供了一流的支持。目的很明确:(适当地)卸掉包袱,证明自己没事。最后分享一句话:上山之人,莫笑下山之神。更何况春天还在如火如荼呢~