Skip to main content

Annotations Reference

Complete reference guide for all annotations available in Sproogy.

Core DI Annotations

@SproogyApplication

Marks the main class of your Sproogy application.

Target: Class Retention: Runtime

@SproogyApplication(basePackage = "com.example")
public class Main {
public static void main(String[] args) {
SproogyApp.run(Main.class, args);
}
}

Attributes:

AttributeTypeRequiredDescription
basePackageStringYesBase package to scan for components

Rules:

  • Must be present on the class passed to SproogyApp.run()
  • Only one @SproogyApplication per application
  • Package must exist and contain your components

@Component

Generic stereotype for any Spring-managed component.

Target: Class Retention: Runtime

@Component
public class MyComponent {
// Any managed bean
}

@Component("customName")
public class NamedComponent {
// Bean name is "customName" instead of "namedComponent"
}

@Component(lazy = true)
public class LazyComponent {
// Created only when first needed
}

Attributes:

AttributeTypeDefaultDescription
valueString""Bean name (defaults to class name with lowercase first letter)
lazybooleanfalseDefer instantiation until first use

Bean naming:

  • MyComponent"myComponent"
  • @Component("custom")"custom"

@Service

Specialization of @Component for service layer beans.

Target: Class Retention: Runtime

@Service
public class UserService {
// Business logic layer
}

Attributes: Same as @Component

Semantic meaning: Indicates this class contains business logic.


@Controller

Specialization of @Component for request handler beans.

Target: Class Retention: Runtime

@Controller
public class UserController {

@AppMapping("/users/{id}")
public ResponseEntity<User> getUser(@PathVariable("id") Long id) {
// Handle socket requests
return ResponseEntity.ok(userService.findById(id));
}
}

Attributes: Same as @Component

Semantic meaning: Indicates this class handles socket requests. Methods can be annotated with @AppMapping.


@Repository

Specialization of @Component for data access layer beans.

Target: Class Retention: Runtime

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByEmail(String email);
}

Attributes: Same as @Component

Semantic meaning: Indicates this class/interface provides data access. Sproogy's JPA module auto-generates implementations for interfaces extending JpaRepository.


@Configuration

Marks a class as a source of bean definitions.

Target: Class Retention: Runtime

@Configuration
public class AppConfig {

@Bean
public DataSource dataSource() {
HikariConfig config = new HikariConfig();
config.setJdbcUrl(EnvLoader.get("DB_URL"));
return new HikariDataSource(config);
}

@Bean
@Primary
public UserRepository userRepository(DataSource dataSource) {
return new MySQLUserRepository(dataSource);
}
}

Attributes: Same as @Component

Usage: Methods annotated with @Bean will be called to create beans.


@Bean

Indicates that a method produces a bean to be managed by the container.

Target: Method Retention: Runtime

@Configuration
public class AppConfig {

@Bean
public MyService myService() {
return new MyServiceImpl();
}

@Bean("customServiceName")
public MyService customNamedService() {
return new MyServiceImpl();
}
}

Attributes:

AttributeTypeDefaultDescription
valueString""Bean name (defaults to method name)

Rules:

  • Must be on a method inside a @Configuration class
  • Return value is registered as a bean
  • Method parameters are automatically injected

Bean naming:

  • myService()"myService"
  • @Bean("custom")"custom"

@Autowired

Marks a constructor or field for automatic dependency injection.

Target: Method (constructor), Field Retention: Runtime

@Service
public class UserService {

// Constructor injection (recommended)
private final UserRepository userRepository;

@Autowired
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
}

@Service
public class ProductService {

// Field injection
@Autowired
private ProductRepository productRepository;
}

Attributes: None

Rules:

  • On constructor: All parameters are injected
  • On field: Field value is injected after construction
  • Constructor injection is preferred (immutable dependencies)

@Qualifier

Specifies which bean to inject when multiple candidates exist.

Target: Field, Parameter Retention: Runtime

@Component
public class MySQLUserRepository implements UserRepository { }

@Component
public class PostgreSQLUserRepository implements UserRepository { }

@Service
public class UserService {

@Autowired
@Qualifier("mySQLUserRepository")
private UserRepository userRepository; // Injects MySQLUserRepository
}

@Configuration
public class AppConfig {

@Bean
public MyService myService(@Qualifier("postgresRepo") UserRepository repo) {
return new MyService(repo);
}
}

Attributes:

AttributeTypeRequiredDescription
valueStringYesBean name to inject

Usage: Resolves ambiguity when multiple beans of the same type exist.


@Primary

Marks a bean as the primary candidate when multiple beans of the same type exist.

Target: Class, Method Retention: Runtime

@Component
@Primary // This one will be injected by default
public class MySQLUserRepository implements UserRepository { }

@Component
public class PostgreSQLUserRepository implements UserRepository { }

@Service
public class UserService {

@Autowired
private UserRepository userRepository; // Injects MySQLUserRepository (primary)
}

Attributes: None

Rules:

  • Only one bean of each type can be marked as @Primary
  • Without @Primary, injection fails if multiple candidates exist (unless @Qualifier is used)

@NonPrimary

Explicitly marks a bean as non-primary (opposite of @Primary).

Target: Class Retention: Runtime

@Component
public class MySQLUserRepository implements UserRepository { }

@Component
@NonPrimary // Explicitly not primary
public class PostgreSQLUserRepository implements UserRepository { }

Attributes: None

Usage: Mostly for clarity. Beans are non-primary by default unless marked @Primary.


Configuration Injection Annotations

@Value

Injects a value from the application configuration file.

Target: Field Retention: Runtime

@Service
public class SocketService {

@Value("application.socket.format.idKey")
private String idKey; // Reads from application.yml

@Value("application.socket.format.payloadKey")
private String payloadKey;

@Value("data.hibernate.show_sql")
private String showSql;
}

Attributes:

AttributeTypeRequiredDescription
valueStringYesPath to configuration value (dot-separated)

Source: application.yml, application.json, or application.properties

Example configuration:

# application.yml
application:
socket:
format:
idKey: "id"
payloadKey: "data"

data:
hibernate:
show_sql: "true"

Path syntax: Use dot notation to navigate nested objects: "parent.child.key"


@Env

Injects a value from environment variables.

Target: Field Retention: Runtime

@Service
public class DatabaseService {

@Env("DB_URL")
private String databaseUrl;

@Env("DB_USER")
private String username;

@Env("DB_PASSWORD")
private String password;

@Env("SOCKET_PORT")
private int port; // Auto-converted to int
}

Attributes:

AttributeTypeRequiredDescription
valueStringYesEnvironment variable name

Source: .env file or system environment variables

Example .env:

DB_URL=jdbc:mysql://localhost:3306/mydb
DB_USER=admin
DB_PASSWORD=secret
SOCKET_PORT=8443

Type conversion: Sproogy automatically converts to the field type (String, int, long, boolean, etc.)


Socket Server Annotations

@AppMapping

Maps a controller method to a socket endpoint (similar to @RequestMapping in Spring).

Target: Method Retention: Runtime

@Controller
public class UserController {

@AppMapping("/users/{id}")
public ResponseEntity<User> getUser(@PathVariable("id") Long id) {
return ResponseEntity.ok(userService.findById(id));
}

@AppMapping("/users")
public ResponseEntity<List<User>> listUsers() {
return ResponseEntity.ok(userService.findAll());
}

@AppMapping("/products/{category}/{id}")
public ResponseEntity<Product> getProduct(
@PathVariable("category") String category,
@PathVariable("id") Long id
) {
return ResponseEntity.ok(productService.find(category, id));
}
}

Attributes:

AttributeTypeRequiredDescription
valueStringYesEndpoint pattern (supports path variables with {name})

Path variables:

  • Use {variableName} in the pattern
  • Extract with @PathVariable("variableName") on method parameters

Request format:

{
"id": "req-001",
"endpoint": "/users/123",
"data": {}
}

The endpoint field is matched against @AppMapping patterns.


@PathVariable

Extracts a value from the URL path template.

Target: Parameter Retention: Runtime

@Controller
public class ProductController {

@AppMapping("/products/{category}/{id}")
public ResponseEntity<Product> getProduct(
@PathVariable("category") String category,
@PathVariable("id") Long id
) {
// Request: { "endpoint": "/products/electronics/42" }
// category = "electronics"
// id = 42L
return ResponseEntity.ok(productService.find(category, id));
}
}

Attributes:

AttributeTypeRequiredDescription
valueStringYesName of the path variable

Type conversion: Sproogy automatically converts to the parameter type (String, Long, Integer, etc.)


@RequestParam

Extracts a value from request query parameters (from the data field).

Target: Parameter Retention: Runtime

@Controller
public class SearchController {

@AppMapping("/search")
public ResponseEntity<List<Product>> search(
@RequestParam("query") String query,
@RequestParam("page") int page,
@RequestParam("size") int size
) {
// Request:
// {
// "endpoint": "/search",
// "data": {
// "query": "laptop",
// "page": 1,
// "size": 20
// }
// }
return ResponseEntity.ok(searchService.search(query, page, size));
}
}

Attributes:

AttributeTypeRequiredDescription
valueStringYesName of the query parameter (key in data object)

Source: Values are extracted from the request's data field.

Type conversion: Automatic conversion to parameter type.


JPA Annotations

@Query

Defines a custom JPQL or native SQL query on a repository method.

Target: Method Retention: Runtime

@Repository
public interface UserRepository extends JpaRepository<User, Long> {

// JPQL query
@Query("SELECT u FROM User u WHERE u.email = :email")
Optional<User> findByEmail(@Param("email") String email);

// JPQL with multiple parameters
@Query("SELECT u FROM User u WHERE u.age > :minAge AND u.status = :status")
List<User> findByAgeAndStatus(@Param("minAge") int minAge, @Param("status") String status);

// Native SQL query
@Query(value = "SELECT * FROM users WHERE created_at > ?1", nativeQuery = true)
List<User> findRecentUsers(LocalDateTime since);

// Aggregation query
@Query("SELECT COUNT(u) FROM User u WHERE u.active = true")
long countActiveUsers();
}

Attributes:

AttributeTypeDefaultDescription
valueString""JPQL or SQL query string
nativeQuerybooleanfalseIf true, use native SQL instead of JPQL

Parameter binding:

  • Named parameters: :paramName + @Param("paramName")
  • Positional parameters: ?1, ?2, etc. (parameter order)

@Param

Names a query parameter for use in @Query.

Target: Parameter Retention: Runtime

@Repository
public interface ProductRepository extends JpaRepository<Product, Long> {

@Query("SELECT p FROM Product p WHERE p.category = :cat AND p.price < :maxPrice")
List<Product> findByCategoryAndPrice(
@Param("cat") String category,
@Param("maxPrice") BigDecimal maxPrice
);
}

Attributes:

AttributeTypeRequiredDescription
valueStringYesParameter name (matches :name in query)

Usage: Required for named parameters in @Query.


@Transactional

Wraps a method in a database transaction.

Target: Method Retention: Runtime

@Service
public class UserService {

@Autowired
private UserRepository userRepository;

@Transactional
public void createUser(User user) {
// Everything in this method runs in a transaction
userRepository.save(user);
// If exception is thrown, transaction is rolled back
}

@Transactional
public void transferFunds(Long fromId, Long toId, BigDecimal amount) {
Account from = accountRepository.findById(fromId).orElseThrow();
Account to = accountRepository.findById(toId).orElseThrow();

from.setBalance(from.getBalance().subtract(amount));
to.setBalance(to.getBalance().add(amount));

accountRepository.save(from);
accountRepository.save(to);
// Both updates committed together (or both rolled back on error)
}
}

@Repository
public interface UserRepository extends JpaRepository<User, Long> {

@Transactional
void deleteByEmail(String email);
}

Attributes: None

Behavior:

  • Transaction starts at method entry
  • Transaction commits on successful return
  • Transaction rolls back on exception

Rules:

  • Can be used on service methods or repository methods
  • Transactions are managed by Hibernate
  • Nested transactions use the same transaction context

Annotation Combinations

Common Patterns

Service with injected dependencies:

@Service
public class OrderService {

@Autowired
private OrderRepository orderRepository;

@Autowired
private EmailService emailService;

@Env("ORDER_TIMEOUT_MINUTES")
private int timeoutMinutes;

@Transactional
public Order createOrder(OrderRequest request) {
Order order = new Order();
order.setItems(request.getItems());
order = orderRepository.save(order);

emailService.sendOrderConfirmation(order);
return order;
}
}

Controller with path variables:

@Controller
public class OrderController {

@Autowired
private OrderService orderService;

@AppMapping("/orders/{orderId}")
public ResponseEntity<Order> getOrder(@PathVariable("orderId") Long orderId) {
return ResponseEntity.ok(orderService.findById(orderId));
}

@AppMapping("/orders")
public ResponseEntity<Order> createOrder(CreateOrderRequest request) {
// Request body is automatically deserialized
return ResponseEntity.ok(orderService.createOrder(request));
}
}

Configuration with multiple beans:

@Configuration
public class DatabaseConfig {

@Bean
@Primary
public DataSource primaryDataSource(@Env("PRIMARY_DB_URL") String url) {
return createDataSource(url);
}

@Bean
public DataSource secondaryDataSource(@Env("SECONDARY_DB_URL") String url) {
return createDataSource(url);
}

private DataSource createDataSource(String url) {
HikariConfig config = new HikariConfig();
config.setJdbcUrl(url);
return new HikariDataSource(config);
}
}

Repository with custom queries:

@Repository
public interface UserRepository extends JpaRepository<User, Long> {

// Method name query (auto-generated)
Optional<User> findByEmail(String email);

// Custom JPQL query
@Query("SELECT u FROM User u WHERE u.age > :age ORDER BY u.createdAt DESC")
List<User> findUsersOlderThan(@Param("age") int age);

// Transactional delete
@Transactional
void deleteByStatus(String status);
}

Quick Reference Table

AnnotationTargetModulePurpose
@SproogyApplicationClassCoreMain application class
@ComponentClassCoreGeneric managed bean
@ServiceClassCoreService layer bean
@ControllerClassCoreRequest handler bean
@RepositoryClassCoreData access bean
@ConfigurationClassCoreBean definition class
@BeanMethodCoreBean factory method
@AutowiredConstructor, FieldCoreDependency injection
@QualifierField, ParameterCoreBean selection by name
@PrimaryClass, MethodCoreDefault bean
@NonPrimaryClassCoreNon-default bean
@ValueFieldCoreConfiguration value injection
@EnvFieldCoreEnvironment variable injection
@AppMappingMethodSocketEndpoint mapping
@PathVariableParameterSocketPath variable extraction
@RequestParamParameterSocketQuery parameter extraction
@QueryMethodJPACustom query
@ParamParameterJPAQuery parameter naming
@TransactionalMethodJPATransaction boundary

Next Steps

Now that you know all available annotations, start building your first application:

👉 Installation Guide

Or explore socket programming in depth:

👉 Socket Programming Overview