1.17M
Category: programmingprogramming

Аспектно-ориентированное программирование

1.

Spring
AOP
Transactions
Servlet API
Spring MVC

2.

AOP
Аспектно-ориентированное программирование - это методика,
дополняющая ООП-парадигму, позволяющая реализовывать
сквозную функционалтьность (например, выполнять внутри
транзакции все методы, которые помечены аннотацией
@Transactional)

3.

Основные термины
Аспект (aspect) - модуль или класс, реализующий сквозную функциональность.
Аспект изменяет поведение остального кода, применяя совет в точках
соединения, определённых некоторым срезом.
Точка соединения (join point) - точка в выполняемой программе, где следует
применить совет.
Например, метод с аннотацией @Transactional. В Spring AOP точка соединения это всегда вызов метода.
Совет (advice) - действие (метод аспекта), выполняемое аспектом в конкретной
точке соединения. Совет может быть выполнен до, после или вместо точки
соединения.
Срез (pointcut) - набор точек соединения. Срез определяет, подходит ли данная
точка соединения к данному совету. Для определения среза в спринге по
умолчанию используется язык выражений AspectJ.

4.

Чтобы использовать Spring AOP, в конфигурации надо добавить аннотацию
@EnableAspectJAutoProxy
@Configuration
@EnableAspectJAutoProxy
public class ApplicationConfig {
...
}
Аналог в XML-конфигурации
<aop:aspectj-autoproxy/>

5.

Чтобы объявить класс аспектом, надо поставить над ним аннотацию @Aspect.
@Aspect
@Service <- аннотация @Aspect не добавляет бин в контекст
public class AspectExample {
Logger log = LoggerFactory.getLogger(AspectExample.class);
@Around("execution(* ru.bellintegrator.practice.controller.impl.PersonControllerImpl.persons())")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
log.info("INVOCATION METHOD:" + joinPoint.getSignature().toShortString());
//method invocation
Object returnValue = joinPoint.proceed();
log.info("METHOD:" + joinPoint.getSignature().toShortString() + "RETURNS:" + returnValue);
return returnValue;
}
}

6.

Пример можно посмотреть в гитхабе:
https://github.com/azEsm/empty_project/tree/aspect_example
Более подробно про аспекты в спринге:
https://docs.spring.io/spring/docs/current/spring-framework-reference/html/aop.html

7.

Transactions
Спринг предоставляет возможность декларативно управлять транзакциями. Это
снижает вероятность ошибок и делает код более читаемым за счёт того, что
бизнес логика отделяется от системной.
В спринге для создания транзакции достаточно поставить над методом
аннотацию @Transactional.
@Transactional
public void businessLogic() {
// работа с БД через EntityManager
}

8.

Параметры аннотации @Transactional
transactionManager
propagation
isolation
timeout
readOnly
rollbackFor
noRollbackFor

9.

Transaction Propagation Behaviors
REQUIRED - если транзакция есть, выполнение продолжается в рамках текущей транзакции.
Если нет, стартует новая.
SUPPORTS - если транзакция есть, выполнение продолжается в рамках текущей транзакции.
Если нет, выполняется без транзакции.
MANDATORY - если транзакция есть, выполнение продолжается в рамках текущей транзакции.
Если нет, бросается исключение.
REQUIRES_NEW - всегда создаётся новая транзакция. Если выполняется в рамках транзакции,
создаётся вложенная транзакция. Текущая транзакция приостанавливается.
NOT_SUPPORTED - всегда выполняется без транзакции. Если выполняется в рамках
транзакции, текущая транзакция приостанавливается.
NEVER - всегда выполняется без транзакции. Если выполняется в рамках транзакции, бросается
исключение.
NESTED - создаётся вложенная транзакция. Работает не во всех менеджерах транзакций.

10.

/**
* Support a current transaction, execute non-transactionally if none exists.
* Analogous to EJB transaction attribute of the same name.
* <p>Note: For transaction managers with transaction synchronization,
* PROPAGATION_SUPPORTS is slightly different from no transaction at all,
* as it defines a transaction scope that synchronization will apply for.
* As a consequence, the same resources (JDBC Connection, Hibernate Session, etc)
* will be shared for the entire specified scope. Note that this depends on
* the actual synchronization configuration of the transaction manager.
...
*/
SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS),

11.

Аннотация @Transactional
разными контекстами:
● persistence context
● транзакции в базе данных
управляет
двумя

12.

Аннотация @Transactional определяет транзакцию в базе данных. Но работает в
рамках persistence context’а. Persistence context в JPA - это EntityManager. В
hibernate он реализован через Session.
Persistence context - это объект, который отслеживает состояние определённых
java объектов (hibernate entities) и отвечает за то, чтобы изменения в этих
объектах когда-нибудь были сброшены в базу данных.
Один EntityManager может быть использован в нескольких транзакциях в базе
данных
@PersistenceContext(type=PersistenceContextType.EXTENDED)
private EntityManager em;

13.

Но обычно в рамках одной транзакции используется один EntityManager.
@PersistenceContext
private EntityManager em;
package javax.persistence;
/**
* Specifies whether a transaction-scoped or extended
* persistence context is to be used in {@link PersistenceContext}.
* If not specified, a transaction-scoped persistence context is used.
*
* @since Java Persistence 1.0
*/
public enum PersistenceContextType {
/**
* Transaction-scoped persistence context
*/
TRANSACTION,
/**
* Extended persistence context
*/
EXTENDED
}

14.

Может возникнуть вопрос, почему EntityManager в объекте устанавливается один
раз, но при этом в при каждом вызове транзакционного метода используется
новый EntityManager. Дело в том, что за интерфейсом EntityManager стоит
прокси-объект.
Обычно
используется
объект
SharedEntityManagerInvocationHandler,
который
реализует
интерфейс
EntityManager. Это можно увидеть в дебагере.

15.

Для extended контекста EntityManager будет выглядеть так

16.

Extended context можно безопасно использовать в JEE-контейнерах в Stateful
бинах, т.к. там контейнер управляет многопоточным доступом. В спринге
использовать extended context не рекомендуется, т.к. бины, хранящие состояние
(например, singleton) никак не синхронизированы.
The @PersistenceContext annotation has an optional attribute type, which defaults to
PersistenceContextType.TRANSACTION. This default is what you need to receive a shared
EntityManager proxy. The alternative, PersistenceContextType.EXTENDED, is a completely different
affair: This results in a so-called extended EntityManager, which is not thread-safe and hence must
not be used in a concurrently accessed component such as a Spring-managed singleton bean.
Extended EntityManagers are only supposed to be used in stateful components that, for example,
reside in a session, with the lifecycle of the EntityManager not tied to a current transaction but rather
being completely up to the application.
https://docs.spring.io/spring/docs/current/spring-framework-reference/data-access.html#orm-jpadao

17.

Для работы аннотации @Transactional используются три
компонента:
● EntityManager proxy
● Transactional aspect (Spring AOP)
● Transaction manager

18.

Transactional Aspect
Transactional Aspect - это аспект типа around. Этот аспект
реализован в классе TransactionInterceptor. Аспект выполняет
две основные задачи:
● перед вызовом метода определяет, надо ли стартовать
новую транзакцию или выполнять метод в рамках
существующей
(решение
делегируется
менеджеру
транзакций)
● после выполнения метода определяет, коммитить или
откатывать транзакцию.

19.

package org.springframework.transaction.interceptor;
/**
* AOP Alliance MethodInterceptor for declarative transaction
* management using the common Spring transaction infrastructure
* ({@link org.springframework.transaction.PlatformTransactionManager}).
* ...
*/
public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable {
….
@Override
public Object invoke(final MethodInvocation invocation) throws Throwable {
// Work out the target class: may be {@code null}.
// The TransactionAttributeSource should be passed the target class
// as well as the method, which may be from an interface.
Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
// Adapt to TransactionAspectSupport's invokeWithinTransaction...
return invokeWithinTransaction(invocation.getMethod(), targetClass, new InvocationCallback() {
@Override
public Object proceedWithInvocation() throws Throwable {
return invocation.proceed();
}
});
}
https://github.com/spring-projects/spring-framework/blob/master/springtx/src/main/java/org/springframework/transaction/interceptor/TransactionInterceptor.java

20.

Transaction Manager
Перед вызовом транзакционного метода менеджер транзакций определяет:
● надо ли создавать новый EntityManager
● надо ли стартовать новую транзакцию в БД
Определяется это исходя из следующих условий:
● выполняется ли метод уже в рамках существующей транзакции
● какое значение атрибута propagation аннотации @Transactional (например,
для значения REQUIRES_NEW всегда создаётся новая транзакция)
Если необходимо создать новую транзакцию, менеджер транзакций создаёт
новый EntityManager.

21.

22.

Конфигурация транзакций
@Configuration
@EnableTransactionManagement
public class ApplicationConfig {
Аннотация @EnableTransactionManagement в конфигурации показывает спрингу, что к
методам с аннотацией @Transactional надо применять аспект TransactionInterceptor.
Также необходимо добавить в контекст бин PlatformTransactionManager. Например
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
Более подробно про транзакции в спринге:
https://docs.spring.io/spring/docs/current/spring-framework-reference/data-access.html#transaction

23.

Servlet API
Servlet API - это спецификация для обработки запросов.
Реализовано только для http.
Основные пакеты:
javax.servlet - классы и интерфейсы, не зависящие от протокола
javax.servlet.http - классы и интерфейсы, специфичные для
протокола http

24.

Servlet API, как и JDBC - это спецификация. Содержит в
основном интерфейсы. Контейнер сервлетов - это конкретная
реализация спецификации.
Самые распространённые контейнеры сервлетов:
● Tomcat
● Jetty
● Undertow

25.

Основная задача сервлета - принимать запрос от клиента и
формировать ответ.
Основной метод сервлета - это service.
void service(ServletRequest request, ServletResponse response)

26.

Пример HTTP-запроса
GET /wiki/страница HTTP/1.1
Host: ru.wikipedia.org
User-Agent: Mozilla/5.0 (X11; U; Linux i686; ru; rv:1.9b5) Gecko/2008050509
Firefox/3.0b5
Accept: text/html
Connection: close
(пустая строка)
(дальше идёт тело HTTP-запроса. Например, какие-то параметры, JSON и т.п.)

27.

Пример ответа на HTTP-запрос
HTTP/1.1 200 OK
Date: Wed, 11 Feb 2009 11:20:59 GMT
Server: Apache
X-Powered-By: PHP/5.2.4-2ubuntu5wm1
Last-Modified: Wed, 11 Feb 2009 11:20:59 GMT
Content-Language: ru
Content-Type: text/html; charset=utf-8
Content-Length: 1234
Connection: close
(пустая строка)
(страница в HTML-формате, JSON и т.п.)

28.

HttpServlet - класс предназначенный для обработки запросов и формирования
ответов по протоколу HTTP.
В этом классе есть отдельные методы для разных HTTP-методов
https://www.tutorialspoint.com/http/http_methods.htm
https://www.w3schools.com/tags/ref_httpmethods.asp
Например:
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws
ServletException, IOException
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws
ServletException, IOException

29.

Классы HttpServletRequest и HttpServletResponse - это
представления HTTP-запроса и HTTP-ответа в виде javaобъектов.
Чтобы
читать
данные
из
тела
HTTP-запроса
у
HttpServletRequest есть метод getReader(), который возвращает
BufferedReader, а у HttpServletResponse есть метод getWriter(), с
помощью которого можно писать данные в тело HTTP-ответа.

30.

Дескриптор развёртывания
Дескриптор развёртывания - это конфигурационный файл, в
котором описано, как разворачивать приложение в контейнере
сервлетов. В этом файле описывается, какие URL будут
передаваться определённому сервлету, какие URL требуют
аутентификации и т.п. Этот файл должен называться web.xml и
находиться в директории WEB-INF. По этому дескриптору
создаётся ServletContext.

31.

Пример дескриптора:
<web-app xmlns="http://java.sun.com/xml/ns/javaee" version="3.0" ...>
<servlet>
<servlet-name>example</servlet-name>
<servlet-class>ru.bellintegrator.example.servlets.Example</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>example</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>

32.

Передача параметров в Servlet
<web-app xmlns="http://java.sun.com/xml/ns/javaee" version="3.0" ...>
<servlet>
<servlet-name>example</servlet-name>
<servlet-class>ru.bellintegrator.example.servlets.Example</servlet-class>
<init-param>
<param-name>someParameter</param-name>
<param-value>42</param-value>
</init-param>
</servlet>
...
</web-app>
public class Example extends HttpServlet {
protected String someParameter;
@Override <- метод из класса GenericServlet
public void init(ServletConfig servletConfig) throws ServletException {
someParameter = servletConfig.getInitParameter("someParameter");
}
...

33.

Аналогичная конфигурация аннотациями
(доступно с версии Servlet API 3.0):
@WebServlet(
urlPatterns = "/*",
initParams = {
@WebInitParam(
name = "someParameter",
value = "42"
)
}
)
public class Example extends HttpServlet {
...

34.

Задание параметров контекста (доступные для всех сервлетов)
<web-app xmlns="http://java.sun.com/xml/ns/javaee" version="3.0" ...>
<context-param>
<param-name>contextParam</param-name>
<param-value>value</param-value>
</context-param>
...
</web-app>
public class Example extends HttpServlet {
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response) {
String contextParam = request
.getSession()
.getServletContext()
.getInitParameter("contextParam");
...
}

35.

Конфигурация приложения
<web-app xmlns="http://java.sun.com/xml/ns/javaee" version="3.0" ...>
...
<session-config>
<session-timeout>30</session-timeout>
</session-config>
<welcome-file-list>
<welcome-file>public/greeting.html</welcome-file>
</welcome-file-list>
...
</web-app>

36.

По умолчанию экземпляр сервлета создаётся в неопределённый момент времени
между запуском приложения и запросом к сервлету. Если надо создать экземпляр
сервлета при старте приложения, надо добавить параметр load-on-startup:
<servlet>
<servlet-name>example</servlet-name>
<servlet-class>ru.bellintegrator.example.servlets.Example</servlet-class>
<load-on-startup>5</load-on-startup>
</servlet>
Параметр тэга load-on-startup - это порядок. Например, если в приложении есть два
сервлета и у одного этот параметр - 3, а у другого - 5, то первым будет создан сервлет
с параметром 3.

37.

Динамическое добавление сервлетов
@WebListener
public class ExampleContextListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
ServletContext context = servletContextEvent.getServletContext();
ServletRegistration.Dynamic servlet = context.addServlet (
"dynamicServlet",
DynamicServlet.class.getName()
);
servlet.setInitParameter("someParameter", "42");
servlet.addMapping(“/dynamic”);
}
@Override
public void contextDestroyed(ServletContextEvent arg0) {
...
}
}

38.

Добавление листенера в web.xml (аналог аннотации
@WebListener)
<web-app xmlns="http://java.sun.com/xml/ns/javaee" version="3.0" ...>

<listener>
<listener-class>
ru.bellintegrator.example.init.ExampleContextListener
</listener-class>
</listener>
...
</web-app>

39.

Фильтры
Фильтры используются для предварительной обработки запроса перед тем, как
он попадёт в сервлет, а также для обработки ответа после сервлета. Запрос
можно проходить через несколько фильтров (шаблон проектирования chain of
responsibilities)
Фильтры конфигурируются аналогично сервлетам в web.xml
<filter>
<filter-name>requestLoggingFilter</filter-name>
<filter-class>ru.bellintegrator.example.servlets.filters.RequestLoggingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>requestLoggingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

40.

Класс RequestLoggingFilter должен реализовать интерфейс javax.servlet.Filter
public interface Filter {
void init(FilterConfig config) throws ServletException;
void doFilter(
ServletRequest request,
ServletResponse response,
FilterChain chain
) throws IOException, ServletException;
void destroy();
}
Необходимо переопределить метод doFilter и в конце обработки вызвать у параметра
chain метод doFilter. После этого обработка продолжится в следующем фильтре или в
сервлете.
public void doFilter(
ServletRequest request,
ServletResponse response,
FilterChain chain
) throws IOException, ServletException {
log.debug(“Filter”);
chain.doFilter(request, response);
}

41.

Жизненный цикл сервлета
Загрузка класса сервлета — когда контейнер получает запрос для сервлета,
то происходит загрузка класса сервлета в память и вызов конструктора без
параметров.
Инициализация класса сервлета — после того как класс загружен контейнер
инициализирует объект ServletConfig для этого сервлета и внедряет его
через init() метод. Это и есть место где сервлет класс преобразуется из
обычного класса в сервлет.
Обработка запросов — после инициализации сервлет готов к обработке
запросов. Для каждого запроса клиента контейнер сервлетов порождает
новый поток и вызывает метод service() путем передачи ссылки на объект
ответа и запроса.
Удаление из Service — когда контейнер останавливается или
останавливается приложение, то контейнер сервлетов уничтожает классы
сервлетов путем вызова destroy() метода.

42.

Spring MVC
Spring MVC работает поверх Servlet API и существенно
упрощает работу с ним. Реализует архитектуру Model-ViewController.
Model (модель) - данные приложения
View (отображение, представление) - отображение данных для
пользователя. Например, HTML-страница или JSON
Controller - отвечает за обработку запросов пользователя и
создание отображения на основе данных приложения.

43.

За обработку HTTP-запросов в Spring MVC отвечает
DispatcherServlet.

44.

XML-конфигурация Spring MVC
<web-app xmlns="http://java.sun.com/xml/ns/javaee" version="3.0" ...>
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/dispatcher-config.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>

45.

Начиная с версии Servlet 3.0 можно конфигурировать ServletContext без web.xml. В спринге для
этого есть интерфейс WebApplicationInitializer.
public class AppInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletContext) {
XmlWebApplicationContext appContext = new XmlWebApplicationContext();
appContext.setConfigLocation("/WEB-INF/spring/dispatcher-config.xml");
ServletRegistration.Dynamic dispatcher = servletContext.addServlet(
"dispatcher",
new DispatcherServlet(appContext)
);
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/");
}
}

46.

package org.springframework.web;

/**
* Interface to be implemented in Servlet 3.0+ environments in order to configure the
* {@link ServletContext} programmatically -- as opposed to (or possibly in conjunction
* with) the traditional {@code web.xml}-based approach.

* @author Chris Beams
* @since 3.1
* @see SpringServletContainerInitializer

*/
public interface WebApplicationInitializer {
void onStartup(ServletContext servletContext) throws ServletException;

47.

package org.springframework.web;
...
import javax.servlet.ServletContainerInitializer;

@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
/**
* Delegate the {@code ServletContext} to any {@link WebApplicationInitializer}
* implementations present on the application classpath.

*/
@Override
public void onStartup(
@Nullable Set<Class<?>> webAppInitializerClasses,
ServletContext servletContext
) throws ServletException {
...

48.

Конфигурация Spring MVC без XML
public class AppInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletContext) {
// Create the 'root' Spring application context
AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
rootContext.register(AppConfig.class);
// Manage the lifecycle of the root application context
servletContext.addListener(new ContextLoaderListener(rootContext));
// Create the dispatcher servlet's Spring application context
AnnotationConfigWebApplicationContext dispatcherContext = new AnnotationConfigWebApplicationContext();
dispatcherContext.register(DispatcherConfig.class);
// Register and map the dispatcher servlet
ServletRegistration.Dynamic dispatcher = servletContext.addServlet(
"dispatcher",
new DispatcherServlet(dispatcherContext)
);
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/");
}
}

49.

dispatcher-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<mvc:annotation-driven />
</beans>

50.

DispatcherConfig.class
@Configuration
@EnableWebMvc
public class DispatcherConfig {
...
}

51.


Все
HTTP-запросы
в
Spring
MVC
направляются
в
DispatcherServlet.doService(...).
Далее после некоторой обработки запроса вызывается метод интерфейса
HandlerMapping
HandlerExecutionChain getHandler(HttpServletRequest request)
Этот метод определяет, какие фильтры и контроллер будет обрабатывать
запрос. Создаёт цепочку обработки запроса.
● Контроллер обрабатывает запрос, выполняет какую-то бизнес-логику и
возвращает имя отображения.
● Чтобы получить нужное отображение, DispatcherServlet в цикле обращается
ко всем зарегистрированным реализациям ViewResolver и вызывает метод
View resolveViewName(String viewName, Locale locale) throws Exception;
Возвращается первое полученное не null значение. View - это объект, который
рендерит HTML-страницу по данным из модели.
● DispatcherServlet передаёт в полученный объект View данные модели в
виде атрибутов и генерирует HTTP-ответ.

52.

/**
* Indicates that an annotated class is a "Controller" (e.g. a web controller).
*
* <p>This annotation serves as a specialization of {@link Component @Component},
* allowing for implementation classes to be autodetected through classpath scanning.
* It is typically used in combination with annotated handler methods based on the
* {@link org.springframework.web.bind.annotation.RequestMapping} annotation.
*
* ...
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {
...

53.

@Controller
@RequestMapping("/hello")
public class HelloController {
@RequestMapping(method = RequestMethod.GET)
public String printHello(ModelMap model) {
model.addAttribute("message", "Hello!");
return "hello";
}
}
<html>
<head>
<title>Hello Spring MVC</title>
</head>
<body>
<h2>${message}</h2>
</body>
</html>

54.

/**
* A convenience annotation that is itself annotated with
* {@link Controller @Controller} and {@link ResponseBody @ResponseBody}.
* <p>
* Types that carry this annotation are treated as controllers where
* {@link RequestMapping @RequestMapping} methods assume
* {@link ResponseBody @ResponseBody} semantics by default.
*
* ...
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {

55.

/**
* Annotation that indicates a method return value should be bound to the web
* response body. Supported for annotated handler methods in Servlet environments.
*
* <p>As of version 4.0 this annotation can also be added on the type level in
* which case it is inherited and does not need to be added on the method level.
*
* ...
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ResponseBody {

56.

@RestController
@RequestMapping(value = "/house", produces = APPLICATION_JSON_VALUE)
public class DummyControllerImpl implements DummyController {

@RequestMapping(value = "/person", method = {POST})
public ResponseView person(@RequestBody PersonView person) {
return dummyService.add(person);
}

}
public class ResponseView {
boolean success;
}
{"success":"true"}

57.

Path variables
@RequestMapping(value = "/library/book/{isbn}", method = GET)
public String bookByIsbn(
@PathVariable("isbn") String bookIdentifier
){
...
}
url: http://localhost:8080/library/book/42
@RequestMapping(value = "/library/book/{id:[\\d]+}", method = GET)
public String bookById(
@PathVariable("id") long bookId
){
...
}
url: http://localhost:8080/library/book/42
NOT: http://localhost:8080/library/book/aaa

58.

Request parameters
@RequestMapping(value = "/library/book", method = GET)
@ResponseBody
public String bookByIsbn(
@RequestParam("isbn") String bookIdentifier
){
...
}
url: http://localhost:8080/library/book?isbn=42
Статья про RequestMapping: https://www.baeldung.com/spring-requestmapping

59.

@RestControllerAdvice
public class ExceptionHandlerController {
@ExceptionHandler(Exception.class)
public String unhandledException(Exception e) {
return e.getMessage();
}
}

60.

@RestControllerAdvice
public class ResponseBodyHandler implements ResponseBodyAdvice<Object> {
private final Logger log = LoggerFactory.getLogger(getClass());
@Override
public boolean supports(MethodParameter methodParameter, Class<? extends
HttpMessageConverter<?>> aClass) { return true; }
@Override
public Object beforeBodyWrite(Object body, MethodParameter methodParameter,
MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass,
ServerHttpRequest serverHttpRequest, ServerHttpResponse response) {
ResultView resultView = new ResultView();
…Логика определяющая что именно нужно подкладывать в результат работы метода или вообще заменять его
resultView.data = body;
return resultView; }
}
English     Русский Rules