Java for Beginner
680 subscribers
573 photos
158 videos
12 files
869 links
Канал от новичков для новичков!
Изучайте Java вместе с нами!
Здесь мы обмениваемся опытом и постоянно изучаем что-то новое!

Наш YouTube канал - https://www.youtube.com/@Java_Beginner-Dev

Наш канал на RUTube - https://rutube.ru/channel/37896292/
Download Telegram
Основы аспектно-ориентированного программирования (AOP) в Spring

Аспектно-ориентированное программирование (AOP) – это подход, позволяющий разделять сквозную функциональность приложения (например, логирование, безопасность, транзакции) от основной логики. AOP в Spring является мощным инструментом для управления поведением приложения, упрощая код и повышая его модульность.

AOP — это методология программирования, которая вводит концепцию аспектов. Аспект – это модуль, содержащий сквозную функциональность, которую можно применить к разным частям программы без изменения их исходного кода.

Основные концепции AOP

Аспект (Aspect)
Модуль, содержащий сквозную функциональность (например, логирование или авторизацию).
@Aspect
@Component
public class LoggingAspect {
@Before("execution(* com.example.service.*.*(..))")
public void logBefore() {
System.out.println("Logging before method execution");
}
}


Точка соединения (Join Point)

Определённое место в программе, где можно "вставить" аспект. Например, вызов метода, создание объекта.

Срез (Pointcut)

Условие, определяющее, на какие точки соединения должен применяться аспект.
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceLayerMethods() {}


Совет (Advice)
Логика, которая выполняется на основе среза. Типы советов:

@Before: выполняется до вызова метода.
@After: выполняется после завершения метода.
@AfterReturning: выполняется, если метод завершился успешно.
@AfterThrowing: выполняется при возникновении исключения.
@Around: оборачивает выполнение метода, позволяет управлять его выполнением.
@Before("serviceLayerMethods()")
public void logBeforeServiceMethod() {
System.out.println("Executing service method...");
}


Как включить AOP в Spring?

Добавьте зависимость в проект: Для использования AOP в Spring Boot:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>


Включите поддержку AOP: Добавьте аннотацию @EnableAspectJAutoProxy в ваш @Configuration класс.
@Configuration
@EnableAspectJAutoProxy
public class AppConfig {}


Создайте аспект:
@Aspect
@Component
public class LoggingAspect {
@Before("execution(* com.example.service.*.*(..))")
public void logBefore() {
System.out.println("Method execution intercepted by AOP.");
}
}


Создайте бизнес-логику:
@Service
public class UserService {
public void getUserById(Long id) {
System.out.println("Getting user by ID: " + id);
}
}


Результат:
Вызов метода getUserById(1L) сначала выполняет логику @Before, а затем основную логику метода.

Пример с логированием (совет @Around):
@Aspect
@Component
public class PerformanceLoggingAspect {

@Around("execution(* com.example.service.*.*(..))")
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();

Object result = joinPoint.proceed(); // Выполнение метода

long executionTime = System.currentTimeMillis() - start;
System.out.println(joinPoint.getSignature() + " executed in " + executionTime + "ms");
return result;
}
}
Теперь при вызове методов сервисного слоя будет логироваться время выполнения.


#Java #Training #Spring #AOP
Зачем нужно AOP?

Основная задача AOP — изолировать сквозную функциональность, которую сложно выразить через объектно-ориентированное программирование. AOP упрощает код, устраняя необходимость дублирования.

Примеры сквозной функциональности

Логирование:
Логировать действия пользователя, запросы или ошибки.

@Before("execution(* com.example.controller.*.*(..))")
public void logRequest() {
System.out.println("Handling request...");
}


Управление транзакциями:
Начало и завершение транзакции при выполнении метода.

@Around("@annotation(org.springframework.transaction.annotation.Transactional)")
public Object manageTransaction(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("Transaction started");
Object result = joinPoint.proceed();
System.out.println("Transaction committed");
return result;
}


Авторизация:
Проверка, имеет ли пользователь доступ к определённому ресурсу.

@Before("execution(* com.example.service.*.*(..))")
public void checkAuthorization() {
if (!isAuthorized()) {
throw new SecurityException("User is not authorized");
}
}


Отслеживание производительности:
Логирование времени выполнения метода.

@Around("execution(* com.example.service.*.*(..))")
public Object logPerformance(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
Object result = joinPoint.proceed();
long elapsedTime = System.currentTimeMillis() - start;
System.out.println(joinPoint.getSignature() + " executed in " + elapsedTime + "ms");
return result;
}


Основные преимущества использования AOP

Модульность:
Сквозная функциональность сосредоточена в аспектах, что упрощает сопровождение кода.

Читаемость:
Устранение дублирования. Бизнес-логику можно сосредоточить в одном месте.

Гибкость:
Возможность легко изменять аспекты без изменения основного кода.

Повышение тестируемости:
Сквозную функциональность можно тестировать отдельно.

Практический пример: Логирование запросов

Аспект для логирования:
@Aspect
@Component
public class RequestLoggingAspect {

@Before("execution(* com.example.controller.*.*(..))")
public void logRequest(JoinPoint joinPoint) {
System.out.println("Request to: " + joinPoint.getSignature());
}
}


Контроллер:
@RestController
public class UserController {

@GetMapping("/users/{id}")
public String getUser(@PathVariable Long id) {
return "User with ID: " + id;
}
}
Результат: Каждый запрос к контроллеру будет логироваться через аспект.


Когда AOP использовать не следует?

Сложная логика аспекта:
Если аспект содержит значительную часть логики, его тестирование и сопровождение могут усложниться.

Чрезмерное использование:
Злоупотребление AOP может сделать проект сложным для понимания из-за "невидимых" операций.

#Java #Training #Spring #AOP
Понятия Joinpoint, Pointcut, Advice

В AOP (аспектно-ориентированном программировании) три ключевых концепции — Joinpoint, Pointcut и Advice — определяют, как и где выполняется логика аспектов. Эти понятия позволяют чётко определить, где вставлять дополнительную функциональность, и упростить реализацию сквозных задач.

Joinpoint


Joinpoint — это конкретная точка в выполнении программы, где аспект может быть применён.

Примеры:
Вызов метода.
Выполнение метода.
Обработка исключений.


Примеры Joinpoint в Spring AOP

Spring поддерживает только Joinpoint, связанные с методами:
Вызов метода (method execution).
Перехват аргументов метода.
Обработка результата или исключения.


Пример:
@Before("execution(* com.example.service.UserService.*(..))")
public void logMethodCall(JoinPoint joinPoint) {
System.out.println("Method called: " + joinPoint.getSignature());
System.out.println("Arguments: " + Arrays.toString(joinPoint.getArgs()));
}


Основные методы Joinpoint

getSignature(): возвращает информацию о вызываемом методе.
getArgs(): возвращает массив аргументов метода.
getTarget(): объект, на котором вызван метод.
getThis(): прокси-объект, созданный Spring
AOP.

Пример использования:
@After("execution(* com.example.service.UserService.*(..))")
public void logAfter(JoinPoint joinPoint) {
System.out.println("Executed method: " + joinPoint.getSignature().getName());
}


Pointcut

Pointcut — это выражение, определяющее набор Joinpoint, к которым будет применяться аспект.
Pointcut помогает сосредоточить внимание только на нужных местах кода.


В Spring используются SpEL (Spring Expression Language) для создания срезов:

По классу и методам:
execution(* com.example.service.UserService.*(..))


По пакетам:
execution(* com.example..*(..)) // Все методы во всех пакетах


По аннотациям:
@annotation(org.springframework.transaction.annotation.Transactional)


Пример:
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceLayerMethods() {}
Теперь serviceLayerMethods() можно использовать как ссылку на срез.


Advice

Advice — это дополнительная логика, которая выполняется в определённой точке выполнения программы (Joinpoint).
Advice описывает, что нужно выполнить.

Типы Advice в Spring

@Before: выполняется перед вызовом метода.
@Before("execution(* com.example.service.UserService.*(..))")
public void logBefore() {
System.out.println("Method is about to be called");
}


@After: выполняется после завершения метода (независимо от результата).
@After("execution(* com.example.service.UserService.*(..))")
public void logAfter() {
System.out.println("Method has been executed");
}


@AfterReturning: выполняется, если метод завершился успешно.
@AfterReturning(pointcut = "execution(* com.example.service.UserService.*(..))", returning = "result")
public void logAfterReturning(Object result) {
System.out.println("Method returned value: " + result);
}


@AfterThrowing: выполняется при выбросе исключения.
@AfterThrowing(pointcut = "execution(* com.example.service.UserService.*(..))", throwing = "exception")
public void logAfterThrowing(Exception exception) {
System.out.println("Exception thrown: " + exception.getMessage());
}


@Around: оборачивает выполнение метода, позволяет контролировать его выполнение.
@Around("execution(* com.example.service.UserService.*(..))")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("Before method execution");
Object result = joinPoint.proceed();
System.out.println("After method execution");
return result;
}


#Java #Training #Spring #AOP #Joinpoint #Pointcut #Advice
Создание простого аспекта

Теперь разберёмся, как создать и настроить простой аспект в Spring.

Шаг 1: Добавьте зависимость

Добавьте зависимость Spring AOP в pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>


Шаг 2: Включите поддержку AOP

Добавьте аннотацию @EnableAspectJAutoProxy в конфигурационный класс:
@Configuration
@EnableAspectJAutoProxy
public class AppConfig {}


Шаг 3: Реализация аспекта

Создайте аспектный класс:
@Aspect
@Component
public class LoggingAspect {

@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceMethods() {}

@Before("serviceMethods()")
public void logBefore(JoinPoint joinPoint) {
System.out.println("Before method: " + joinPoint.getSignature().getName());
}

@AfterReturning(pointcut = "serviceMethods()", returning = "result")
public void logAfterReturning(Object result) {
System.out.println("Method returned: " + result);
}

@AfterThrowing(pointcut = "serviceMethods()", throwing = "exception")
public void logAfterThrowing(Exception exception) {
System.out.println("Exception: " + exception.getMessage());
}
}


Создайте бизнес-логику:
@Service
public class UserService {
public String getUserById(Long id) {
System.out.println("Fetching user...");
if (id == null) {
throw new IllegalArgumentException("ID cannot be null");
}
return "User" + id;
}
}


Контроллер для проверки:
@RestController
public class UserController {
@Autowired
private UserService userService;

@GetMapping("/users/{id}")
public String getUser(@PathVariable Long id) {
return userService.getUserById(id);
}
}


Результат работы аспекта


При вызове метода getUserById(1):
- @Before: "Before method: getUserById".
- Выполняется логика метода.
- @AfterReturning: "Method returned: User1".

При вызове метода с ошибкой getUserById(null):
- @Before: "Before method: getUserById".
- Метод выбрасывает исключение.
- @AfterThrowing: "Exception: ID cannot be null".

Советы по работе с AOP

Используйте AOP для сквозных задач, таких как:
Логирование.
Транзакции.
Безопасность.


Избегайте чрезмерного использования AOP, чтобы не усложнять отладку.
Структурируйте Pointcut выражения, чтобы аспекты легко переиспользовались.
Тестируйте аспекты отдельно от основной бизнес-логики.

#Java #Training #Spring #AOP #CreateAOP
Аннотации @Before, @After, @Around

Spring AOP предоставляет различные типы советов (Advice), которые позволяют внедрять дополнительную логику в определённые точки выполнения программы.

Аннотация @Before

Аннотация @Before используется для выполнения кода перед вызовом метода.
Срабатывает до метода-цели, вне зависимости от его результата.
Используется для логирования, проверки входных данных, проверки прав доступа и т.д.


@Before("pointcut_expression")
public void adviceMethod(JoinPoint joinPoint) {
// Логика совета
}


Пример использования:
@Aspect
@Component
public class LoggingAspect {

@Before("execution(* com.example.service.UserService.*(..))")
public void logBeforeMethod(JoinPoint joinPoint) {
System.out.println("Before method: " + joinPoint.getSignature().getName());
}
}


Нюансы:
Не контролирует выполнение метода: метод-цель будет вызван независимо от совета.
Не может перехватывать результат или исключение.


Аннотация @After

Аннотация @After используется для выполнения кода после завершения метода, независимо от того, успешно он завершился или выбросил исключение.
Полезна для освобождения ресурсов, записи логов.

@After("pointcut_expression")
public void adviceMethod(JoinPoint joinPoint) {
// Логика совета
}


Пример использования:
@Aspect
@Component
public class CleanupAspect {

@After("execution(* com.example.service.UserService.*(..))")
public void cleanupAfterMethod(JoinPoint joinPoint) {
System.out.println("After method: " + joinPoint.getSignature().getName());
}
}


Нюансы:
Всегда срабатывает после метода, даже если метод выбросил исключение.
Используется для логирования и операций, которые должны быть выполнены в любом случае.


Аннотация @Around

Аннотация @Around оборачивает вызов метода-цели и позволяет полностью контролировать его выполнение.
Может изменять параметры метода и результат.
Позволяет управлять выполнением метода, включая его пропуск или повторное выполнение.
Используется для измерения времени выполнения, транзакционного управления и других сложных сценариев.


@Around("pointcut_expression")
public Object adviceMethod(ProceedingJoinPoint joinPoint) throws Throwable {
// Логика до метода
Object result = joinPoint.proceed(); // Выполнение метода-цели
// Логика после метода
return result;
}


Пример использования:
@Aspect
@Component
public class PerformanceAspect {

@Around("execution(* com.example.service.UserService.*(..))")
public Object measureExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();

Object result = joinPoint.proceed(); // Выполняем метод

long executionTime = System.currentTimeMillis() - start;
System.out.println(joinPoint.getSignature() + " executed in " + executionTime + "ms");

return result;
}
}


Нюансы:
Требует вызова proceed() для выполнения метода-цели.
Может модифицировать результат или перехватывать исключения.
Самая мощная из всех аннотаций, но требует осторожного использования.


#Java #Training #Spring #AOP #Before #After #Around
Примеры аспектов

1. Аспект логирования

Этот аспект логирует вызовы методов до и после их выполнения.
@Aspect
@Component
public class LoggingAspect {

@Before("execution(* com.example.service.UserService.*(..))")
public void logBefore(JoinPoint joinPoint) {
System.out.println("Before method: " + joinPoint.getSignature().getName());
}

@After("execution(* com.example.service.UserService.*(..))")
public void logAfter(JoinPoint joinPoint) {
System.out.println("After method: " + joinPoint.getSignature().getName());
}
}


2. Аспект измерения времени выполнения

Измеряет время, которое метод тратит на выполнение.
@Aspect
@Component
public class PerformanceAspect {

@Around("execution(* com.example.service.UserService.*(..))")
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();

Object proceed = joinPoint.proceed(); // Выполняем метод

long executionTime = System.currentTimeMillis() - start;
System.out.println(joinPoint.getSignature() + " executed in " + executionTime + "ms");

return proceed;
}
}


3. Аспект обработки исключений

Перехватывает и обрабатывает исключения, выбрасываемые методами.

@Aspect
@Component
public class ExceptionHandlingAspect {

@AfterThrowing(pointcut = "execution(* com.example.service.UserService.*(..))", throwing = "exception")
public void logException(JoinPoint joinPoint, Throwable exception) {
System.out.println("Exception in method: " + joinPoint.getSignature().getName());
System.out.println("Exception message: " + exception.getMessage());
}
}


4. Аспект для проверки авторизации

Проверяет, имеет ли пользователь доступ к выполнению метода.
@Aspect
@Component
public class SecurityAspect {

@Before("execution(* com.example.service.SensitiveService.*(..))")
public void checkAccess(JoinPoint joinPoint) {
System.out.println("Checking access before method: " + joinPoint.getSignature().getName());
// Проверка прав доступа
if (!isUserAuthorized()) {
throw new SecurityException("User not authorized");
}
}

private boolean isUserAuthorized() {
// Логика авторизации
return true; // Например, возвращаем всегда true
}
}


5. Аспект кеширования

Кеширует результат выполнения метода.
@Aspect
@Component
public class CachingAspect {

private final Map<String, Object> cache = new HashMap<>();

@Around("execution(* com.example.service.CacheableService.*(..))")
public Object cacheMethod(ProceedingJoinPoint joinPoint) throws Throwable {
String key = joinPoint.getSignature().toString() + Arrays.toString(joinPoint.getArgs());

if (cache.containsKey(key)) {
System.out.println("Returning cached result for " + key);
return cache.get(key);
}

Object result = joinPoint.proceed(); // Выполнение метода
cache.put(key, result);
System.out.println("Caching result for " + key);

return result;
}
}


#Java #Training #Spring #AOP
Pointcut Expressions

Pointcut Expressions — это важный элемент AOP, позволяющий указать, где именно в коде должен срабатывать аспект. С их помощью можно точно определить, какие методы или классы будут подвержены сквозной функциональности.

1. Что такое Pointcut Expressions?

Pointcut — это выражение, определяющее набор точек соединения (Joinpoints), к которым будет применяться аспект. В Spring Pointcut Expressions основываются на синтаксисе AspectJ, что позволяет гибко описывать нужные методы или классы.

2. Основные типы Pointcut Expressions

execution:
Самый часто используемый тип, который указывает выполнение метода.
execution(modifiers-pattern? return-type-pattern declaring-type-pattern? method-name-pattern(param-pattern) throws-pattern?)


Все методы в классе UserService:
execution(* com.example.service.UserService.*(..))


Методы, возвращающие void:
execution(void com.example.service.*.*(..))


Методы с определённым набором параметров:

execution(* com.example.service.UserService.findBy*(String, int))


within:
Указывает на методы, принадлежащие определённому классу или пакету.
within(com.example.service.UserService)


@annotation:
Указывает на методы, которые помечены определённой аннотацией.
@annotation(org.springframework.transaction.annotation.Transactional)


args:
Указывает на методы, принимающие параметры определённого типа.
args(java.lang.String, int)


this / target:
this: срабатывает на прокси-объекты.
target: срабатывает на исходные объекты.

this(com.example.service.UserService)
target(com.example.service.UserService)


3. Комбинирование Pointcut Expressions

Pointcut выражения можно комбинировать с логическими операторами:
&& — И.
|| — ИЛИ.
! — НЕ.


Пример:
@Pointcut("execution(* com.example.service.*.*(..)) && @annotation(org.springframework.transaction.annotation.Transactional)")
public void transactionalMethods() {}


4. Использование Pointcut в аспектах

Определение Pointcut:
@Aspect
@Component
public class LoggingAspect {

@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceLayerMethods() {}

@Before("serviceLayerMethods()")
public void logBefore(JoinPoint joinPoint) {
System.out.println("Before method: " + joinPoint.getSignature().getName());
}
}


Применение к методам с аннотацией:
@Before("@annotation(com.example.annotations.Loggable)")
public void logForAnnotatedMethods(JoinPoint joinPoint) {
System.out.println("Logging for annotated method: " + joinPoint.getSignature().getName());
}


#Java #Training #Spring #AOP #Pointcut_Expressions
Настройка AOP через XML и аннотации

Spring AOP предоставляет два способа конфигурации: через XML и с помощью аннотаций.

1. Настройка AOP через XML

Ранее в Spring было популярно настраивать
AOP через XML-конфигурацию.

Шаги настройки

Добавьте зависимость AOP в pom.xml:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
</dependency>


Создайте аспект:
public class LoggingAspect {
public void logBefore() {
System.out.println("Logging before method execution...");
}
}


Определите Pointcut и Advice в XML:
<aop:config>
<aop:aspect id="loggingAspect" ref="loggingAspectBean">
<aop:pointcut id="serviceMethods" expression="execution(* com.example.service.*.*(..))" />
<aop:before method="logBefore" pointcut-ref="serviceMethods" />
</aop:aspect>
</aop:config>


Объявите аспект в applicationContext.xml:
<bean id="loggingAspectBean" class="com.example.aspect.LoggingAspect" />


2. Настройка AOP с помощью аннотаций

Настройка через аннотации является более современным и удобным подходом.

Шаги настройки

Добавьте зависимость:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>


Включите поддержку AOP в приложении:
@Configuration
@EnableAspectJAutoProxy
public class AppConfig {}


Создайте аспект:
@Aspect
@Component
public class LoggingAspect {

@Before("execution(* com.example.service.*.*(..))")
public void logBefore(JoinPoint joinPoint) {
System.out.println("Before method: " + joinPoint.getSignature().getName());
}

@After("execution(* com.example.service.*.*(..))")
public void logAfter(JoinPoint joinPoint) {
System.out.println("After method: " + joinPoint.getSignature().getName());
}

@Around("execution(* com.example.service.*.*(..))")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("Around - Before method: " + joinPoint.getSignature().getName());
Object result = joinPoint.proceed();
System.out.println("Around - After method: " + joinPoint.getSignature().getName());
return result;
}
}


Пример сервиса:
@Service
public class UserService {
public String getUserById(Long id) {
System.out.println("Fetching user with ID: " + id);
return "User" + id;
}
}


#Java #Training #Spring #AOP
Практическое использование AOP для логирования

Логирование является одной из самых распространённых задач, где применяют AOP. Вместо того чтобы добавлять логи в каждый метод вручную, можно создать аспект, который автоматически будет логировать информацию о вызовах методов, их параметрах, результатах и времени выполнения.

1. Задача: Логирование вызовов методов

Cоздадим аспект, который будет:
Логировать вызовы методов сервисного слоя.
Выводить информацию о параметрах методов.
Логировать результат выполнения.
Замерять время выполнения метода.


Шаг 1: Зависимости

Добавьте в pom.xml зависимости для работы с
AOP:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>


Шаг 2: Создание аспекта логирования
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;

@Aspect
@Component
public class LoggingAspect {

// Логируем все методы в сервисном слое
@Around("execution(* com.example.service.*.*(..))")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
long startTime = System.currentTimeMillis();

System.out.println("Method called: " + joinPoint.getSignature());
System.out.println("Arguments: " + Arrays.toString(joinPoint.getArgs()));

Object result;
try {
result = joinPoint.proceed(); // Выполняем метод
System.out.println("Method result: " + result);
} catch (Throwable ex) {
System.out.println("Method threw an exception: " + ex.getMessage());
throw ex;
}

long elapsedTime = System.currentTimeMillis() - startTime;
System.out.println("Method execution time: " + elapsedTime + " ms");

return result;
}
}


Шаг 3: Бизнес-логика


Создадим простой сервис для тестирования:
import org.springframework.stereotype.Service;

@Service
public class UserService {

public String getUserById(Long id) {
System.out.println("Fetching user from database...");
return "User" + id;
}

public void updateUser(Long id, String name) {
System.out.println("Updating user in database...");
}
}


Шаг 4: Проверка

Теперь вызов методов UserService будет автоматически логироваться:
@RestController
public class UserController {

private final UserService userService;

public UserController(UserService userService) {
this.userService = userService;
}

@GetMapping("/users/{id}")
public String getUser(@PathVariable Long id) {
return userService.getUserById(id);
}

@PutMapping("/users/{id}")
public void updateUser(@PathVariable Long id, @RequestBody String name) {
userService.updateUser(id, name);
}
}


Пример лога:
Method called: String com.example.service.UserService.getUserById(Long)
Arguments: [1]
Fetching user from database...
Method result: User1
Method execution time: 5 ms


#Java #Training #Spring #AOP #AOP_for_logging
Проверка авторизации для выполнения методов

Создадим аспект, который будет:
Проверять, имеет ли пользователь доступ к выполнению метода.
Блокировать вызов метода, если доступ запрещён.


Шаг 1: Создание аннотации для ограничения доступа
import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Secured {
String role(); // Роль, необходимая для доступа
}


Шаг 2: Аспект для проверки безопасности
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.JoinPoint;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class SecurityAspect {

// Перехватываем методы с аннотацией @Secured
@Before("@annotation(secured)")
public void checkAccess(JoinPoint joinPoint, Secured secured) {
String requiredRole = secured.role();
String userRole = getCurrentUserRole(); // Эмуляция получения роли пользователя

if (!userRole.equals(requiredRole)) {
throw new SecurityException("Access denied: required role is " + requiredRole);
}

System.out.println("Access granted to method: " + joinPoint.getSignature());
}

private String getCurrentUserRole() {
// Для примера, возвращаем роль текущего пользователя
return "USER"; // Например, текущая роль пользователя — "USER"
}
}


Шаг 3: Применение аннотации Secured


Теперь можно использовать аннотацию @Secured для методов, которые требуют проверки безопасности.
import org.springframework.stereotype.Service;

@Service
public class AdminService {

@Secured(role = "ADMIN")
public void deleteUser(Long id) {
System.out.println("Deleting user with ID: " + id);
}

@Secured(role = "USER")
public String getUserData(Long id) {
System.out.println("Fetching user data...");
return "User data for ID: " + id;
}
}


Шаг 4: Проверка

Создадим контроллер для вызова методов:
@RestController
public class AdminController {

private final AdminService adminService;

public AdminController(AdminService adminService) {
this.adminService = adminService;
}

@DeleteMapping("/admin/users/{id}")
public void deleteUser(@PathVariable Long id) {
adminService.deleteUser(id); // Проверка роли ADMIN
}

@GetMapping("/admin/users/{id}")
public String getUserData(@PathVariable Long id) {
return adminService.getUserData(id); // Проверка роли USER
}
}


Если вызвать метод deleteUser с текущей ролью USER, выбросится исключение:
Access denied: required role is ADMIN


Шаг 5: Логирование в аспекте


Вы можете расширить аспект, добавив логирование успешных или неуспешных проверок безопасности:

@Before("@annotation(secured)")
public void checkAccess(JoinPoint joinPoint, Secured secured) {
String requiredRole = secured.role();
String userRole = getCurrentUserRole();

if (!userRole.equals(requiredRole)) {
System.out.println("Access denied for method: " + joinPoint.getSignature());
throw new SecurityException("Access denied: required role is " + requiredRole);
}

System.out.println("Access granted for method: " + joinPoint.getSignature());
}


#Java #Training #Spring #AOP #AOP_for_security
Транзакционный менеджмент с помощью AOP

Транзакционный менеджмент — это одна из ключевых функций, где
AOP в Spring используется наиболее активно. С помощью AOP можно автоматически управлять транзакциями, устраняя необходимость вручную начинать, коммитить или откатывать транзакции в бизнес-логике.

1. Транзакционный менеджмент: Основы

Транзакция — это единица работы, которая либо выполняется полностью, либо откатывается при возникновении ошибки. Примером транзакции может быть:
Добавление пользователя в базу данных.
Перевод денег между счетами.


Роль
AOP в транзакционном менеджменте

Spring AOP позволяет автоматически обрабатывать транзакции:
Начинать транзакцию перед выполнением метода.
Фиксировать транзакцию после успешного выполнения.
Откатывать транзакцию при возникновении исключения.


Аннотация @Transactional

В Spring для работы с транзакциями используется аннотация @Transactional.

2. Пример использования транзакционного менеджмента

Шаг 1: Добавление зависимостей
Добавьте в pom.xml зависимости для Spring Data JPA и базы данных:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>


Шаг 2: Настройка базы данных
Добавьте настройки H2 в application.properties:
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect


Шаг 3: Бизнес-логика с транзакциями


Создайте сущность:
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;

@Entity
public class User {
@Id
@GeneratedValue
private Long id;
private String name;
private double balance;

// Getters and Setters
}


Репозиторий для работы с данными:
import org.springframework.data.jpa.repository.JpaRepository;

public interface UserRepository extends JpaRepository<User, Long> {}


Сервис с транзакционным методом:
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class UserService {
private final UserRepository userRepository;

public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}

@Transactional
public void transferMoney(Long fromUserId, Long toUserId, double amount) {
User fromUser = userRepository.findById(fromUserId)
.orElseThrow(() -> new RuntimeException("User not found"));
User toUser = userRepository.findById(toUserId)
.orElseThrow(() -> new RuntimeException("User not found"));

fromUser.setBalance(fromUser.getBalance() - amount);
toUser.setBalance(toUser.getBalance() + amount);

userRepository.save(fromUser);
userRepository.save(toUser);

if (amount > 1000) {
throw new RuntimeException("Transfer limit exceeded!");
}
}
}


Шаг 4: Проверка

Создайте тестовый контроллер:
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/users")
public class UserController {
private final UserService userService;

public UserController(UserService userService) {
this.userService = userService;
}

@PostMapping("/transfer")
public void transferMoney(@RequestParam Long fromUserId, @RequestParam Long toUserId, @RequestParam double amount) {
userService.transferMoney(fromUserId, toUserId, amount);
}
}
Теперь, если перевести более 1000 единиц, метод выбросит исключение, а все изменения в базе данных будут откатаны.


#Java #Training #Spring #AOP #AOP_Transaction
Все аннотации AOP и их использование

1. @Aspect

Обозначает класс как аспект.
@Aspect
@Component
public class LoggingAspect {
// Pointcut и Advice
}


2. @Pointcut

Определяет срез, на который будет ссылаться Advice.
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceLayer() {}


3. @Before

Совет, выполняющийся перед методом.
@Before("serviceLayer()")
public void logBefore() {
System.out.println("Before method");
}


4. @After

Совет, выполняющийся после метода, независимо от его результата.

@After("serviceLayer()")
public void logAfter() {
System.out.println("After method");
}


5. @AfterReturning

Совет, выполняющийся после успешного выполнения метода.
@AfterReturning(pointcut = "serviceLayer()", returning = "result")
public void logAfterReturning(Object result) {
System.out.println("Method returned: " + result);
}


6. @AfterThrowing

Совет, выполняющийся при выбросе исключения.
@AfterThrowing(pointcut = "serviceLayer()", throwing = "ex")
public void logAfterThrowing(Exception ex) {
System.out.println("Exception thrown: " + ex.getMessage());
}


7. @Around

Оборачивает метод, позволяя контролировать его выполнение.

@Around("serviceLayer()")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("Before method");
Object result = joinPoint.proceed();
System.out.println("After method");
return result;
}


8.
@EnableAspectJAutoProxy

Включает поддержку AOP в приложении.
@Configuration
@EnableAspectJAutoProxy
public class AppConfig {}


#Java #Training #Spring #AOP #AOP_Annotations