Java, как хранить объект различного типа
В настоящее время я использую Spring MVC и Hibernate для разработки моего веб-приложения. Все еще изучает внутреннюю работу Java.
Я нахожу себя в ситуации, когда мне нужно хранить данные в поле, которое может принимать разные типы объектов вместе с некоторыми другими строками данных. Точнее, я хочу создать объект ReturnObject, который может содержать сообщения, код ошибки... и т.д. Таким образом, мое возвращение JSON может оставаться постоянным через api.
Вот как я создал свой ReturnObject
public class ReturnResponse {
//Set fields
private Object returnObj; <----- Need this to accept different types
private HttpStatus httpStatus;
private String message;
private String developerMessage;
// Start build
public ReturnResponse(){
this.returnObj = returnObj;
this.httpStatus =.....etc.
}
// Setters... getters...
}
private Object returnObj;
, чтобы это поле могло принимать Collection, Maps, Class..etc, но безопасно ли это?
Кажется, что я помню, что всегда хорошая практика объявлять конкретный тип объекта в поле для обеспечения безопасного типа.
Вопросы
-
Видите ли вы прогнозируемую проблему в будущем, есть ли лучший способ сделать это?
-
Если это не безопасный тип, как бы я сделал его более безопасным.
Ответы
Ответ 1
Вы можете использовать общий:
public class ReturnResponse<ObjectType> {
private ObjectType returnObj;
private HttpStatus httpStatus;
private String message;
private String developerMessage;
public ReturnResponse(ObjectType returnObj, /*other parameters...*/) {
this.returnObj = returnObj;
// set other parameters
}
public ObjectType getReturnObj() {
return this.returnObj;
}
// ...
}
Он будет работать, если вы знаете, когда вы инициируете свой ReturnResponse
тип возвращаемого объекта.
Я использую этот шаблон в большинстве своих API без проблем.
Ответ 2
Если вы хотите "хранить данные в поле, которое может принимать разные типы объектов вместе с некоторыми другими строками данных". то вам нужен базовый класс для этого объекта, который в вашем случае, вероятно, будет Object.
Проблема в том, что вам нужно каким-то образом расшифровать код в своем коде, какой тип объекта - который в большинстве случаев, я думаю, будет нежелательным и потребует небезопасного кастинга.
Единственный способ, который я могу придумать, чтобы сделать его более безопасным, - сделать какую-то оболочку вроде:
public class Bean {
private String string;
private Integer integer;
private List<String> list;
private Bicycle bicycle;
//setters
//...
public Optional<Bicycle> getBicycle() {
return Optional.ofNullable(bicycle);
}
//... and so on...
}
Ответ 3
Обработчик ошибок должен находиться в контроллере, и он должен ответить на ошибку http. Это означает правильный HTTP-статус ошибки и желаемое сообщение об ошибке. Ошибка не должна выглядеть как успешный запрос (нет кода состояния 200). Это ошибка. В своем интерфейсе вы должны соответствующим образом обрабатывать ответ об ошибке http.
С spring это может быть очень легко сделать. Вот пример обработчика ошибок моего проекта. Это собственный класс с аннотацией @ControllerAdvice
. spring будет автоматически использовать это.
Этот класс автоматически поймает любое необработанное исключение, которое определено с помощью @ExceptionHandler
, и отправьте в моем случае a ShortExceptionResponse
, который содержит тип и сообщение об исключении, с правильным статусом ошибки Http, определенным с помощью @ResponseStatus
.
В своем интерфейсе вы можете реагировать на различные коды ошибок HTTP-статуса. Это приятный и общий.
@ControllerAdvice
public class RestExceptionResponseHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(SetRestController.class);
@ExceptionHandler(NoSuchElementException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public @ResponseBody
ShortExceptionResponse noSuchElementExceptionHandler(Exception ex) {
LOGGER.error("An error occured processing a rest request", ex);
return new ShortExceptionResponse(ex);
}
@ExceptionHandler(value = {EntityAlreadyExistsException.class})
@ResponseStatus(HttpStatus.FORBIDDEN)
public @ResponseBody
ShortExceptionResponse entityAlreadyExistsExceptionHandler(EntityAlreadyExistsException ex) {
LOGGER.debug("A rest request could not been process due an illegal state of the target entity", ex);
return new ShortExceptionResponse(ex);
}
@ExceptionHandler(value = {IllegalArgumentException.class, UnsupportedOperationException.class})
@ResponseStatus(HttpStatus.BAD_REQUEST)
public @ResponseBody
ShortExceptionResponse illegalArgumentExceptionHandler(Exception ex) {
LOGGER.error("An error occured processing a rest request", ex);
return new ShortExceptionResponse(ex);
}
@ExceptionHandler(value = {HttpMessageNotReadableException.class})
@ResponseStatus(HttpStatus.BAD_REQUEST)
public @ResponseBody
ShortExceptionResponse httpMessageNotReadableExceptionHandler(Exception ex) {
LOGGER.error("An error occured processing a rest request", ex);
if (ex.getCause() instanceof InvalidFormatException) {
return new ShortExceptionResponse(new InvalidValueException(((InvalidFormatException) ex.getCause()).getOriginalMessage()));
}
return new ShortExceptionResponse(ex);
}
...
...
}
В реальном контроллере вы просто продолжаете бросать исключение, и он будет обрабатываться обработчиком ошибок
@RequestMapping(method = RequestMethod.POST)
public @ResponseBody
MetadataDTO createMetadata(@RequestBody MetadataDTO metaDataDTO) throws EntityAlreadyExistsException {
MetadataDTO result = metaDataService.createMetadata(metaDataDTO.getName(), metaDataDTO.getDescription(), metaDataDTO.getType());
return result;
}
Ответ 4
Вы можете создать класс 'model' для хранения полного объекта, который будет преобразован в json:
@AllArgsConstructor //or make a constructor with all the fields manually
class ResponseObject {
User user;
House house;
Car car;
}
Поскольку вы используете Spring, у вас уже есть библиотека Джексона. Так что вы можете сделать:
@Autowired ObjectMapper objectMapper; // no need to configure anything to use this
public String getJson(){
User user = new User("user", "password");
House house = new House(4, true, ...);
Car car = new Car();
ResponseObject resp = new ResponseObject(user, house, car);
String json = null;
json = objectMapper.convertValue(resp, ResponseObject.class);
// or:
try {
json = objectMapper.writeValueAsString(resp);
} catch (Exception e) {
...
}
// or: (would need to use a google Gson dependency)
Gson gson = new Gson();
json = gson.toJson(resp, ResponseObject.class);
return json;
}
В качестве альтернативы, если вам действительно нужна гибкость,
@Autowired ObjectMapper mapper;
public String getJson() {
Map<String, Object> jsonMap = new HashMap<>();
jsonMap.put("number", 5);
jsonMap.put("String", "string");
jsonMap.put("kanky", 987L);
String json = mapper.writeValueAsString(jsonMap);
System.out.println("json: " + json);
} // works fine if your map values have a toString defined