最近更新

本章节包含 Jmix 框架和 Studio 1.4 的新功能介绍,以及在升级框架版本时需要注意的一些破坏性改动。

如何升级

如需新建 Jmix 1.4 项目或者升级已有项目,需要使用 Studio 1.4 以上版本。因此,请先 升级 Jmix Studio 插件。

IntelliJ IDEA 的最低版本要求是 2022.1。

参阅 升级项目 部分的介绍了解如何使用 Studio 升级项目。自动升级迁移过程会对项目做如下修改:

  • 升级 Jmix BOM 的版本,BOM 又定义了所有依赖的版本。

  • 升级 Jmix Gradle 插件的版本。

  • 升级 Gradle wrapper 的版本至 7.5.1,修改 gradle/wrapper/gradle-wrapper.properties 文件。

新功能和改进

Flow UI

基于 Vaadin 23 的新 Flow UI 模块现在可以用来做为新建项目的坚实基础。其 API 已经足够稳定,并在后续的开发计划中按照标准规则进一步升级:提供向后兼容的补丁、主发布版本中可能引入小的不兼容等。

如需在新项目使用 Flow UI,请选择 Full-Stack Application with Incubating FlowUI 项目模板。

我们认为 Flow UI 模块现在仍处于孵化阶段,因为其功能与经典 UI 还有一定差距。

Flow UI 项目已经有少数可以使用的扩展组件:数据工具、审计,以及那些不带 UI 的扩展组件,例如 REST 或文件存储的实现。

Studio 为展示 Flow UI 提供了新的可视化设计器。与经典 UI 的设计器有很大区别,不再有 Palette 工具窗口。而是通过顶部的操作面板的 Add ComponentComponent Hierarchy 的右键菜单以及 Generate 菜单(Alt+Ins / Cmd+N)提供。

如果设计器的预览窗口显示不出来,点击顶部面板的 Restart 按钮。Console 按钮可以打开或关闭前端构建的输出控制台,可用于诊断问题。

flowui designer 1

Jmix 工具窗口

Jmix 工具窗口的 Configuration 部分现在能展示所有带 @Configuration 注解及其派生注解(例如,@SpringBootApplication)的类、@ConfigurationProperties 类,还有 REST 查询语句服务 的配置文件:

config section

在实体的 Beans 部分展示其关联的 Spring beans,只要这些 beans 中的方法参数或者返回值是相应实体即可。

data model beans
可以按包对展示的内容进行分组,选择 Show Options Menugear) → Group by Packages

构造函数注入

Studio 现在支持以在 Spring beans 中使用构造函数注入的方式。在 Choose Objects to Inject 对话框中,选中 Use constructor injection 复选框:

constructor injection 1

Studio 会创建一个 final 字段以及构造函数参数:

@Component
public class CustomerService {

    private final DataManager dataManager;

    public CustomerService(DataManager dataManager) {
        this.dataManager = dataManager;
    }
}

Studio 会记住你的选择,可以在 Jmix 插件的配置中修改。

行级角色向导

现在可以通过 Jmix 工具窗口点击 NewRow-level Role 使用向导创建 行级角色 和策略。详细内容请参考 行级角色向导 部分。

自定义项目模板

Studio 现在支持包含项目模板的自定义制件,因此,可以提供自定义的模板用于新建项目、UI 界面以及 Flow UI 视图。

详细内容请参考 自定义项目模板 部分。

安全配置扩展点

现在可以对框架和扩展组件提供的安全配置进行扩展,而不像之前,只能完全替换。

如需调整安全配置,定义一个继承 AbstractHttpConfigurer 的 Spring bean,使用合适的 @Qualifier 注解。

扩展 StandardSecurityConfiguration 的示例:

@Component
@Qualifier(StandardSecurityConfiguration.SECURITY_CONFIGURER_QUALIFIER)
public class MySecurityConfigurer extends AbstractHttpConfigurer<MySecurityConfigurer, HttpSecurity> {

    @Override
    public void configure(HttpSecurity http) throws Exception {
        MyFilter myFilter = new MyFilter();
        http.addFilterBefore(myFilter, UsernamePasswordAuthenticationFilter.class);
    }
}

扩展 OIDC 组件中安全配置的示例:

@Component
@Qualifier(OidcAutoConfiguration.OAuth2LoginSecurityConfiguration.SECURITY_CONFIGURER_QUALIFIER)
public class MyOidcSecurityConfigurer extends AbstractHttpConfigurer<MyOidcSecurityConfigurer, HttpSecurity> {
    @Override
    public void init(HttpSecurity http) throws Exception {
	// any method that adds another configurer must be invoked in the init method
        http.headers(headers -> {
            headers.frameOptions().deny();
        });
    }
}

自定义密码验证

如需实现应用程序中的自定义密码验证逻辑,可以创建一个 bean(或多个 bean)实现 PasswordValidator 接口,示例:

@Component
public class MyPasswordValidator implements PasswordValidator<User> {

    @Override
    public void validate(PasswordValidationContext<User> context) throws PasswordValidationException {
         if (context.getPassword().length() < 3)
            throw new PasswordValidationException("Password is too short, must be >= 3 characters");
    }
}

所有的密码验证器都会自动用在 ChangePassword 操作对话框中。

如需在用户编辑或详情界面添加验证器,使用 PasswordValidation 助手类:

@Autowired
private PasswordValidation passwordValidation;

@Subscribe
protected void onBeforeCommit(BeforeCommitChangesEvent event) {
  if (entityStates.isNew(getEditedEntity())) {
      // ...
      List<String> validationErrors = passwordValidation.validate(getEditedEntity(), passwordField.getValue());
      if (!validationErrors.isEmpty()) {
          notifications.create(Notifications.NotificationType.WARNING)
                  .withCaption(String.join("\n", validationErrors))
                  .show();
          event.preventCommit();
      }
      getEditedEntity().setPassword(passwordEncoder.encode(passwordField.getValue()));
  }
}

DataManager 使用悲观锁

DataManager 流式加载接口现在可以在 lockMode() 方法使用 javax.persistence.LockModeType 枚举值。当处理 JPA 实体时,会在数据库级别使用 select …​ for update 语句形成相应的悲观锁。

示例:

Customer customer = dataManager.load(Customer.class)
        .id(customerId)
        .lockMode(LockModeType.PESSIMISTIC_WRITE)
        .one();

功能预览

认证服务

Jmix 认证服务扩展组件提供分发 access 和 refresh token,并使用这些 token 保护 API 资源(REST API,自定义控制器)的功能。支持为客户端和移动端授予认证码,以及为服务端的端到端交互授予秘钥。

该扩展组件基于 Spring Authorization Server 构建。Jmix 认证服务是 Jmix 安全机制 OAuth2 模块的升级版,OAuth2 模块依赖的 Spring Security OAuth 项目已经过时。

查看项目的 README 文档 了解更多内容。

破坏性改动

迁移至 SecurityFilterChain

框架的安全配置部分已经从废弃的 WebSecurityConfigurerAdapter 迁移至 SecurityFilterChain

如果项目有扩展 WebSecurityConfigurerAdapter 的安全配置,请按照 Spring 博客的 这篇文章 的建议重写。

CurrentAuthentication 中重加载用户

仅适用于 Jmix 1.4.0。这个改动在 1.4.1 进行了回退,通过另外的方式修复了最初的问题。

为了修复 这个问题 并避免可能出现的不一致性,CurrentAuthentication.getUser()CurrentUserSubstitution.getEffectiveUser() 方法会在每次调用时从数据库重新加载用户实体。

如果新的加载行为引起了其他的问题,可以设置 jmix.core.current-authentication-user-reload-enabledfalse 关闭。

使用 Java 8 的 CUBA 项目

仅适用于 Studio 1.4.0。在 Studio 1.4.1 进行了修复。

Jmix Studio 1.4 会导致使用 Java 8 的 CUBA 项目导入 IDE 失败。修复方法:打开 CUBA 项目之后,打开 Settings/PreferencesBuild, Execution, DeploymentBuild ToolsGradle,设置 Gradle JVM 为 JDK 11 即可。

编译 Widgetset

仅适用于 Jmix 1.4.0。在 1.4.1 进行了修复。

如果你的项目位于一个包含空格的路径下,Jmix 1.4.0 的 widgetset 编译有可能会失败。目前的解决方案只能是将项目移至一个不包含空格的路径。已经在 1.4.1 修复,参考 #1162

变更日志