Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Where should @Service annotation be kept? Interface or Implementation?

Tags:

spring

service

People also ask

Can I use @service annotation in the place of controller?

No both are different. @Service annotation have use for other purpose and @Controller use for other. Actually Spring @Component, @Service, @Repository and @Controller annotations are used for automatic bean detection using classpath scan in Spring framework, but it doesn't ,mean that all functionalities are same.

What is the use of @service annotation?

It is used to mark the class as a service provider. So overall @Service annotation is used with classes that provide some business functionalities. Spring context will autodetect these classes when annotation-based configuration and classpath scanning is used. Add the spring-context dependency in your pom.

Can we use @service in place of @repository?

Answer. According to documentaion @Repository , @Service , @Controller are all synonyms. They all are just specializations of @Component annotation. So, generally, they can be used one instead of other.

What is @service annotation in Spring boot?

Spring @Service annotation is a specialization of @Component annotation. Spring Service annotation can be applied only to classes. It is used to mark the class as a service provider.


I never put @Component (or @Service, ...) at an interface, because this make the interface useless. Let me explain why.

claim 1: If you have an interface then you want to use that interface for the injection point type.

claim 2: The purpose of an interface is that it define a contract that can been implemented by several implementations. On the other side you have your injection point (@Autowired). Having just one interface and only one class that implement it, is (IMHO) useless, and violates YAGNI.

fact: When you put:

  • @Component (or @Service, ...) at an interface,
  • have multiple classes that implements it,
  • at least two classes become Spring Beans, and
  • have an injection point that use the interface for type based injection,

then you will get and NoUniqueBeanDefinitionException (or you have a very special configurations setup, with Environment, Profiles or Qualifiers ...)

Conclusion: If you use @Component (or @Service, ...) at an interface then you must violate at least one of the two clains. Therefore I think it is not useful (except some rare scenarios) to put @Component at interface level.


Spring-Data-JPA Repository interfaces are something complete different


Basically annotations like @Service, @Repository, @Component, etc. they all serve the same purpose:

auto-detection when using annotation-based configuration and classpath scanning.

From my experience I am always using @Service annotation on the interfaces or abstract classes and annotations like @Component and @Repository for their implementation. @Component annotation I am using on those classes which serves basic purposes, simple Spring beans, nothing more. @Repository annotation I am using in the DAO layer, for e.g. if I have to communicate to the database, have some transactions, etc.

So I would suggest to annotate your interface with the @Service and other layers depending on the functionality.


I used @Component, @Service, @Controller and @Repository annotations only on the implementation classes and not on the interface. But @Autowired annotation with Interfaces still worked for me. If there's only one implementation of your interface Spring component scan automatically finds it with just @Autowired annotation. In case you have multiple implementations, you will need to use the @Qualifier annotation along with @Autowired to inject the correct implementation at the injection point.


Pros of putting annotation on @Service is that it gives a hint that it is a service. I don't know if any implementing class will by default inherit this annoation.

Con side is that you are coupling your interface with a specific framework i.e. Spring, by using spring specific annotation. As interfaces are supposed to be decoupled from implementation, I would not suggest using any framework specific Annotations or object part of your interface.


I would put @Service on your class but put the name of the interface as a parameter to the annotation e.g.

interface ServiceOne {}

@Service("ServiceOne")
class ServiceOneImpl implements ServiceOne{}

By doing that you get all the benefits and can still inject the interface but get the class

@Autowired 
private ServiceOne serviceOne;

So your interface is not tied to spring framework and you can change the class at any time and not have to update all your injection points.

So if I wanted to change the implementation class I could just annotate the new class and remove from the first but that's all that is required to be changed. If you inject the class you could have a lot of work when ever you want to change the impl class.


1. @Service on Interfaces

@Service
public interface AuthenticationService {

    boolean authenticate(String username, String password);
}

Normally, that's fine, but there's a drawback. By putting Spring's @Service on interfaces, we create an extra dependency and couple our interfaces with an outside library.

Next, to test the autodetection of our new service beans, let's create an implementation of our AuthenticationService:

public class InMemoryAuthenticationService implements AuthenticationService {

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

We should pay attention that our new implementation, InMemoryAuthenticationService, doesn't have the @Service annotation on it. We left @Service only on the interface, AuthenticationService.

So, let's run our Spring context with the help of a basic Spring Boot setup:

@SpringBootApplication
public class AuthApplication {

    @Autowired
    private AuthenticationService authService;

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

When we run our app, we may get the infamous NoSuchBeanDefinitionException, and the Spring context fails to start.

Therefore, placing @Service on interfaces isn't enough for the auto-detection of Spring components.


2. @Service on Abstract Classes

Using the @Service annotation on abstract classes isn't common.

We'll start by defining an abstract class from scratch and putting the @Service annotation on it:

@Service
public abstract class AbstractAuthenticationService {

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

Next, we extend AbstractAuthenticationService to create a concrete implementation without annotating it:

public class LdapAuthenticationService extends AbstractAuthenticationService {

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

Accordingly, we also update our AuthApplication, to inject the new service class:

@SpringBootApplication
public class AuthApplication {

    @Autowired
    private AbstractAuthenticationService authService;

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

After we run our AuthApplication, the Spring context doesn't start. It ends up with the same NoSuchBeanDefinitionException exception again.

So, using @Service annotation on abstract classes doesn't have any effect in Spring.


3. @Service on Concrete Classes

Contrary to what we've seen above, it's quite a common practice to annotate the implementation classes instead of abstract classes or interfaces.

In this way, our goal is mostly to tell Spring this class is going to be a @Component and mark it with a special stereotype, which is @Service in our case.

Therefore, Spring will autodetect those classes from the classpath and automatically define them as managed beans.

So, let's put @Service on our concrete service classes this time around. We'll have one class that implements our interface and a second that extends the abstract class that we defined previously:

@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) {
        //...
    }
}

We should take notice here that our AbstractAuthenticationService doesn't implement the AuthenticationService here. Hence, we can test them independently.

Finally, we add both of our service classes into the AuthApplication and give it a try:

@SpringBootApplication
public class AuthApplication {

    @Autowired
    private AuthenticationService inMemoryAuthService;

    @Autowired
    private AbstractAuthenticationService ldapAuthService;

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

Our final test gives us a successful result, and the Spring context boots up with no exceptions. Both of the services are automatically registered as beans.

For full explanations, you might have a look at Where Should the Spring @Service Annotation Be Kept? by Yavuz Taş.