Для чего нужна аннотация Service в Spring?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
# Аннотация @Service в Spring Framework
@Service — это специализированная аннотация Spring, которая используется для маркирования класса как сервиса (Service Layer). Она является подтипом аннотации @Component и призвана повысить читаемость и организацию кода.
Что такое @Service
1. Основное назначение
@Service — это аннотация-маркер, которая сообщает Spring, что класс является сервисом бизнес-логики приложения. Spring автоматически создаёт bean этого класса и управляет его жизненным циклом.
import org.springframework.stereotype.Service;
@Service
public class UserService {
// Это автоматически создаст bean типа UserService
// и поместит его в Spring Application Context
}
Иерархия аннотаций
@Component (базовая аннотация)
|
+-- @Service (для Service Layer)
+-- @Repository (для Data Access Layer)
+-- @Controller (для Presentation Layer)
+-- @RestController (для REST Presentation Layer)
2. Управление жизненным циклом bean'а
Spring автоматически управляет созданием, инициализацией и удалением bean'а:
@Service
public class OrderService {
@Autowired
private OrderRepository orderRepository;
@PostConstruct
public void init() {
System.out.println("OrderService инициализирован");
}
public Order createOrder(OrderRequest request) {
return orderRepository.save(new Order(request));
}
@PreDestroy
public void cleanup() {
System.out.println("OrderService удаляется");
}
}
3. Внедрение зависимостей (Dependency Injection)
@Service позволяет Spring автоматически внедрять зависимости через @Autowired:
@Service
public class PaymentService {
@Autowired
private OrderRepository orderRepository;
@Autowired
private NotificationService notificationService;
@Autowired
private PaymentGateway paymentGateway;
public void processPayment(Long orderId) {
Order order = orderRepository.findById(orderId)
.orElseThrow(() -> new OrderNotFoundException(orderId));
// Используем внедрённые зависимости
paymentGateway.charge(order.getTotal());
notificationService.sendConfirmation(order);
}
}
4. Управление транзакциями
@Service часто используется с @Transactional для управления транзакциями БД:
@Service
@Transactional
public class TransferService {
@Autowired
private AccountRepository accountRepository;
// Все методы этого сервиса будут выполняться в транзакции
public void transferMoney(Long fromId, Long toId, BigDecimal amount) {
Account from = accountRepository.findById(fromId).orElseThrow();
Account to = accountRepository.findById(toId).orElseThrow();
from.debit(amount);
to.credit(amount);
// Если произойдёт исключение, обе операции откатятся
}
@Transactional(readOnly = true)
public BigDecimal getBalance(Long accountId) {
return accountRepository.findById(accountId)
.map(Account::getBalance)
.orElse(BigDecimal.ZERO);
}
}
5. Область действия (Scope) bean'а
По умолчанию @Service создаёт Singleton bean, но можно изменить область действия:
// Singleton (по умолчанию) — одна копия на всё приложение
@Service
public class UserService {
}
// Prototype — новая копия при каждом внедрении
@Service
@Scope("prototype")
public class PrototypeService {
}
// Request scope — новая копия для каждого HTTP запроса
@Service
@Scope("request")
public class RequestScopedService {
}
// Session scope — новая копия для каждой сессии пользователя
@Service
@Scope("session")
public class SessionScopedService {
}
6. Реальный пример: многоуровневая архитектура
// Repository Layer
@Repository
public class UserRepository extends JpaRepository<User, Long> {
}
// Service Layer
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
public User createUser(UserRequest request) {
// Валидация
if (userRepository.existsByEmail(request.getEmail())) {
throw new UserAlreadyExistsException(request.getEmail());
}
// Бизнес-логика
User user = new User();
user.setEmail(request.getEmail());
user.setPassword(encodePassword(request.getPassword()));
return userRepository.save(user);
}
public Optional<User> getUser(Long id) {
return userRepository.findById(id);
}
}
// Controller Layer
@RestController
@RequestMapping("/api/users")
public class UserController {
@Autowired
private UserService userService;
@PostMapping
public ResponseEntity<User> createUser(@RequestBody UserRequest request) {
User user = userService.createUser(request);
return ResponseEntity.status(HttpStatus.CREATED).body(user);
}
@GetMapping("/{id}")
public ResponseEntity<User> getUser(@PathVariable Long id) {
return userService.getUser(id)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
}
7. Service с несколькими репозиториями
@Service
public class OrderService {
@Autowired
private OrderRepository orderRepository;
@Autowired
private ItemRepository itemRepository;
@Autowired
private CustomerRepository customerRepository;
@Transactional
public Order createOrder(Long customerId, List<OrderItem> items) {
// Проверяем существование клиента
Customer customer = customerRepository.findById(customerId)
.orElseThrow(() -> new CustomerNotFoundException(customerId));
// Создаём заказ
Order order = new Order();
order.setCustomer(customer);
order.setStatus(OrderStatus.PENDING);
// Сохраняем товары
for (OrderItem item : items) {
item.setOrder(order);
itemRepository.save(item);
}
// Сохраняем заказ
return orderRepository.save(order);
}
}
8. Тестирование Service
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
@Mock
private UserRepository userRepository;
@InjectMocks
private UserService userService;
@Test
void testCreateUserSuccess() {
// Arrange
UserRequest request = new UserRequest("test@example.com", "password");
User expectedUser = new User("test@example.com", "hashedPassword");
when(userRepository.existsByEmail("test@example.com")).thenReturn(false);
when(userRepository.save(any(User.class))).thenReturn(expectedUser);
// Act
User result = userService.createUser(request);
// Assert
assertNotNull(result);
assertEquals("test@example.com", result.getEmail());
verify(userRepository).save(any(User.class));
}
@Test
void testCreateUserAlreadyExists() {
UserRequest request = new UserRequest("existing@example.com", "password");
when(userRepository.existsByEmail("existing@example.com")).thenReturn(true);
assertThrows(UserAlreadyExistsException.class,
() -> userService.createUser(request));
}
}
Преимущества использования @Service
- Читаемость кода — сразу видно, что класс отвечает за бизнес-логику
- Организация архитектуры — четкое разделение слоев приложения
- Управление жизненным циклом — Spring автоматически создаёт и удаляет bean'ы
- Dependency Injection — простое внедрение зависимостей
- Транзакционность — легко добавить @Transactional
- Тестируемость — легко мокировать зависимости
- Масштабируемость — легко добавлять новые сервисы
Резюме
@Service используется для:
- Маркирования класса как сервиса бизнес-логики
- Автоматического создания Spring bean'а
- Управления жизненным циклом объекта
- Внедрения зависимостей
- Организации кода в многоуровневую архитектуру
- Упрощения управления транзакциями и логирования