«1. Введение
Как разработчики программного обеспечения, мы всегда ищем передовой опыт использования данной технологии или библиотеки. Естественно, иногда возникают споры.
Один из таких споров касается размещения аннотации Spring @Service. Поскольку Spring предоставляет альтернативные способы определения bean-компонентов, стоит обратить внимание на расположение аннотаций стереотипов.
В этом уроке мы рассмотрим аннотацию @Service и выясним, как лучше всего ее поместить в интерфейсы, абстрактные классы или конкретные классы.
2. @Service на интерфейсах
Некоторые разработчики могут решить поместить @Service на интерфейсы, потому что они хотят:
-
Явно показать, что интерфейс должен использоваться только для целей уровня службы. Определить новые реализации службы и иметь они автоматически определяются как компоненты Spring во время запуска
Давайте посмотрим, как это будет выглядеть, если мы аннотируем интерфейс:
@Service
public interface AuthenticationService {
boolean authenticate(String username, String password);
}
Как мы заметили, AuthenticationService теперь становится более информативным. Знак @Service советует разработчикам использовать его только для служб бизнес-уровня, а не для уровня доступа к данным или любых других уровней.
Обычно это нормально, но есть недостаток. Помещая Spring @Service на интерфейсы, мы создаем дополнительную зависимость и связываем наши интерфейсы с внешней библиотекой.
Далее, чтобы протестировать автоопределение наших новых сервисных компонентов, давайте создадим реализацию нашего AuthenticationService:
public class InMemoryAuthenticationService implements AuthenticationService {
@Override
public boolean authenticate(String username, String password) {
//...
}
}
Обратите внимание, что наша новая реализация InMemoryAuthenticationService не имеет аннотации @Service. . Мы оставили @Service только на интерфейсе AuthenticationService.
Итак, давайте запустим наш контекст Spring с помощью базовой настройки Spring Boot:
@SpringBootApplication
public class AuthApplication {
@Autowired
private AuthenticationService authService;
public static void main(String[] args) {
SpringApplication.run(AuthApplication.class, args);
}
}
Когда мы запускаем наше приложение, мы получаем печально известное исключение NoSuchBeanDefinitionException, и контекст Spring не запускается:
org.springframework.beans.factory.NoSuchBeanDefinitionException:
No qualifying bean of type 'com.baeldung.annotations.service.interfaces.AuthenticationService' available:
expected at least 1 bean which qualifies as autowire candidate. Dependency annotations:
...
~ ~~ Следовательно, размещения @Service на интерфейсах недостаточно для автоматического обнаружения компонентов Spring.
3. @Service для абстрактных классов
Использование аннотации @Service для абстрактных классов не является обычным явлением.
Давайте проверим его, чтобы увидеть, достигает ли он нашей цели, заставляя Spring автоматически определять наши классы реализации.
Мы начнем с определения абстрактного класса с нуля и добавления к нему аннотации @Service:
@Service
public abstract class AbstractAuthenticationService {
public boolean authenticate(String username, String password) {
return false;
}
}
Затем мы расширим AbstractAuthenticationService, чтобы создать конкретную реализацию без аннотирования:
public class LdapAuthenticationService extends AbstractAuthenticationService {
@Override
public boolean authenticate(String username, String password) {
//...
}
}
Соответственно , мы также обновляем наше приложение AuthApplication, чтобы внедрить новый класс службы:
@SpringBootApplication
public class AuthApplication {
@Autowired
private AbstractAuthenticationService authService;
public static void main(String[] args) {
SpringApplication.run(AuthApplication.class, args);
}
}
Следует заметить, что мы не пытаемся внедрить абстрактный класс напрямую здесь, что невозможно. Вместо этого мы намерены получить экземпляр конкретного класса LdapAuthenticationService, зависящего только от абстрактного типа. Это хорошая практика, о чем также свидетельствует принцип замещения Лискова.
Итак, мы снова запускаем наше приложение AuthApplication:
org.springframework.beans.factory.NoSuchBeanDefinitionException:
No qualifying bean of type 'com.baeldung.annotations.service.abstracts.AbstractAuthenticationService' available:
expected at least 1 bean which qualifies as autowire candidate. Dependency annotations:
...
Как мы видим, контекст Spring не запускается. Это заканчивается тем же исключением NoSuchBeanDefinitionException.
Конечно, использование аннотации @Service для абстрактных классов не имеет никакого эффекта в Spring.
4. @Service для конкретных классов
Вопреки тому, что мы видели выше, довольно распространенной практикой является аннотирование классов реализации вместо абстрактных классов или интерфейсов.
Таким образом, наша цель в основном состоит в том, чтобы сообщить Spring, что этот класс будет @Component, и пометить его специальным стереотипом, которым в нашем случае является @Service.
Поэтому Spring автоматически обнаружит эти классы в пути к классам и автоматически определит их как управляемые компоненты.
Итак, давайте на этот раз поместим @Service в наши конкретные классы обслуживания. У нас будет один класс, реализующий наш интерфейс, и второй, расширяющий абстрактный класс, который мы определили ранее:
@Service
public class InMemoryAuthenticationService implements AuthenticationService {
@Override
public boolean authenticate(String username, String password) {
//...
}
}
@Service
public class LdapAuthenticationService extends AbstractAuthenticationService {
@Override
public boolean authenticate(String username, String password) {
//...
}
}
Здесь следует обратить внимание, что наша служба AbstractAuthenticationService не реализует здесь AuthenticationService. Следовательно, мы можем проверить их независимо.
Наконец, мы добавляем оба наших класса обслуживания в AuthApplication и пробуем:
@SpringBootApplication
public class AuthApplication {
@Autowired
private AuthenticationService inMemoryAuthService;
@Autowired
private AbstractAuthenticationService ldapAuthService;
public static void main(String[] args) {
SpringApplication.run(AuthApplication.class, args);
}
}
«
«Наш последний тест дает нам успешный результат, и контекст Spring загружается без каких-либо исключений. Обе службы автоматически регистрируются как bean-компоненты.
5. Результат
В конце концов, мы увидели, что единственный работающий способ — добавить @Service в наши классы реализации, чтобы сделать их автоматически обнаруживаемыми. Сканирование компонентов Spring не обнаруживает классы, если они не аннотированы отдельно, даже если они получены из другого аннотированного интерфейса @Service или абстрактного класса.
Кроме того, в документации Spring также указано, что использование @Service для классов реализации позволяет автоматически обнаруживать их при сканировании компонентов.
6. Заключение
В этой статье мы рассмотрели различные места использования аннотации Spring @Service и узнали, где хранить @Service для определения bean-компонентов Spring сервисного уровня, чтобы они автоматически обнаруживались при сканировании компонентов.
В частности, мы увидели, что размещение аннотации @Service на интерфейсах или абстрактных классах не имеет никакого эффекта и что только конкретные классы будут обнаружены при сканировании компонентов, когда они аннотированы @Service.