Рубрики
Без рубрики

Где Должна Храниться аннотация Spring @Service?

Автор оригинала: Yavuz Tas.

1. введение

Как разработчики программного обеспечения, мы всегда ищем лучшие практики использования данной технологии или библиотеки. Естественно, иногда возникают споры.

Одна из таких дискуссий касается размещения аннотации Spring @Service . Поскольку Spring предоставляет альтернативные способы определения бобов, стоит обратить внимание на местоположение аннотаций стереотипов.

В этом уроке мы рассмотрим аннотацию @Service и рассмотрим, лучше ли ее размещать на интерфейсах, абстрактных классах или конкретных классах .

2. @Service на интерфейсах

Некоторые разработчики могут решить поставить @Service на интерфейсы, потому что они хотят:

  • Явно покажите, что интерфейс должен использоваться только для целей уровня обслуживания
  • Определите новые реализации служб и автоматически определите их как компоненты Spring во время запуска

Давайте посмотрим, как это выглядит, если мы аннотируем интерфейс:

@Service
public interface AuthenticationService {

    boolean authenticate(String username, String password);
}

Как мы заметили, Служба аутентификации теперь становится более самоописательной. Знак @Service советует разработчикам использовать его только для служб бизнес-уровня, а не для уровня доступа к данным или любых других уровней.

Обычно это нормально, но есть один недостаток. Помещая @Service Spring в интерфейсы, мы создаем дополнительную зависимость и соединяем наши интерфейсы с внешней библиотекой.

Затем, чтобы проверить автоматическое обнаружение наших новых компонентов службы, давайте создадим реализацию нашей службы Аутентификации :

public class InMemoryAuthenticationService implements AuthenticationService {

    @Override
    public boolean authenticate(String username, String password) {
        //...
    }
}

Мы должны обратить внимание, что наша новая реализация, Служба аутентификации в памяти , не имеет аннотации @Service . Мы оставили @Service только на интерфейсе, Сервис аутентификации .

Итак, давайте запустим наш контекст Spring с помощью базовой настройки загрузки Spring:

@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 для абстрактных классов не является обычным явлением.

Давайте проверим его, чтобы увидеть, достигает ли он нашей цели-заставить Spring автоматически определять наши классы реализации.

Мы начнем с определения абстрактного класса с нуля и размещения на нем аннотации @Service :

@Service
public abstract class AbstractAuthenticationService {

    public boolean authenticate(String username, String password) {
        return false;
    }
}

Далее мы расширяем Абстрактную службу аутентификации до создания конкретной реализации без ее аннотирования :

public class LdapAuthenticationService extends AbstractAuthenticationService {

    @Override
    public boolean authenticate(String username, String password) { 
        //...
    }
}

Соответственно, мы также обновляем ваше приложение Auth , чтобы ввести новый класс обслуживания :

@SpringBootApplication
public class AuthApplication {

    @Autowired
    private AbstractAuthenticationService authService;

    public static void main(String[] args) {
        SpringApplication.run(AuthApplication.class, args);
    }
}

Мы должны заметить, что мы не пытаемся ввести абстрактный класс непосредственно здесь, что невозможно. Вместо этого мы намерены приобрести экземпляр конкретного класса Службы аутентификации Ldap , в зависимости только от абстрактного типа . Это хорошая практика, как также предполагает принцип замены Лисков .

Итак, мы снова запускаем наше приложение Auth :

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: 
...

Как мы видим, весенний контекст не начинается. Это заканчивается тем же NoSuchBeanDefinitionException исключением.

Конечно, использование @Service аннотации к абстрактным классам не имеет никакого эффекта весной .

4. @Сервис по конкретным классам

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

Таким образом, наша цель в основном состоит в том, чтобы сообщить 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) {
        //...
    }
}

Здесь мы должны обратить внимание на то, что наша Абстрактная служба аутентификации не реализует Службу аутентификации здесь. Следовательно, мы можем проверить их независимо.

Наконец, мы добавляем оба наших класса обслуживания в приложение Auth и даем ему попробовать:

@SpringBootApplication
public class AuthApplication {

    @Autowired
    private AuthenticationService inMemoryAuthService;

    @Autowired
    private AbstractAuthenticationService ldapAuthService;

    public static void main(String[] args) {
        SpringApplication.run(AuthApplication.class, args);
    }
}

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

5. Результат

В конце концов, мы увидели, что единственный рабочий способ-это поместить @Service в наши классы реализации, чтобы сделать их автоматически обнаруживаемыми. Сканирование компонентов Spring не собирает классы, если они не аннотированы отдельно, даже если они получены из другого @Service аннотированного интерфейса или абстрактного класса.

Кроме того, в документации Spring также говорится, что использование @Service в классах реализации позволяет автоматически обнаруживать их при сканировании компонентов.

6. Заключение

В этой статье мы рассмотрели различные места использования аннотации Spring @Service и узнали, где хранить @Service для определения компонентов Spring уровня обслуживания, чтобы они были автоматически обнаружены во время сканирования компонентов.

В частности, мы видели, что размещение аннотации @Service на интерфейсах или абстрактных классах не имеет никакого эффекта и что только конкретные классы будут выбраны сканированием компонентов, когда они будут аннотированы с помощью @Service .

Как всегда, все примеры кода и многое другое доступны на GitHub .