我有一个动态模型,包括无线电,文本,选择,选择多个的X编号。这基本上就像是后端的EAV数据库。
我需要使用N个字段来呈现此动态表单,然后验证提交的动态模型对象,然后使用字段定义的正则表达式对其进行验证。我希望通过JSR 303注释来执行此验证。
所以,我想通过ModelAttribute
@Valid
功能等方式使用典型的Spring MVC开发方式绑定表单对象。唯一的区别是模型对象未知/未定义,直到运行系统。
我倾向于使用CGLIB或类似的东西在运行时生成类,并使用特殊的taglib呈现它,然后使用反射以某种方式使用特殊验证来验证它。
这样的事情完全脱离了可能性吗?同样,我想做常规的Spring MVC控制器和模型,但是使用动态表单对象。
答案 0 :(得分:0)
编写控制器类(RegisterController.java)的代码,如下所示:
File: src/main/java/net/codejava/spring/controller/RegisterController.java
package net.codejava.spring.controller;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import net.codejava.spring.model.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
@RequestMapping(value = "/register")
public class RegisterController {
@RequestMapping(method = RequestMethod.GET)
public String viewRegistration(Map<String, Object> model) {
User userForm = new User();
model.put("userForm", userForm);
List<String> professionList = new ArrayList<>();
professionList.add("Developer");
professionList.add("Designer");
professionList.add("IT Manager");
model.put("professionList", professionList);
return "Registration";
}
@RequestMapping(method = RequestMethod.POST)
public String processRegistration(@ModelAttribute("userForm") User user,
Map<String, Object> model) {
// implement your own registration logic here...
// for testing purpose:
System.out.println("username: " + user.getUsername());
System.out.println("password: " + user.getPassword());
System.out.println("email: " + user.getEmail());
System.out.println("birth date: " + user.getBirthDate());
System.out.println("profession: " + user.getProfession());
return "RegistrationSuccess";
}
}
我们可以看到此控制器旨在处理请求URL /寄存器:
@RequestMapping(value = "/register")
我们分别实现了两个方法viewRegistration()和processRegistration()来处理GET和POST请求。 Spring中的编写处理程序方法非常灵活,因为我们可以自由选择自己的方法名称和必要的参数。让我们详细看一下上面控制器类的每个方法: viewRegistration():在这个方法中,我们创建一个模型对象,并使用键“userForm”将其放入模型映射中:
User userForm = new User();
model.put("userForm", userForm);
这将在指定对象与此方法返回的视图中的表单(即注册表单)之间创建绑定。请注意,键“userForm”必须与标记的commandName属性的值匹配。
另一个有趣的一点是,我们创建一个字符串列表并将其放入模型映射中,并使用“professionList”键:
List<String> professionList = new ArrayList<>();
professionList.add("Developer");
professionList.add("Designer");
professionList.add("IT Manager");
model.put("professionList", professionList);
此集合将由Registration.jsp页面中的标记使用,以便动态呈现行业下拉列表。 最后,此方法返回一个视图名称(“Registration”),该名称将映射到上面的注册表单页面。 processRegistration():此方法处理表单提交(通过POST请求)。这里的重要参数是:
@ModelAttribute("userForm") User user
这将使模型映射中存储在键“userForm”下的模型对象可用于方法体。同样,键“userForm”必须与标记的commandName属性的值匹配。 提交表单时,Spring会自动将表单的字段值绑定到模型中的后备对象,因此我们可以通过此后备对象访问用户输入的表单值,如下所示:
System.out.println("username: " + user.getUsername());
出于演示目的,此方法仅打印出User对象的详细信息,最后返回成功页面的视图名称(“RegistrationSuccess”)。
答案 1 :(得分:0)
这是可能的,但不适用于cglib。 Cglib是一个代理库,只允许您覆盖现有方法,但不能定义新的,以便将动态模型传递给Spring或任何其他bean验证库。
当然可以使用Javassist或Byte Buddy(披露:我是作者)生成这样的类,它允许您定义这样的元数据。例如,使用Byte Buddy,您可以定义动态类:
new ByteBuddy()
.subclass(Object.class)
.defineField("foo", String.class, Visibility.PUBLIC)
.annotateField(AnnotationDescription.Builder.ofType(NotNull.class).build())
.make();
使用此代码,您可以加载该类,为"foo"
字段分配一个值,并将此bean对象提供给bean验证库。
与此同时,这种方法对我来说过于设计。如果您需要提供与您无法重新实现的特定JSR 303验证器的兼容性,我只会使用此解决方案。否则,您可能更愿意直接验证数据。
如果您的框架还需要getter和setter,您可以通过以下方式添加它们:
builder = builder
.defineMethod("getFoo", String.class, Visibility.PUBLIC)
.intercept(FieldAccessor.ofField("foo"));
.defineMethod("setFoo", void.class, Visibility.PUBLIC)
.withParameters(String.class)
.intercept(FieldAccessor.ofField("foo"));