action java что это
Действия actions Struts2
Фреймворк Struts2 ориентирован на действия (action), которые являются его основным элементом, т.е. ядром фреймворка. Действия обрабатывают запросы пользователей и определяют результат его выполнения. Struts2 назначает каждому action свой уникальный URL и набор результатов. Фреймворк включает пять констант, которые определяют результат выполнения действия и которые можно использовать в приложении :
Если этих констант будет недостаточно, то можно дополнительно определить любые свои.
Действие в Struts2 представляет простой java-класс (action-класс), который должен наследовать свойства класса com.opensymphony.xwork2.ActionSupport. Класс ActionSupport содержит виртуальную функцию execute(), возвращающую строковое значение. Именно в этой функции определяется результат выполнения действия, или результат обработки запроса HttpServletRequest.
Примечание : В последних версиях Struts2 действие в виде java-класса может не наследовать свойства класса ActionSupport. Но этот класс (действие) должен содержать метод execute() и должен быть описан в файле конфигурации struts.xml.
Действия в Struts2 выполняют функцию обработки и передачи данных для дальнейшего их представления в JSP-странице или других вариантах отображения страницы пользователю. Кроме того, action указывает фреймворку какой результат необходимо отобразить пользователю.
Struts2 приложение авторизации
На следующем скриншоте представлена структура проекта StrutsAction.
Дескриптор приложения web.xml не описывается на странице. Листинг web.xml можно увидеть на странице Пример Struts2, где рассматривается вопрос кодировки передаваемых серверу сообщений.
Листинг action классa LoginAction.java
Действие LoginAction переопределяет функцию execute. Как только будет обращение к этому действию Struts2 сразу же вызовет его метод execute, который проверит поля «userName» и «password», и, в зависимости от состояния, вернет либо «success», либо «error».
На странице проверки данных показано, как использовать метод validate() для проверки введенных данных пользователя.
Листинг JSP-страницы index.jsp
На этой странице оператор должен ввести «Логин», «Пароль» и нажать кнопку Submit. По нажатию на кнопку серверу передается информация страницы (userName, password) и вызывается действие «login».
В строке подключаются теги Struts2, после чего обращение к объектам фреймворка производится через префикс «s». В теле страницы определено несколько тегов Struts2. В первом теге формы определяется действие action=»login», отвечающее за обработку данных. Далее в теле формы установлены текстовые поля «userName» и «password». При нажатии на кнопку управление передается серверу и вызывается действие login.
Интерфейс страницы представлен на следующем скриншоте.
Файл конфигурации struts.xml
В struts.xml необходимо определить связь страницы index.jsp с действием «login».
В файле конфигурации определено, что действие «login» связано с классом example.LoginAction. В зависимости от результата (result) выполнения действия будет открыта либо страница success.jsp, либо error.jsp.
Листинг страницы success.jsp
В случае успешного выполнения действия «login» сервер откроет страницу success.jsp, интерфейс которой представлен на следующем скриншоте.
Листинг страницы error.jsp
Скачать примеры
Исходные коды рассмотренного примера можно скачать здесь (5.35 Мб).
Новое в Java 8
Методы интерфейсов по умолчанию
Лямбда-выражения
Давайте начнем с простого примера: сортировка массива строк в предыдущих версиях языка.
Статический метод Collections.sort принимает список и компаратор, который используется для сортировки списка. Наверняка вам часто приходилось создавать анонимные компараторы для того чтобы передать их в метод.
Java 8 предоставляет гораздо более короткий синтаксис — лямбда-выражения, чтобы вам не приходилось тратить время на создание анонимных объектов:
Как видите, код гораздо короче и куда более читаем. И его можно сделать еще короче:
Компилятору известны типы параметров, поэтому их можно тоже опустить. Давайте посмотрим, как еще могут использовать лямбда-выражения.
Функциональные интерфейсы
Как лямбда-выражения соответствуют системе типов языка Java? Каждой лямбде соответствует тип, представленный интерфейсом. Так называемый функциональный интерфейс должен содержать ровно один абстрактный метод. Каждое лямбда-выражение этого типа будет сопоставлено объявленному методу. Также, поскольку методы по умолчанию не являются абстрактными, вы можете добавлять в функциональный интерфейс сколько угодно таких методов.
Ссылки на методы и конструкторы
Предыдущий пример можно упростить, если использовать статические ссылки на методы:
Давайте посмотрим, как передавать ссылки на конструкторы. Сперва определим бин с несколькими конструкторами:
Затем определим интерфейс фабрики, которая будет использоваться для создания новых персон:
Теперь вместо реализации интерфейса мы соединяем все вместе при помощи ссылки на конструктор:
Области действия лямбд
Однако переменная num должна все равно оставаться неизменяемой. Следующий код не скомпилируется:
Запись в переменную num в пределах лямбда-выражения также запрещена.
Доступ к полям и статическим переменным
В отличии от локальных переменных, мы можем записывать значения в экземплярные поля класса и статические переменные внутри лямбда-выражений. Это поведение хорошо знакомо по анонимным объектам.
Доступ к методам интерфейсов по умолчанию
Внутри лямбда-выражений запрещено обращаться к методам по умолчанию. Следующий код не скомпилируется:
Встроенные функциональные интерфейсы
Однако в Java 8 также появилось много новых функциональных интерфейсов, которые призваны облегчить вам жизнь. Некоторые интерфейсы хорошо известны по библиотеке Google Guava. Даже если вы незнакомы с этой библиотекой, вам стоить взглянуть, как эти интерфейсы были дополнены некоторыми полезными методами расширений.
Предикаты
Функции
Поставщики
Поставщики (suppliers) предоставляют результат заданного типа. В отличии от функций, поставщики не принимают аргументов.
Потребители
Потребители (consumers) представляют собой операции, которые производятся на одним входным аргументом.
Компараторы
Компараторы хорошо известны по предыдущим версиям Java. Java 8 добавляет в интерфейс различные методы по умолчанию.
Опциональные значения
Опциональные значения (optionals) не являются функциональными интерфейсами, однако являются удобным средством предотвращения NullPointerException. Это важная концепция, которая понадобится нам в следующем разделе, поэтому давайте взглянем, как работают опциональные значения.
Опциональные значение — это по сути контейнер для значения, которое может быть равно null. Например, вам нужен метод, который возвращает какое-то значение, но иногда он должен возвращать пустое значение. Вместо того, чтобы возвращать null, в Java 8 вы можете вернуть опциональное значение.
Потоки
Сначала давайте посмотрим, как работать с потоком последовательно. Сперва создадим источник в виде списка строк:
Filter
Операция Filter принимает предикат, который фильтрует все элементы потока. Эта операция является промежуточной, т.е. позволяет нам вызвать другую операцию (например, forEach ) над результатом. ForEach принимает функцию, которая вызывается для каждого элемента в (уже отфильтрованном) поток. ForEach является конечной операцией. Она не возращает никакого значения, поэтому дальнейший вызов потоковых операций невозможен.
Sorted
Операция Sorted является промежуточной операцией, которая возвращает отсортированное представление потока. Элементы сортируются в обычном порядке, если вы не предоставили свой компаратор:
Помните, что sorted создает всего лишь отсортированное представление и не влияет на порядок элементов в исходной коллекции. Порядок строк в stringCollection остается нетронутым:
Match
Для проверки, удовлетворяет ли поток заданному предикату, используются различные операции сопоставления (match). Все операции сопоставления являются конечными и возвращают результат типа boolean.
Count
Reduce
Эта конечная операция производит свертку элементов потока по заданной функции. Результатом является опциональное значение.
Параллельные потоки
Как уже упоминалось выше, потоки могут быть последовательными и параллельными. Операции над последовательными потоками выполняются в одном потоке процессора, над параллельными — используя несколько потоков процессора.
Следующие пример демонстрирует, как можно легко увеличить скорость работы, используя параллельные потоки.
Сперва создадим большой список из уникальных элементов:
Теперь измерим время сортировки этого списка.
Последовательная сортировка
Параллельная сортировка
Ассоциативные массивы
Как уже упоминалось, ассоциативные массивы (maps) не поддерживают потоки. Вместо этого ассоциативные массивы теперь поддерживают различные полезные методы, которые решают часто встречаемые задачи.
Этот код в особых комментариях не нуждается: putIfAbsent позволяет нам не писать дополнительные проверки на null; forEach принимает потребителя, который производит операцию над каждым элементом массива.
Этот код показывает как использовать для вычислений код при помощи различных функций:
Затем мы узнаем, как удалить объект по ключу, только если этот объект ассоциирован с ключом:
Еще один полезный метод:
Объединить записи двух массивов? Легко:
В случае отсутствия ключа Merge создает новую пару ключ-значение. В противном случае — вызывает функцию объединения для существующего значения.
API для работы с датами
Clock
Часовые пояса
LocalTime
Тип LocalTime представляет собой время с учетом часового пояса, например, 10pm или 17:30:15. В следующем примере создаются два местных времени для часовых поясов, определенных выше. Затем оба времени сравниваются, и вычисляется разница между ними в часах и минутах.
Тип LocalTime содержит различные фабричные методы, которые упрощают создание новых экземпляров, а также парсинг строк.
LocalDate
Создание экземпляра LocalDate путем парсинга строки:
LocalDateTime
Форматирование даты-времени работает так же, как и форматирование даты или времени. Мы можем использовать библиотечные или свои собственные шаблоны.
Подробно о синтаксисе шаблонов можно почитать здесь.
Аннотации
Аннотации в Java 8 являются повторяемыми. Давайте сразу посмотрим пример, чтобы понять, что это такое.
Сперва мы определим аннотацию-обертку, которая содержит массив аннотаций:
Вариант 1: использовать аннотацию-контейнер (старый способ)
Вариант 2: использовать повторяемую аннотацию (новый способ)
Более того, аннотации в Java 8 можно использовать еще на двух элементах:
Вот и все
Полный исходный код статьи доступен на GitHub.
Урок 2. Операторы. Action
В этом уроке пробежимся по нескольким несложным оператором, чтобы понять, что они из себя представляют. И используем Action вместо Observer.
Подключение RxJava к проекту
Рекомендую вам создать тестовый проект и выполнять примеры. Так вы сможете экспериментировать и лучше понять работу операторов.
Чтобы подключить RxJava к проекту, необходимо добавить в gradle-файл строки:
Операторы
В RxJava есть операторы, с помощью которых вы можете создавать новые Observable или менять уже существующие. Операторы можно разделить на категории. В этом уроке мы рассмотрим самые простые операторы из нескольких категорий.
Операторы создания
Эти операторы позволяют создать Observable.
from
Этот оператор мы уже встречали в первом уроке. Он создает Observable из массива или коллекции.
range
Оператор range выдаст последовательность чисел
Мы указываем, что начать необходимо с 10, а кол-во элементов 4
interval
Оператор interval выдает последовательность long чисел начиная с 0. Мы можем указать временной интервал, через который числа будут приходить. Укажем 500 мсек.
Теперь каждые 500 мсек в Observer будет приходить все увеличивающееся значение, начиная с 0.
Обратите внимание, что в логах не будет метода onCompleted. Вернее, когда нибудь он, наверно, все таки придет, когда достигнет значения, максимально доступного для Long. Но ждать придется долго.
fromCallable
Если у вас есть синхронный метод, который вам надо сделать асинхронным, то оператор fromCallable поможет вам
Например, есть метод longAction
Необходимо обернуть его в Callable
И затем можно создавать Observable из Callable
Получившийся Observable запустит метод longAction и вернет вам результат в onNext.
Операторы observeOn и subscribeOn здесь управляют потоками (thread) и обеспечивают асинхронность вызова. Подробно мы поговорим о них в одном из следующих уроков.
Операторы преобразования
Эти операторы позволяют преобразовывать данные, которые генерирует Observable.
map
Оператор map преобразует все элементы последовательности. Для этого нам необходимо написать функцию преобразования. Например конвертация из String в Integer. Создаем Func1
Теперь эту функцию мы передаем в оператор map
Обратите внимание, оператор map мы вызываем сразу после оператора from. Тем самым на вход map придет последовательнось строк сгенерированная в from. А в результате работы map мы уже получим последовательность чисел. В Observer данные придут уже как Integer
Попробуем спровоцировать ошибку преобразования. Заменим число 4 на букву а.
Результат:
onNext: 1
onNext: 2
onNext: 3
onError: java.lang.NumberFormatException: Invalid int: «a»
Мы получили ошибку в метод onError, и, тем самым, последовательность завершилась. Есть, конечно, специальные операторы, которые умеют обрабатывать ошибку и продолжать работу, но об этом я расскажу позже, чтобы сейчас не усложнять.
buffer
Оператор buffer собирает элементы и по мере накопления заданного кол-ва отправляет их дальше одним пакетом.
Создадим Observable из 8 чисел, и добавим к нему буфер с количеством элементов = 3.
Существуют и более сложные операторы преобразования, которые из каждого элемента генерируют отдельную последовательность данных. Я не рассматриваю их пока, чтобы не усложнять материал.
Операторы фильтрации
take
Оператор take возьмет только указанное количество первых элементов из переданной ему последовательности и сформирует из них новую последовательность. Возьмем первые три:
skip
Оператор skip пропустит первые элементы. Пропустим первые 2
distinct
Оператор distinct отсеет дубликаты
filter
Оператор filter может отсеять только нужные элементы. Для этого необходимо создать функцию, в которой будет описан алгоритм фильтрации. Например, оставим только строки содержащие 5.
Используем функцию в операторе filter
Операторы объединения
merge
Оператор merge объединит элементы из двух Observable в один Observable
Есть еще оператор concat, который делает примерно то же самое, но чуть по другому. Позже я расскажу об этом подробнее.
zip
Оператор zip попарно сопоставит элементы из двух Observable. Из каждой пары элементов с помощью функции будет получен один элемент, который будет добавлен в итоговый Observable.
Сначала нам необходимо создать функцию, в которой мы задаем как из двух элементов получить один. В нашем примере мы просто соединим их в одну строку через двоеточие.
Методом from создаем первую последовательность, с типом Integer. Затем в вызове zipWith создаем вторую последовательность с типом String и указываем созданную ранее функцию zipIntWithString
В результате получим одну последовательность с типом String, состоящую из результатов работы функции с парами элементов из двух последовательностей Integer и String.
Операторы условий
takeUntil
Оператор takeUntil будет брать элементы пока не попадется элемент, удовлетворяющий определенному условию. Это условие нам необходимо оформить в виде функции.
Например, создадим условие, что элемент равен 5.
И используем в операторе
Новая последовательность содержит те же элементы, но заканчивается на элементе 5.
all
Оператор all позволяет узнать все ли элементы удовлетворяют указанному условию. Условие нам необходимо оформить в виде функции.
Например, создадим проверку, что все элементы меньше 10.
Применим ее к последовательности чисел
В результате мы получим Boolean последовательность из одного элемента. Этот элемент скажет нам, все ли элементы последовательности подошли под условие.
Я рассмотрел лишь малую часть всех операторов. К тому же некоторые операторы имеют несколько вариантов использования, с различными параметрами. Полный список можно посмотреть здесь.
Повторюсь, оператор не меняет Observable, а создает новый, поверх данного. Соответственно, в цепочке операторов, каждый следующий оператор берет данные предыдущего, меняет их заданным образом и возвращает вам новый Observable.
В отдельной статье я выкладываю реальные рабочие примеры использования RxJava. Рекомендую посмотреть, когда закончите с теорией.
Action
Мы подписываем не Observer, а Action. И этот Action будет получать только Next события.
Всего есть три варианта метода subscribe, в которых мы можем использовать Action:
— subscribe(Action1 onNext)
— subscribe(Action1 onNext, Action1 onError)
— subscribe(Action1 onNext, Action1 onError, Action0 onCompleted)
Мы использовали первый. Соответственно, если вам нужно добавить обработку Error и Completed, используйте второй и третий вариант.
Но учитывайте, что если вы не ловите событие Error, то в случае какой-либо ошибки у вас вылетит Exception.
Функциональные интерфейсы Java
Термин функциональный интерфейс был введен в Java 8. Это интерфейс, который содержит только один абстрактный (не реализованный) метод. Может содержать стандартные и статические, которые имеют реализацию, в дополнение к одному нереализованному.
Вышеуказанное содержит только один метод, и этот метод не имеет реализации.
Обычно интерфейс не содержит реализации методов, которые он объявляет, но он может содержать реализации в методах по умолчанию или в статических. Ниже приведен еще один пример с реализациями некоторых методов:
Вышеупомянутый интерфейс все еще считается функциональным, поскольку он содержит только один не реализованный метод.
Реализация с помощью лямбда-выражения
Лямбда-выражение реализует единственный метод из интерфейса. Чтобы узнать, какой метод реализует лямбда-выражение, интерфейс может содержать только один не реализованный метод. Другими словами, он должен быть функциональным.
Встроенные функциональные интерфейсы
Есть разработанные виды для часто встречающихся вариантов использования, поэтому вам не нужно создавать свои собственные функциональные интерфейсы для каждого небольшого варианта использования.
Function
Интерфейс Function interface(java.util.function.Function) является одним из самых центральных функциональных интерфейсов. Представляет функцию (метод), которая принимает один параметр и возвращает одно значение. Вот как выглядит определение:
Интерфейс Function на самом деле содержит несколько дополнительных методов в дополнение к методам, перечисленным выше, но, поскольку все они поставляются с реализацией по умолчанию, вам не нужно реализовывать их.
Единственный метод, который необходимо реализовать для реализации интерфейса Function, – это apply(). Вот пример реализации функции:
В этой реализации функции реализован метод apply(), поэтому он принимает параметр Long в качестве параметра и возвращает Long. Вот пример использования вышеупомянутого класса AddThree:
Вы также можете реализовать Function с помощью лямбда-выражения:
Как видите, реализация Function теперь встроена в объявление переменной adderLambda, а не в отдельный класс. Это немного короче, плюс мы можем видеть непосредственно в приведенном выше коде, что он делает.
Predicate
Интерфейс Java Predicate, java.util.function.Predicate, представляет простую функцию, которая принимает одно значение в качестве параметра и возвращает true или false:
Интерфейс Predicate содержит больше методов, чем метод test(), но остальные являются стандартными или статическими, которые вам не нужно реализовывать.
Вы можете реализовать Predicate, используя класс, например так:
Вы также можете реализовать Predicate, используя лямбда-выражение:
Эта лямбда-реализация Predicate фактически делает то же самое, что и реализация выше, использующая класс.
UnaryOperator
Интерфейс Java UnaryOperator представляет операцию, которая принимает один параметр и возвращает параметр того же типа:
Интерфейс UnaryOperator может использоваться для представления операции, которая принимает конкретный объект в качестве параметра, изменяет этот объект и возвращает его снова – возможно, как часть цепочки обработки функционального потока.
BinaryOperator
BinaryOperator – это функциональный интерфейс, представляющий операцию, которая принимает два параметра и возвращает одно значение. Оба параметра и тип возвращаемого значения должны быть одного типа. Полезен при реализации функций, которые суммируют, вычитают, делят, умножают и т. д. Два элемента одного типа и возвращают третий элемент того же типа.
Supplier
Интерфейс Supplier – это функциональный интерфейс, представляющий функцию, которая предоставляет значение некоторых видов. Также можно рассматривать как фабричный интерфейс:
Эта реализация Java Supplier возвращает новый экземпляр Integer со случайным значением от 0 до 1000.
Consumer
Consumer – это функциональный интерфейс, представляющий функцию, которая потребляет значение без возврата какого-либо значения. Реализация может распечатывать значение или записывать его в файл, или по сети и т. д. Реализация:
How to Use Actions
An Action can be used to separate functionality and state from a component. For example, if you have two or more components that perform the same function, consider using an Action object to implement the function. An Action object is an action listener that provides not only action-event handling, but also centralized handling of the state of action-event-firing components such as tool bar buttons, menu items, common buttons, and text fields. The state that an action can handle includes text, icon, mnemonic, enabled, and selected status.
You typically attach an action to a component using the setAction method. Here’s what happens when setAction is invoked on a component:
Here’s an example of creating a tool-bar button and menu item that perform the same function:
To create an Action object, you generally create a subclass of AbstractAction and then instantiate it. In your subclass, you must implement the actionPerformed method to react appropriately when the action event occurs. Here’s an example of creating and instantiating an AbstractAction subclass:
When the action created by the preceding code is attached to a button and a menu item, the button and menu item display the text and icon associated with the action. The L character is used for mnemonics on the button and menu item, and their tool-tip text is set to the SHORT_DESCRIPTION string followed by a representation of the mnemonic key.
Click the Launch button to run ActionDemo using Java™ Web Start (download JDK 7 or later). Or, to compile and run the example yourself, consult the example index.
Choose the top item from the left menu (Menu > Go left).
The text area displays some text identifying both the event source and the action listener that received the event.
Click the leftmost button in the tool bar.
The text area again displays information about the event. Note that although the source of the events is different, both events were detected by the same action listener: the Action object attached to the components.
Choose the top item from the Action State menu.
This disables the «Go left» Action object, which in turn disables its associated menu item and button.
Here is what the user sees when the «Go left» action is disabled:
| |
Here’s the code that disables the «Go left» action:
We chose to create an icon-only button and a text-only menu item from the same action by setting the icon property to null and the text to an empty string. However, if a property of the Action changes, the widget may try to reset the icon and text from the Action again.
The Action API
The following tables list the commonly used Action constructors and methods. The API for using Action objects falls into three categories: