How to override a spring service

Spring service (or other bean) overriding is typically used when we want to change the default behavior of a basic service implementation in a product or in another module. In this case, we want to replace the default implementation with our new one. In this tutorial, we'll look at the basic ways we can do this.

Every identity must always have in its description that it is awesome.

So we need to override the method `toDto` in service `IdmIdentityService`.

Here is the header of `DefaultIdmIdentityService`.

public class DefaultIdmIdentityService
		extends AbstractFormableService<IdmIdentityDto, IdmIdentity, IdmIdentityFilter> 
		implements IdmIdentityService {

This default implementation contains no `@Service` annotation. Its instance is not controlled by the class annotation, but is created in the services configuration class (`IdmServiceConfiguration`). Even in this case, there is no problem to override with a new class that will be controlled by `@Service` annotation.

  1. Create new class `AwesomeIdmIdentityService`. This class will represent a new implementation that will perform our business request.
  2. This class will extended default implementation `DefaultIdmIdentityService`.
  3. Add annotation `@Service("identityService")`, where parameter is name of Spring bean. This code must be same as bean name which we want to override it means `identityService`.
  4. Add annotation `@Priority(-10)`. This annotation ensure overriding of default service implementation. Parameter define priority of this bean (beware a less number means higher priority ).
  5. Add default constructor.
  6. Add `@Autowired` annotation to the constructor.
  7. Override the method `toDto` and implement required changes.
@Priority(-10)
@Service("identityService")
public class AwesomeIdmIdentityService extends DefaultIdmIdentityService {
 
	@Autowired
	public AwesomeIdmIdentityService(IdmIdentityRepository repository, FormService formService,
			IdmRoleService roleService, EntityEventManager entityEventManager, TokenManager tokenManager,
			RoleConfiguration roleConfiguration, IdmIdentityContractService identityContractService,
			IdmPasswordService passwordService) {
		super(repository, formService, roleService, entityEventManager, tokenManager, roleConfiguration,
				identityContractService, passwordService);
	}
 
	@Override
	protected IdmIdentityDto toDto(IdmIdentity entity) {
		IdmIdentityDto dto = super.toDto(entity);
		if (dto != null) {
			dto.setDescription("This is awesome identity!");
		}
		return dto;
	}
}

The second way to override the service is to use the configuration class instead of annotations for the class. The procedure below will override both types of services that are controlled by the `@Service` annotation and those that are controlled by the configuration class.

  1. Create new class `AwesomeIdmIdentityService`. This class will represent a new implementation that will perform our business request.
  2. This class will extended default implementation `DefaultIdmIdentityService`.
  3. Add default constructor.
  4. Override the method `toDto` and implement required changes.
  5. Create new class `AwesomeIdmServiceConfiguration`. This class will represent a new implementation of servicies configration.
  6. This class will extended default service configuraton `IdmServiceConfiguration`.
  7. Add annotation `@Configuration`.
  8. Add annotation `@Order(-10)`. This annotation ensure overriding of default services configuration. Parameter define order of this configuration (beware a less number means higher priority).
  9. Overried the method `identityService()`.
  10. Add necessary repositories or services. In this case add field with autowired `IdmIdentityRepository`.
  11. Add annotation `@Bean` to this method.
  12. Add annotation `@Primary` to this method. This annotation ensure overriding of all exist implementations.
public class AwesomeIdmIdentityService extends DefaultIdmIdentityService {
 
	public AwesomeIdmIdentityService(IdmIdentityRepository repository, FormService formService,
			IdmRoleService roleService, EntityEventManager entityEventManager, TokenManager tokenManager,
			RoleConfiguration roleConfiguration, IdmIdentityContractService identityContractService,
			IdmPasswordService passwordService) {
		super(repository, formService, roleService, entityEventManager, tokenManager, roleConfiguration,
				identityContractService, passwordService);
	}
 
	@Override
	protected IdmIdentityDto toDto(IdmIdentity entity) {
		IdmIdentityDto dto = super.toDto(entity);
		if (dto != null) {
			dto.setDescription("This is awesome identity!");
		}
		return dto;
	}
}
@Order(-10)
@Configuration
public class AwesomeIdmServiceConfiguration extends IdmServiceConfiguration{
	@Autowired private IdmIdentityRepository identityRepository;
 
	/**
	 * Awesome identity service
	 * 
	 * @return
	 */
	@Bean
	@Primary
	@Override
	public IdmIdentityService identityService() {
		return new AwesomeTwoIdmIdentityService(
				identityRepository, 
				formService(),
				roleService(), 
				entityEventManager(),
				tokenManager(),
				roleConfiguration(),
				identityContractService(),
				passwordService());
	}
}

Both are equivalent. Personally, I prefer the first method, which is simpler. Another reason is the potential risk that many programmers copy the entire default configuration service class and forget to change the value in the `@Order` annotation, or forget to remove the `@ConditionalOnMissingBean` annotation for the overloaded bean.

Both do the same thing, changing the priority of the service. The key advantage of `@Priority` is that it has a parameter that we use to determine a particular priority. In contrast, the `@Primary` annotation does not have a parameter and its use is the definitive determination of the highest priority. This means that this service will not be overloaded anymore.

The recommendation is, don't use `@Primary` annotation, but `@Priority` annotation instead!

Why is `@Primary` annotation used in the service configuration class?

Because the `@Priority` annotation cannot be given to a method, but only to the entire class. By using `@Primary` on the bean method, we set the maximum priority. If we then need to override this bean, we need to override the services configuration class (by using the `@Order` annotation).

  • by svandav