2016-11-11 10 views
2

У меня есть динамическая модель, состоящая из X номера каждого из радио, текста, select, select-multi. В основном это база данных EAV в бэкэнд.Динамическая форма с объектом связывания MVC с объективом CGLIB

Мне нужно представить эту динамическую форму с помощью N суммарного количества полей, а затем проверить представленный объект динамической модели, который затем будет проверяться с использованием заданных пользователем регулярных выражений. Я бы хотел, чтобы эта проверка выполнялась через аннотации JSR 303.

Итак, я хотел бы привязать объект формы, используя типичный способ создания Spring MVC, с помощью функций ModelAttribute@Valid и т. Д. Единственное отличие заключается в том, что объект модели неизвестен/не определен до Runtime.

Моей наклонностью является использование CGLIB или что-то подобное, чтобы сгенерировать класс во время выполнения и представить его специальным taglib, а затем проверить его с помощью специальной проверки как-то используя отражение.

Это что-то вроде этого полностью вне сферы возможностей? Опять же, я хотел бы делать обычные Spring MVC-контроллеры и модели, но с объектом динамической формы.

ответ

0

Это возможно, но не с cglib. Cglib - это прокси-библиотека и позволяет вам переопределять существующие методы, но не определять новые, которые вам потребуются, чтобы передавать динамическую модель Spring или любую другую библиотеку проверки компонентов.

Конечно, можно создавать такие классы с использованием 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" поле значения и представить этот объект боба в библиотеку проверки боба.

В то же время этот подход чувствует себя слишком переработанным для меня. Я бы подошел к этому решению только в том случае, если вам требуется обеспечить совместимость с определенными валидаторами JSR 303, которые невозможно переопределить. В противном случае вы можете скорее проверить данные напрямую.

Если система также требует добытчика и сеттеры, вы можете добавить их с помощью:

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")); 
+0

Пробовал это. Я получаю 'java.lang.IllegalStateException: не удается получить доступ к объекту аннотации public abstract java.lang.Class [] javax.validation.constraints.NotNull.groups() \t net.bytebuddy.description.annotation.AnnotationDescription $ ForLoadedAnnotation.getValue (AnnotationDescription.java:673) ' –

+0

Неплохо, я не думал, что свойство« NotNull »имеет необязательные атрибуты, где трюк с лямбдой не работает. Это действительно анти-шаблон, я был на моем телефоне и ленился. Официальный способ определения аннотаций теперь включен в мой обновленный ответ выше, где вы также можете установить необязательные атрибуты. –

+0

Похоже, что ошибка исчезла. Только сейчас проблема заключается в том, что значение не задано. Появляется Spring MVC, требующий геттер/сеттер. Не понимаю, как добавить getter/setter с ByteBuddy. Есть идеи по этому поводу? –

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, соответственно. Методы написания обработчиков весной очень гибкие, так как мы можем свободно выбирать собственные имена методов и необходимые параметры. Давайте рассмотрим каждый метод указанного класса контроллера в деталях: 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 для того, чтобы динамический список выпадающих профессий. Наконец, этот метод возвращает имя вида («Регистрация»), которое будет отображаться на странице регистрационной формы выше. processRegistration(): этот метод обрабатывает отправку формы (через запрос POST). Важным параметром здесь является:

@ModelAttribute("userForm") User user 

Это сделает объект модели, который хранится под ключ «UserForm» в карте модели, доступной для тела метода. Опять же, ключевой «userForm» должен соответствовать значению атрибута commandName тега. Когда форма отправлена, Spring автоматически связывает значения полей формы на объекте подложки в модели, таким образом, мы можем получить доступ к значениям формы, введенные пользователем с помощью этого опорного объекта, как это:

System.out.println("username: " + user.getUsername()); 

Для демонстрационной цели , этот метод только распечатывает детали объекта User и, наконец, возвращает имя представления страницы успеха («RegistrationSuccess»).

+0

Это не ответ на мой вопрос вообще. У меня динамическая модель. Пожалуйста, перечитайте мой вопрос - Downvote. –