From 4d46f058c314a3c67d1db584dd0a3755b5e0c101 Mon Sep 17 00:00:00 2001 From: 0samsung0 <78357262+0samsung0@users.noreply.github.com> Date: Mon, 19 May 2025 17:35:02 +0300 Subject: [PATCH 1/5] add LoggingAspect --- pom.xml | 6 ++++- .../samples/petclinic/LoggingAspect.java | 27 +++++++++++++++++++ .../samples/petclinic/owner/Visit.java | 9 +++---- 3 files changed, 36 insertions(+), 6 deletions(-) create mode 100644 src/main/java/org/springframework/samples/petclinic/LoggingAspect.java diff --git a/pom.xml b/pom.xml index 27fb9a6bd..b9c62ccf7 100644 --- a/pom.xml +++ b/pom.xml @@ -18,7 +18,7 @@ - 17 + 21 UTF-8 UTF-8 io.projectreactor diff --git a/src/main/java/org/springframework/samples/petclinic/LoggingAspect.java b/src/main/java/org/springframework/samples/petclinic/LoggingAspect.java new file mode 100644 index 000000000..651a5421d --- /dev/null +++ b/src/main/java/org/springframework/samples/petclinic/LoggingAspect.java @@ -0,0 +1,27 @@ +package org.springframework.samples.petclinic; + + +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.springframework.stereotype.Component; + +@Aspect +@Component +public class LoggingAspect { + + //Pointcut: all methods in package "model" + @Before("execution(* model.BaseEntity.getId())") + public void logBeforeMethod(){ + System.out.println("model.BaseEntity.getId..."); + } + + @Before("execution(* model.NamedEntity.getName())") + public void logBeforeMethod2(){ + System.out.println("model.NamedEntity.getName..."); + } + + @Before("execution(* model.Person.*())") + public void logBeforeMethod3(){ + System.out.println("model.Person"); + } +} diff --git a/src/main/java/org/springframework/samples/petclinic/owner/Visit.java b/src/main/java/org/springframework/samples/petclinic/owner/Visit.java index 35569bdaa..3e0873748 100644 --- a/src/main/java/org/springframework/samples/petclinic/owner/Visit.java +++ b/src/main/java/org/springframework/samples/petclinic/owner/Visit.java @@ -15,15 +15,14 @@ */ package org.springframework.samples.petclinic.owner; -import java.time.LocalDate; - -import org.springframework.format.annotation.DateTimeFormat; -import org.springframework.samples.petclinic.model.BaseEntity; - import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.Table; import jakarta.validation.constraints.NotBlank; +import org.springframework.format.annotation.DateTimeFormat; +import org.springframework.samples.petclinic.model.BaseEntity; + +import java.time.LocalDate; /** * Simple JavaBean domain object representing a visit. From 8393b074fc55d7c53bfe456caf3ff1affd88a926 Mon Sep 17 00:00:00 2001 From: 0samsung0 <78357262+0samsung0@users.noreply.github.com> Date: Mon, 19 May 2025 17:47:39 +0300 Subject: [PATCH 2/5] add LoggingAspect --- .../org/springframework/samples/petclinic/LoggingAspect.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/springframework/samples/petclinic/LoggingAspect.java b/src/main/java/org/springframework/samples/petclinic/LoggingAspect.java index 651a5421d..ba088a772 100644 --- a/src/main/java/org/springframework/samples/petclinic/LoggingAspect.java +++ b/src/main/java/org/springframework/samples/petclinic/LoggingAspect.java @@ -22,6 +22,6 @@ public class LoggingAspect { @Before("execution(* model.Person.*())") public void logBeforeMethod3(){ - System.out.println("model.Person"); + System.out.println("model.Person*"); } } From 0a43a7a311ee16183516abdcb68162a5667d8e69 Mon Sep 17 00:00:00 2001 From: 0samsung0 <78357262+0samsung0@users.noreply.github.com> Date: Tue, 20 May 2025 18:40:35 +0300 Subject: [PATCH 3/5] add AOP for logging --- .../samples/petclinic/LoggingAspect.java | 41 +++++++++++-------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/src/main/java/org/springframework/samples/petclinic/LoggingAspect.java b/src/main/java/org/springframework/samples/petclinic/LoggingAspect.java index ba088a772..95fcbbdc4 100644 --- a/src/main/java/org/springframework/samples/petclinic/LoggingAspect.java +++ b/src/main/java/org/springframework/samples/petclinic/LoggingAspect.java @@ -1,27 +1,34 @@ package org.springframework.samples.petclinic; - +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; -import org.aspectj.lang.annotation.Before; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; @Aspect @Component public class LoggingAspect { + + private static final Logger logger = LoggerFactory.getLogger(LoggingAspect.class); - //Pointcut: all methods in package "model" - @Before("execution(* model.BaseEntity.getId())") - public void logBeforeMethod(){ - System.out.println("model.BaseEntity.getId..."); - } - - @Before("execution(* model.NamedEntity.getName())") - public void logBeforeMethod2(){ - System.out.println("model.NamedEntity.getName..."); - } - - @Before("execution(* model.Person.*())") - public void logBeforeMethod3(){ - System.out.println("model.Person*"); - } + @Around("execution(* org.springframework.samples.petclinic..*.*(..))") + public Object logMethodExecution(ProceedingJoinPoint joinPoint) throws Throwable { + String methodName = joinPoint.getSignature().getName(); + String className = joinPoint.getTarget().getClass().getSimpleName(); + + logger.info("Начало выполнения метода: {}.{}", className, methodName); + + long startTime = System.currentTimeMillis(); + Object result = null; + try { + result = joinPoint.proceed(); + return result; + } finally { + long endTime = System.currentTimeMillis(); + logger.info("Завершение выполнения метода: {}.{} (время выполнения: {} мс)", + className, methodName, (endTime - startTime)); + } + } } From c037c698a709ee9ab14e3f9dbeda0b3dd08a5ab7 Mon Sep 17 00:00:00 2001 From: 0samsung0 <78357262+0samsung0@users.noreply.github.com> Date: Sat, 31 May 2025 18:12:18 +0300 Subject: [PATCH 4/5] add tests for logging class --- .../samples/petclinic/LoggingAspect.java | 22 ++++++--- .../samples/petclinic/LoggingAspectTest.java | 48 +++++++++++++++++++ 2 files changed, 64 insertions(+), 6 deletions(-) create mode 100644 src/test/java/org/springframework/samples/petclinic/LoggingAspectTest.java diff --git a/src/main/java/org/springframework/samples/petclinic/LoggingAspect.java b/src/main/java/org/springframework/samples/petclinic/LoggingAspect.java index 95fcbbdc4..081e2c257 100644 --- a/src/main/java/org/springframework/samples/petclinic/LoggingAspect.java +++ b/src/main/java/org/springframework/samples/petclinic/LoggingAspect.java @@ -7,28 +7,38 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; +import java.util.function.BiFunction; +import java.util.function.Supplier; + @Aspect @Component public class LoggingAspect { private static final Logger logger = LoggerFactory.getLogger(LoggingAspect.class); + @FunctionalInterface + private interface MethodLogger { + void log(String className, String methodName, long executionTime); + } + + private final MethodLogger defaultLogger = (className, methodName, executionTime) -> + logger.info("Method {}.{} completed in {} ms", className, methodName, executionTime); + @Around("execution(* org.springframework.samples.petclinic..*.*(..))") public Object logMethodExecution(ProceedingJoinPoint joinPoint) throws Throwable { String methodName = joinPoint.getSignature().getName(); String className = joinPoint.getTarget().getClass().getSimpleName(); - logger.info("Начало выполнения метода: {}.{}", className, methodName); + Supplier methodInfo = () -> String.format("%s.%s", className, methodName); + logger.info("Starting method: {}", methodInfo.get()); long startTime = System.currentTimeMillis(); - Object result = null; try { - result = joinPoint.proceed(); + Object result = joinPoint.proceed(); return result; } finally { - long endTime = System.currentTimeMillis(); - logger.info("Завершение выполнения метода: {}.{} (время выполнения: {} мс)", - className, methodName, (endTime - startTime)); + long executionTime = System.currentTimeMillis() - startTime; + defaultLogger.log(className, methodName, executionTime); } } } diff --git a/src/test/java/org/springframework/samples/petclinic/LoggingAspectTest.java b/src/test/java/org/springframework/samples/petclinic/LoggingAspectTest.java new file mode 100644 index 000000000..a0d49bf51 --- /dev/null +++ b/src/test/java/org/springframework/samples/petclinic/LoggingAspectTest.java @@ -0,0 +1,48 @@ +package org.springframework.samples.petclinic; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.slf4j.Logger; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; + +import static org.mockito.Mockito.*; + +@ExtendWith({SpringExtension.class, MockitoExtension.class}) +@SpringJUnitConfig +public class LoggingAspectTest { + + @Mock + private Logger logger; + + @InjectMocks + private LoggingAspect loggingAspect; + + @Test + public void testLogMethodExecution() throws Throwable { + // Создаем тестовый метод для проверки + TestService testService = new TestService(); + + // Вызываем метод, который должен быть перехвачен аспектом + testService.testMethod(); + + // Проверяем, что логирование было вызвано + verify(logger, atLeastOnce()).info(contains("Starting method:")); + verify(logger, atLeastOnce()).info(contains("Method TestService.testMethod completed in")); + } + + // Вспомогательный класс для тестирования + private static class TestService { + public void testMethod() { + // Простой метод для тестирования + try { + Thread.sleep(100); // Имитируем некоторую работу + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + } +} \ No newline at end of file From d359225981563e12d377c04d3c8c2d2d1936c184 Mon Sep 17 00:00:00 2001 From: 0samsung0 <78357262+0samsung0@users.noreply.github.com> Date: Sat, 31 May 2025 18:22:45 +0300 Subject: [PATCH 5/5] add cache and test for that Signed-off-by: 0samsung0 <78357262+0samsung0@users.noreply.github.com> --- .../samples/petclinic/cache/CacheConfig.java | 19 +++++++ .../petclinic/cache/CacheableService.java | 36 ++++++++++++ .../petclinic/cache/CacheableServiceTest.java | 55 +++++++++++++++++++ 3 files changed, 110 insertions(+) create mode 100644 src/main/java/org/springframework/samples/petclinic/cache/CacheConfig.java create mode 100644 src/main/java/org/springframework/samples/petclinic/cache/CacheableService.java create mode 100644 src/test/java/org/springframework/samples/petclinic/cache/CacheableServiceTest.java diff --git a/src/main/java/org/springframework/samples/petclinic/cache/CacheConfig.java b/src/main/java/org/springframework/samples/petclinic/cache/CacheConfig.java new file mode 100644 index 000000000..f5e22317e --- /dev/null +++ b/src/main/java/org/springframework/samples/petclinic/cache/CacheConfig.java @@ -0,0 +1,19 @@ +package org.springframework.samples.petclinic.cache; + +import org.springframework.cache.CacheManager; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.cache.concurrent.ConcurrentMapCacheManager; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +@EnableCaching +public class CacheConfig { + + @Bean + public CacheManager cacheManager() { + ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager(); + cacheManager.setCacheNames(java.util.Arrays.asList("dataCache")); + return cacheManager; + } +} \ No newline at end of file diff --git a/src/main/java/org/springframework/samples/petclinic/cache/CacheableService.java b/src/main/java/org/springframework/samples/petclinic/cache/CacheableService.java new file mode 100644 index 000000000..d91276a11 --- /dev/null +++ b/src/main/java/org/springframework/samples/petclinic/cache/CacheableService.java @@ -0,0 +1,36 @@ +package org.springframework.samples.petclinic.cache; + +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +@Service +public class CacheableService { + + private final Map dataStore = new HashMap<>(); + + public CacheableService() { + // Инициализация тестовых данных + dataStore.put("key1", "value1"); + dataStore.put("key2", "value2"); + dataStore.put("key3", "value3"); + } + + @Cacheable(value = "dataCache", key = "#key") + public String getData(String key) { + // Имитация задержки при получении данных + try { + TimeUnit.MILLISECONDS.sleep(100); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + return dataStore.get(key); + } + + public void updateData(String key, String value) { + dataStore.put(key, value); + } +} \ No newline at end of file diff --git a/src/test/java/org/springframework/samples/petclinic/cache/CacheableServiceTest.java b/src/test/java/org/springframework/samples/petclinic/cache/CacheableServiceTest.java new file mode 100644 index 000000000..5e537a8bc --- /dev/null +++ b/src/test/java/org/springframework/samples/petclinic/cache/CacheableServiceTest.java @@ -0,0 +1,55 @@ +package org.springframework.samples.petclinic.cache; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cache.CacheManager; + +import static org.junit.jupiter.api.Assertions.*; + +@SpringBootTest +public class CacheableServiceTest { + + @Autowired + private CacheableService cacheableService; + + @Autowired + private CacheManager cacheManager; + + @Test + public void testCacheHit() { + // Первый вызов - кэш промах + long startTime1 = System.currentTimeMillis(); + String result1 = cacheableService.getData("key1"); + long duration1 = System.currentTimeMillis() - startTime1; + + // Второй вызов - должен быть кэш хит + long startTime2 = System.currentTimeMillis(); + String result2 = cacheableService.getData("key1"); + long duration2 = System.currentTimeMillis() - startTime2; + + // Проверяем результаты + assertEquals("value1", result1); + assertEquals("value1", result2); + + // Второй вызов должен быть быстрее из-за кэширования + assertTrue(duration2 < duration1); + } + + @Test + public void testCacheMiss() { + // Проверяем несуществующий ключ + String result = cacheableService.getData("nonExistentKey"); + assertNull(result); + } + + @Test + public void testCacheUpdate() { + // Обновляем значение + cacheableService.updateData("key1", "newValue"); + + // Получаем обновленное значение + String result = cacheableService.getData("key1"); + assertEquals("newValue", result); + } +} \ No newline at end of file