Customization
Sprout is designed to be the starting point, not the ceiling. Two extension mechanisms cover most use cases: overriding service logic and overriding the repository interface.
Override service logic (recommended)
Section titled “Override service logic (recommended)”The generated controller depends on the Sprout{Name}Operations interface, not the
concrete Sprout{Name}Service. Providing your own @Primary implementation of that
interface replaces the default behavior without touching any generated code.
The simplest approach is to extend the generated service and only override the methods you need:
import org.springframework.context.annotation.Primary;import org.springframework.stereotype.Service;import com.example.domain.generated.services.SproutBookService;import com.example.domain.generated.repositories.SproutBookRepository;
@Service@Primarypublic class CustomBookService extends SproutBookService {
public CustomBookService(SproutBookRepository repository) { super(repository); }
@Override public List<Book> findAll() { // Add filtering, sorting, tenant isolation, etc. return super.findAll(); }
@Override public Book create(Book book) { // Add business rules before or after calling super book.setCreatedAt(Instant.now()); return super.create(book); }}Key points:
- You can override only the methods you need; all others delegate to the generated implementation automatically.
- The operations interface is regenerated on every build — do not edit the generated
Operationsinterface orServicedirectly. Your custom class lives in your own source tree and is unaffected by rebuilds.
With authenticationPrincipal = true
Section titled “With authenticationPrincipal = true”If you set authenticationPrincipal = true on @SproutResource, the generated
Operations interface methods include an Authentication authentication parameter. Your
override receives it and can use it for tenant isolation, audit logging, or fine-grained
access control:
@Overridepublic List<Book> findAll(Authentication authentication) { String tenantId = authentication.getName(); return repository.findByTenantId(tenantId);}Override repository generation
Section titled “Override repository generation”If you need custom Spring Data query methods, you can tell Sprout to generate the repository
as @NoRepositoryBean (a base interface that Spring Data will not register as a bean), and
then provide your own interface that extends it.
Step 1 — enable override mode
Section titled “Step 1 — enable override mode”@Entity@SproutResource(overrideRepository = true)public class Book { ... }Sprout now generates SproutBookRepository annotated with @NoRepositoryBean instead of
@Repository.
Step 2 — provide your own repository
Section titled “Step 2 — provide your own repository”import org.springframework.stereotype.Repository;import com.example.domain.generated.repositories.SproutBookRepository;
@Repositorypublic interface BookRepository extends SproutBookRepository {
// Spring Data query methods List<Book> findByAuthor(String author);
@Query("SELECT b FROM Book b WHERE b.title LIKE %:keyword%") List<Book> searchByTitle(@Param("keyword") String keyword);}Your BookRepository becomes the Spring Data bean. The generated service still uses
SproutBookRepository as its dependency type, which is satisfied by your extending interface.