что такое контроллер java
Spring MVC — основные принципы
Фреймворк Spring MVC обеспечивает архитектуру паттерна Model — View — Controller (Модель — Отображение (далее — Вид) — Контроллер) при помощи слабо связанных готовых компонентов. Паттерн MVC разделяет аспекты приложения (логику ввода, бизнес-логику и логику UI), обеспечивая при этом свободную связь между ними.
DispatcherServlet
Вся логика работы Spring MVC построена вокруг DispatcherServlet, который принимает и обрабатывает все HTTP-запросы (из UI) и ответы на них. Рабочий процесс обработки запроса DispatcherServlet’ом проиллюстрирован на следующей диаграмме:
Ниже приведена последовательность событий, соответствующая входящему HTTP-запросу:
Конфигурирование
Вам будет необходимо связать (замапить) запросы, которые Вы хотите обработать при помощи DispatcherServlet, используя мапинг URL в файле web.xml. Ниже приведён пример объявления и мапинга DispatcherServlet’а HelloWeb:
Файл web.xml будет находиться в каталоге WebContent/WEB-INF. После инициализации HelloWeb, фреймворк попытается загрузить контекст приложения из файла с именем [servlet-name]-servlet.xml, находящегося в каталоге WebContent/WEB-INF. В нашем случае, это будет HelloWeb-servlet.xml.
Далее, тэг указывает, какие веб-адреса обрабатываются каким DispatcherServlet’ом. В нашем случае, все HTTP-запросы, заканчивающиеся на «.jsp», будут обработаны HelloWeb.
Если Вы не хотите использовать [servlet-name]-servlet.xml / WebContent/WEB-INF в качестве файла и директории по умолчанию, Вы можете настроить имя файла и директорию, добавив слушатель сервлета (servlet listener) ContextLoaderListener в web.xml, как показано ниже:
Теперь давайте проверим конфигурацию для HelloWeb-servlet.xml, размещённую в каталоге WebContent/WEB-INF:
Ниже приведены важные моменты в HelloWeb-servlet.xml:
Определение Контроллера
DispatcherServlet отправляет запрос контроллерам для выполнения определённых функций. Аннотация @Controllerannotation указывает, что конкретный класс является контроллером. Аннотация @RequestMapping используется для мапинга (связывания) с URL для всего класса или для конкретного метода обработчика.
Аннотация Controller определяет класс как Контроллер Spring MVC. В первом случае, @RequestMapping указывает, что все методы в данном Контроллере относятся к URL-адресу «/hello». Следующая аннотация @RequestMapping(method = RequestMethod.GET) используется для объявления метода printHello() как дефолтного метода для обработки HTTP-запросов GET (в данном Контроллере). Вы можете определить любой другой метод как обработчик всех POST-запросов по данному URL-адресу.
Вы можете написать вышеуказанный Контроллер по-другому, указав дополнительные атрибуты для аннотации @RequestMapping следующим образом:
Атрибут «value» указывает URL, с которым мы связываем данный метод (value = «/hello»), далее указывается, что этот метод будет обрабатывать GET-запросы (method = RequestMethod.GET). Также, нужно отметить важные моменты в отношении приведённого выше контроллера:
Создание Вида (JSP)
Spring MVC поддерживает множество типов Видов для различных технологий отображения страницы. В том числе — JSP, HTML, PDF, Excel, XML, Velocity templates, XSLT, JSON, каналы Atom и RSS, JasperReports и проч. Но чаще всего используются шаблоны JSP, написанные при помощи JSTL.
Давайте напишем простой Вид «hello» в /WEB-INF/hello/hello.jsp:
В данном случае, переменная $ выводит тот самый атрибут, установленный нами в Контроллере. Внутри Вида Вы можете отобразить любое количество атрибутов.
Примеры реализации фреймворка Spring MVC
Основываясь на приведённых выше концепциях, предлагаю выполнить несколько важных уроков, которые в дальнейшем помогут нам создавать приложения Spring Web:
Spring MVC Hello World Example
Пример, разъясняющий написание простейшего приложения Hello World.
Spring MVC Form Handling Example
В этом примере объясняется, как написать приложение Spring Web с помощью форм HTML, отправить данные контроллеру и отобразить обработанный результат.
Spring Static Pages Example
Получаем доступ к статическим страницам вместе с динамическими.
Подробный взгляд на контроллеры в Java EE 8 MVC
В этом посте мы более подробно рассмотрим контроллеры MVC.
Контроллеры MVC
Контролер отвечает за обработку входящих запросов. Он вызывает бизнес-логику, обновляет модель и возвращает представление, которое должно быть отображено. Контроллер MVC — это метод ресурсов JAX-RS, аннотированный @Controller. Если класс аннотируется @Controller, то все методы ресурсов этого класса считаются контроллерами.
В следующем примере показан простой контроллер, который отображает страницу сведений о продукте для данного идентификатора продукта:
Этот Контроллер разрешает идентификатор продукта (передаваемый как параметр запроса идентификатора) в продукт, используя ProductService Полученный продукт добавляется в модель и возвращается путь к представлению. Затем представление отображается с информацией, хранящейся в модели.
Как и в JAX-RS, аннотация @Path используется для определения пути URL. Этот контроллер доступен через URL, который выглядит следующим образом:
В следующем примере показан гибридный класс с одним методом контроллера MVC и одним традиционным методом ресурса JAX-RS:
Методы контроллера работают очень похоже на методы ресурсов JAX-RS. Однако есть два небольших различия:
Классы контроллера MVC и гибридные классы с методами контроллера MVC должны быть компонентами, управляемыми CDI. Как и классы ресурсов JAX-RS, классы контроллеров MVC создаются для каждого запроса. Для каждого запроса создается новый экземпляр класса Controller.
Как и в JAX-RS, поддерживаемый HTTP-глагол определяется аннотациями. Если метод контроллера должен прослушивать HTTP-запросы POST, он должен быть аннотирован @POST вместо @Get.
Типы возврата контроллера
Четыре различных типа возврата поддерживаются в методах контроллера MVC:
Следующий класс определяет четыре метода контроллера, использующих разные типы возврата. Все методы возвращают один и тот же ответ:
Возврат ответа JAX-RS является наиболее гибким способом. Таким образом, построитель ответов JAX-RS можно использовать для изменения кода состояния HTTP, заголовков ответов и многого другого.
Если void используется в качестве возвращаемого типа, представление должно быть определено с помощью аннотации @View. @View можно применять к методам (как в предыдущем примере) и классам. Если класс аннотирован @View, представление применяется ко всем методам контроллера в этом классе. Аннотацию @View уровня класса можно переопределить с помощью более конкретного определения представления на уровне метода, как показано в следующем примере:
Резюме
Аннотация @Controller может использоваться для методов и классов. При использовании в классах все методы класса рассматриваются как контроллеры. Методы контроллера вызывают бизнес-логику и определяют представление, которое должно быть отображено. Классы с методами Controller являются управляемыми компонентами CDI. Для каждого запроса будет создан новый экземпляр класса. Традиционные методы ресурсов JAX-RS можно комбинировать с методами контроллера MVC в одном классе.
В следующих статьях о Java EE 8 MVC мы рассмотрим привязку и проверку параметров.
JavaFX: класс Controller и fx:id в FXML
Мне кажется, не оправданно много полезных статей не только не посвящают и двух слов самому животрепещущему вопросу при начале работы с JavaFX, но и посвятив, все равно не раскрывают его полностью. А вопрос возникает следующий: как наладить связь ваших node по их fx:id и вашего кода. Как использовать их в разных частях кода, чтобы ссылаться на ваш программный интерфейс? Вот на это, я и постараюсь ответить под катом
Что такое fx:id и с чем его едят
По какой-то причине, для авторов статей и комментаторов на StackOverFlow не очевидно, что попросту не понятно для читателя, как происходит связь fx:id ссылок с вашим кодом. Потому что это не очевидно. А некоторые моменты, вообще больше похожи на магию, этот момент я отдельно упомяну ниже.
Вообще, многие статьи полезны, информативны (особенно на английском), так же, есть ответы почти на все на StackOverFlow. Но, практически нигде развернуто не сказано, как именно работать с fx:id, которые мы определяем в FXML файле. Лишь краткие упоминания, которые приводят к еще большей путанице в голове. Обычно пишут «задайте вашей node нужный вам fx:id и будем вам счастье».
К сожалению, счастья не будет. Будет NullPointerException. По причине некорректного использования, которое идет из непонимания области видимости, скажем так, этих id. И я хочу рассказать, по какой причине получается exception, а главное, как корректно связать ваш FXML с классом-контроллером и java-кодом. Надеюсь, это поможет людям избежать тех мучений, которые прошел я в поисках ответа на данный вопрос.
Я бы наверно и пришел к ответу сам, рано или поздно, но спустя многие часы дни поиска, я наткнулся на статью. В ней рассказано про MVC модель, которую и следует использовать при разработке на JavaFX. Не стану повторять написанное в статье, там достаточное описание этой модели. Если вы о ней не слышали, то к прочтению обязательно, поскольку можно обойтись работая с javaFX вообще без FXML, на чистом java коде, но не стоит. Так же, там есть некоторое пояснение, как заставить ожить fx:id, но я все же хочу дополнить эту статью своими наблюдениями и знаниями в оригинальной статье.
Модель эта, кстати, не так очевидна, даже если смотреть статьи на Oracle. По крайней мере, я не нашел у Oracle, как мне работать с fx:id. Зато очень много экспериментировал, что и привело к пониманию, как с ними работать.
Итак, с предисловием пора заканчивать, к делу.
Что конкретно не очевидно из примеров в статьях? Примеры в интернетах, предлагают в FXML оформить дизайн приложения, после создать к нему класс-контроллер, который наследуется от Application, в нем определить метод start() (или сделать это в классе Main, разные есть примеры) и там пользоваться вашими fx:id. И это работает. Казалось бы, чудненько, все так просто, что даже хочется сплясать.
Вот даже банальный пример, естественно «hello world»:
Это полностью рабочий пример. В данном примере, в FXML определяется Pane, в ней одна кнопка Button, у которой fx:id=»fxButton», при нажатии на нее, вызывается метод в классе-контроллере onAction=»#click». В методе click есть вывод на консоль и назначения текста самой кнопке.
Если его запустить и ткнуть в кнопку, то в консоли получим результат, у кнопки изменится ее название. Лихо? А вот и нет. Это не вносит ясности, как с этим работать. И такими примерами пестрит интернет. А если не делать MVC модель или каким-либо иным способом не разделять и властвовать, получится каша, на которой подскользнешься, ну и как говорят во всех американских фильмах, «упадешь и сломаешь бедро». Причем, бедренная кость — одна из самых крепких…
Следующим, логичным казалось бы шагом, было бы взять в этом же коде выше, убрать из него метод click и поработать напрямую, из кода. Например из метода start(), чтобы далеко не ходить, добавить в него обработку клика мышки на кнопку fxButton (панель/иную часть интерфейса, не имеет значения, это простой пример fx:id):
Вас обматерит ваша IDE еще при запуске
Exception in Application start method
java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:389)
at com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:328)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:767)
Caused by: java.lang.RuntimeException: Exception in Application start method
at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:917)
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication$154(LauncherImpl.java:182)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.NullPointerException
at sample.Main.start(Main.java:25)
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$161(LauncherImpl.java:863)
at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$174(PlatformImpl.java:326)
at com.sun.javafx.application.PlatformImpl.lambda$null$172(PlatformImpl.java:295)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.application.PlatformImpl.lambda$runLater$173(PlatformImpl.java:294)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.lambda$null$147(WinApplication.java:177)
… 1 more
Exception running application sample.Main
Process finished with exit code 1
В первый миг, захочется материться в ответ, ведь не ясно, почему не инициализирована переменная. Ведь это контроллер, он знает про FXML файл, в него с успехом обращаются элементы интерфейса и обрабатываются, ведь в коде с методом click мы так и делали, обращались по fx:id.
Но это работает только в методах, в которые они жестко закодированы в FXML. А при прямом обращении из кода java в файл FXML, а не наоборот, получается, что объявленная в классе переменная Button fxButton, к которой мы в методе успешно обратились — не инициализирована. Вот казалось бы, незадача. И волки вроде сыты, да и овцы целы, но вот пастух слегка лукавит.
Ваша IDE, например, IDEA, вполне успешно генерирует пример выше, модель в нем верная(генерируются FXML и классы Controller, Main и немного кода в нем). Этого достаточно для начала работы. Я немного дописал кода, для наглядности, но сделал это в Main классе, вместо контроллера, для экономии места.
Итак, выходит что для взаимодействия с вашими node в в FXML файле, вам требуется создать метод, в котором действие будет обработано. Но обратите внимание, именно действие по элементу интерфейса. Внутри метода, можно так же использовать fx:id.
Но, стоит задуматься о использовании кнопки из примера выше в других частях кода (точнее выразиться, использовании fx:id этих элементов), даже в этом же классе (а это — контроллер), вас ждет NullPointerException и отсутствие понимания, собственно, WHY? А вдруг я хочу в коде ссылаться на какую-либо панель, сделав ей отдельный fx:id, менять текст или внешний вид форм, при этом не взаимодействуя физически с кнопками и прочими элементами, делая это из кода, согласно какой-то логике? Какое верное решение?
Это настолько не очевидно, учитывая отсутствие информации по данному вопросу, что я ушел в магазин за пивом.
Вернувшись и проковыряв дыру в интернете и в голове, перепробовав разные варианты, я докопался до истины. Местами в интернете, видел предложения провести инициализацию. Собственно, это и оказалось решением, но нигде опять же, нет примеров. По такому случаю, сейчас примеры с разъяснениями будут у меня. Как должна выглядеть программа на javaFX, прилагаю код.
FXML. Внешний вид. Необходимо определить в нем контроллер
Main. Единственная задача, запустить приложение, загрузить сцену из FXML
Controller. Адаптер FXML и java кода.
View. Отвечает за то, что отображает приложение.
В контроллере и View я показал, как можно взаимодействовать разными способами с fx:id.
Прямо закодировать в FXML вызов метода в контроллере.
Инициализировать используемые id и иже с ними node и передавать в необходимое место в коде, вплоть до присваивания локальным переменным этих node по их fx:id.
Разберу работу кода, предложенного выше. Запускается вот такого вида окно:
В FXML файле определен текст labelFx, но в initialize() он при запуске, получает значение, которое определено в методе.
При нажатии на кнопку:
Происходит вывод на консоль «Hello World», у кнопки меняется текст на «Hey!», управление переходит в метод labelLocalInitialize(), в нем локально определенной переменной назначается объект labelFx. Следом, labelFx назначается новый текст.
Резюмируя:
Где-то выше я говорил, что есть магия в javaFX. Она начинается в классе View. Вроде, очевидно бы было, если бы от контроллера можно было наследовать классы, для работы с id, но происходит обратное… Почему так сделано, я в подробности не вдавался, хотя и любопытно, почему так. Если в комментариях знающие люди приоткроют завесу тайны, буду весьма благодарен.