spring6新特性汇总
part1 spring6.0新特性
spring6.0 2022年11月。新一代框架带jdk17&jakarta ee9
https://www.graalvm.org/
part2 AOP&事务
1.AOP:面向切面编程
-
通过预编译方式和运行期动态 代理实现程序功能的统一维护的一种技术。
-
使用场景:
权限的校验
日志记录
事务处理
性能检测 -
常用代理方式
- jdk动态代理,只能代理接口
- Cglib动态代理,spring提供,只能代理类
-
jdk动态代理:Proxy类,该类中有一个静态方法,可以创建代理对象
/**
*classloader:类加载器,加载代理对象,Class对象就可以获取类加载器
*
*Class<?>[] interfaces 接口的字节码数组对象,通过该参数,生成代理对象,可以知道该接口*有那些方法
*
*InvocationHandler h :执行句柄,也是一个接口; 当生成代理对象后,调用原目标方法的时候,自动进入到InvocationHandler中invoke方法;
*
*invoke(Object proxy,Method method,Object[] args):
* Object proxy:代理对象
* Method method:代理对象目前正在调用的方法,invoke(Object ...param)
* Object[] args:目标方法参数
*
*/
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h) {
Objects.requireNonNull(h);
@SuppressWarnings("removal")
final Class<?> caller = System.getSecurityManager() == null
? null
: Reflection.getCallerClass();
/*
* Look up or generate the designated proxy class and its constructor.
*/
Constructor<?> cons = getProxyConstructor(caller, loader, interfaces);
return newProxyInstance(caller, cons, h);
}
-
Cglib动态代理
Enhance cglib的核心类,主要作用是先设置属性,再创建代理对象- setSuperClass(被代理对象.getClass()):设置目标类的字节码对象
- setCallback(MethodInceptor对象)设置回调函数,让代理对象执行的时候进入回调函数执行具体逻辑;
该方法的参数 :callback接口,传递的是子接口MethodInceptor匿名内部类 - create() 创建代理对象的方法
自定义实现cglib动态代理:
1.导入cglib依赖库
<!--使用Cglib动态代理,需要导入spring-context--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>6.1.3</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>6.1.3</version> </dependency>
2.代码实现:
package com.spring;
public class PersonService {
public void work(){
System.out.println("我爱上班");
}
}
package com.spring;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* 创建代理对象
*/
public class ProxyBeanFactoryCglib {
//1.定义代理对象
private PersonService target;
//2.构造方法初始化代理对象
public ProxyBeanFactoryCglib(PersonService target){
this.target=target;
}
//3.编写一个获取代理对象的方法
public Object getProxy(){
//1.创建enchance
Enhancer enhancer=new Enhancer();
//2.给enhancer对象设置setSuperclass
enhancer.setSuperclass(target.getClass());
//3.给enhancer设置回调函数
enhancer.setCallback(new MethodInterceptor() {
/**
*
* @param proxy 代理对象
* @param method 代理对象正在调用的方法对象
* @param args 代理对象方法传递的参数
* @param methodProxy 方法的代理对象
* @return
*/
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
//1.通过method对象,反射调用原始类的方法
method.invoke(target);
//2.补充一个记录日志的功能
System.out.println("记录日志,记录工作状态。。");
return null;
}
});
//4.调用enhancer的create方法,创建代理对象
return enhancer.create();
}
}
package com.spring;
/**
* 测试类
*/
public class PersonServiceTest {
public static void main(String[] args) {
//1.创建PersonService 这个的被代理对象
PersonService personService=new PersonService();
//2.创建ProxyBeanFactoryCglib
ProxyBeanFactoryCglib proxyBeanFactoryCglib=new ProxyBeanFactoryCglib(personService);
//3.调用ProxyBeanFactoryCglib的getProxy方法
PersonService proxy= (PersonService) proxyBeanFactoryCglib.getProxy();
//4.调用work方法,看是否有日志
proxy.work();
}
}
AOP术语:
- 目标类target:就是需要增强的那个类
- 代理类proxy:就是自定义的代理的对象
- 连接点joinPoint:程序执行的一个特定位置,spring仅支持方法的连接点
- 切入点pointCut:就是在目标类中实际增强的方法
- 织入weave:就是将代理类中需要增强的方法放入到目标类中去执行的过程
- 引介Introduction:引介是一种特殊的 增强,他为类添加一些属性和方法
- 通知advice:将代理对象中的方法应用到目标类 的过程中产生的结果
- 切面aspect:所有的切入点和代理对象的方法组成在一起构成了切面
切点表达式:
5种通知方式:
- 前置通知(beforeAdvice):增强的逻辑在目标方法之前执行
- 后置通知(afterReturningAdvice):增强的逻辑在目标方法之后执行
- 异常通知(throwableAdvice):目标方法出现异常,则增强逻辑运行
- 环绕通知(aroundAdvice):增强的逻辑在目标方法的前后执行
- 最终通知(afterAdvice):增强的逻辑在目标方法的之后执行,不管有没有异常都会执行
2.事务:保证一组操作执行过程要么成功要么失败
- 目的:保持多并发情况下数据一致性
- 目的:如果出现错误的操作可以进行修改
事务的特性(ACID):
- 原子性:一个事务是最小的执行单元,不可分割
- 一致性:事务执行前后,数据要保持 一致性
- 持久性:事务一旦提交或回滚,在数据库中持久的改变
- 隔离性:隔离性用来保证多个用户操作数据库的同一份数据,不会受到相关干扰
并发操作数据库会出现问题:
-
脏读:一个事务读到了另一个事务的还没有提交数据。
-
不可重复读:一个事务中多次读到的数据不一致。一个事务读到了另一个事务修改后的数据。
- **幻读(虚读)😗*一个事务读到了insert的数据。
隔离级别 | 出现的问题 |
---|---|
读未提交(read uncommited) | 都会出现 |
读已提交(read committed) | 不会出现脏读,但会出现不可重复读和幻读(oracle 默认隔离级别) |
可重复读(repeatable read) | 不会出现脏读 不可重复读,但会出现幻读(mysql的默认隔离基本) |
串行化(serializable) | 以上问题都不会出现(不支持并发) |
spring 事务管理 api:
-
PlatformTransactionManager:平台事务管理器 ->DataSourceTransactionManager
常用方法:
commit:提交事务
rollback回滚事务
transactionStatus getTrasaction(TransactionDefinition definition) -
TransactionDefinition :事务定义信息(隔离、传播、超时、只读)
a. ISOLATION_XXX 设置事务的隔离级别b. PROPAGATION_XXX 事务传播行为
c. TIMEOUT_DEFAULT 默认超时时间 -1
d. isReadOnly() 事务是否只读(增删改 false) 查询 true传播行为图解:
package org.springframework.transaction;
import java.io.Flushable;
public interface TransactionStatus extends TransactionExecution, SavepointManager, Flushable {
/**
*事务保存点
*/
default boolean hasSavepoint() {
return false;
}
default void flush() {
}
}
part3 提前编译:AOT
AOT概述
-
概念介绍:
- AOT是 Ahead-of-Time的缩写,意为提前编译
-
对比AOT和JIT(Just-in-time)编译的区别:AOT在程序运行之前将代码编译成本地机器码;JIT是在程序运行时动态将代码编译成机器码;
-
工作原理:
- 描述AOT编译的工作原理,即在程序部署或安装时将代码转换为本地机器码
- 强调AOT编译可以提前优化代码,从而在程序运行时无需再进行实时编译,从而获得更快的启动时间和更稳定的性能
- 提前编译的过程
-
使用场景
-
AOT编译适用的常见,eg: 移动应用程序、嵌入式系统、WebAssembly、服务器应用程序、安全性要求较高的常见 - AOT编译并非适用所有情况,需要根据应用程序的特点和需求选择编译策略
-
-
AOT的优缺点:
-
优点:
a. 更快的启动时间:AOT编译将应用程序在部署前预先编译成本地机器码,因此应用程序启动可以显著降低解释执行的开销,从而减少启动时间b.减少运行时内存使用:由于代码被提前编译,AOT可以消除JIT编译器在运行时生成的中间代码和优化信息,从而降低应用程序的内存占用.
c.更好的安全性:AOT编译可以减少运行时的动态代码生成,从而减少潜在的安全漏洞,因为攻击者无法利用动态生成的代码进行攻击 -
缺点:
a.冷启动时间:AOT编译会将应用程序的代码提前编译成机器码,可导致应用程序的初始启动时间增加,特别大型复杂的应用程序
b.预先编译的局限性:由于AOT编译时需要提前确定代码的结构和执行路径,可能无法充分利用一些运行时信息进行优化,从而降低一些特定场景下的性能
c.多平台支持:AOT编译通常会生成特定平台的本地机器码,可能需要针对不同的目标平台进行不同的编译,增加了构建和发布的复杂性
-
part4 GraalVM
GraalVM介绍 |
GraalVM(格拉尔虚拟机)是一种高性能的通用虚拟机,由Oracle Labs开发。
GraalVM Native Image(简称Native Image):是GraalVM的一个特性,是一种用于将java应用程序编译成本地机器代码的工具,它提供了将java应用程序编译成本地机器代码的能力,从而使得应用程序可以在没有java虚拟机情况下直接运行
AOT和GraalVM的关系 |
GraalVM支持两种编译方式,并且它将AOT编译与JIT编译结合在一起,形成了一种称为 "GraalVM Native image"的功能。GraalVM Native Image允许将整个java应用程序(或特定部分)预先编译成本地机器码,生成一个可执行的本地二进制文件。这样的本地二进制文件可以独立运行,无需java虚拟机的存在,从而显著降低了应用程序的启动时间和内存占用.
Native image的用途和优势: |
- 快速启动和低内存占用:与传统的java应用相比,Native Image编译的应用程序可以在启动时间上有显著优势,由于编译为本地机器代码,不需要启动JVM,因此启动时间明显缩短,而且应用程序的内存占用也会减少。
- 镜像化和容器化:Native Image使得将Java应用程序打包为轻量级容器变得更容易,因为不需要依赖Java虚拟机的存在。这样可以减少容器的体检,加快部署速度,并降低资源消耗。
- 安全性:使用Native Image编译的应用程序可以减少攻击面,因为不需要依赖外部的JVM或JRE,从而减少一些与Java运行时相关的安全风险
- AOT编译:GraalVM Native Image使用AOT编译技术,意味着代码在部署之前就已经被编译成机器代码,而不是在运行时进行即时编译。有助于进一步优化性能,并提前发现潜在的编译错误.
Native Image的限制:
- 启动时间优化:尽管Native Image可以大大减少启动时间,但在构建过程中可能需要额外的时间和资源
- 动态特性限制:由于编译时需要明确的知道应用程序的所有依赖,所以动态特性(eg:反射)的使用会受到限制
- 操作系统和架构限制:由于native image是提前编译的,所以生成的本地可执行文件将与特定的操作系统和架构相关
part5 native image
Native image安装 |
-
下载安装
访问地址: https://www.graalvm.org/
选择版本:
配置环境变量: GRAALVM_HOME
给path增加%GRAALVM_HOME%\bin;
注意:给Path增加%GRAALVM_HOME%bin;一定要放在%JAVA_HOME%bin;之前。不然执行java -version不生效,只能看到jdk配置的信息,看不到GraalVM信息;建议就 把这两个放在path开头
执行 java -version:
执行gu install native-image: 安装native-image
-
安装Visual Studio:
构建环境需要用的c++的环境,因此要安装visual Studio,该软件包含了c++以及其他环境.
step1.下载visual studio
https://visualstudio.microsoft.com/zh-hans/downloads/step2 勾选安装桌面c++库
step3 配置 VC INCLUDE 环境变量:
注意:这里的include 配置有问题,请参考下面的配置
step4 打开x64 Native Tools ,执行cl命令验证环境是否已经配置正确:
- AOT Native Image的构建与使用:
windows kits目录默认在C:\Program Files (x86)\Windows Kits 下
配置include:
C:\Program Files\Microsoft Visual Studio\2022\Community\
C:\Program Files (x86)\Windows Kits\10\Include\10.0.22621.0\ucrt
C:\Program Files (x86)\Windows Kits\10\Include\10.0.22621.0\um
C:\Program Files (x86)\Windows Kits\10\Include\10.0.22621.0\shared
配置LIB:
C:\Program Files (x86)\Windows Kits\10\Lib\10.0.22621.0\um\x64
C:\Program Files (x86)\Windows Kits\10\Lib\10.0.22621.0\ucrt\x64
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.38.33130
给环境变量path增加如下:
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.38.33130\bin\Hostx64\x64
执行 javac BB.java 生成字节码文件
执行 native-image BB 生成镜像文件
执行BB.exe镜像文件
chatGPT easycode :AI编写代码工具