mirror of
https://github.com/spring-projects/spring-petclinic.git
synced 2026-01-18 17:41:11 +00:00
Merge d359225981 into 3a931080d4
This commit is contained in:
commit
9156eea85c
7 changed files with 211 additions and 6 deletions
6
pom.xml
6
pom.xml
|
|
@ -18,7 +18,7 @@
|
|||
<properties>
|
||||
|
||||
<!-- Generic properties -->
|
||||
<java.version>17</java.version>
|
||||
<java.version>21</java.version>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
<!-- Important for reproducible builds. Update using e.g. ./mvnw versions:set
|
||||
|
|
@ -71,6 +71,10 @@
|
|||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-aop</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<!-- Workaround for AOT issue (https://github.com/spring-projects/spring-framework/pull/33949) -->
|
||||
<groupId>io.projectreactor</groupId>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
package org.springframework.samples.petclinic;
|
||||
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
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();
|
||||
|
||||
Supplier<String> methodInfo = () -> String.format("%s.%s", className, methodName);
|
||||
logger.info("Starting method: {}", methodInfo.get());
|
||||
|
||||
long startTime = System.currentTimeMillis();
|
||||
try {
|
||||
Object result = joinPoint.proceed();
|
||||
return result;
|
||||
} finally {
|
||||
long executionTime = System.currentTimeMillis() - startTime;
|
||||
defaultLogger.log(className, methodName, executionTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
19
src/main/java/org/springframework/samples/petclinic/cache/CacheConfig.java
vendored
Normal file
19
src/main/java/org/springframework/samples/petclinic/cache/CacheConfig.java
vendored
Normal file
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
36
src/main/java/org/springframework/samples/petclinic/cache/CacheableService.java
vendored
Normal file
36
src/main/java/org/springframework/samples/petclinic/cache/CacheableService.java
vendored
Normal file
|
|
@ -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<String, String> 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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
55
src/test/java/org/springframework/samples/petclinic/cache/CacheableServiceTest.java
vendored
Normal file
55
src/test/java/org/springframework/samples/petclinic/cache/CacheableServiceTest.java
vendored
Normal file
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue