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:
| Attribute | Type | Required | Description |
|---|---|---|---|
basePackage | String | Yes | Base package to scan for components |
Rules:
- Must be present on the class passed to
SproogyApp.run() - Only one
@SproogyApplicationper 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:
| Attribute | Type | Default | Description |
|---|---|---|---|
value | String | "" | Bean name (defaults to class name with lowercase first letter) |
lazy | boolean | false | Defer 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:
| Attribute | Type | Default | Description |
|---|---|---|---|
value | String | "" | Bean name (defaults to method name) |
Rules:
- Must be on a method inside a
@Configurationclass - 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:
| Attribute | Type | Required | Description |
|---|---|---|---|
value | String | Yes | Bean 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@Qualifieris 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:
| Attribute | Type | Required | Description |
|---|---|---|---|
value | String | Yes | Path 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:
| Attribute | Type | Required | Description |
|---|---|---|---|
value | String | Yes | Environment 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:
| Attribute | Type | Required | Description |
|---|---|---|---|
value | String | Yes | Endpoint 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:
| Attribute | Type | Required | Description |
|---|---|---|---|
value | String | Yes | Name 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:
| Attribute | Type | Required | Description |
|---|---|---|---|
value | String | Yes | Name 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:
| Attribute | Type | Default | Description |
|---|---|---|---|
value | String | "" | JPQL or SQL query string |
nativeQuery | boolean | false | If 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:
| Attribute | Type | Required | Description |
|---|---|---|---|
value | String | Yes | Parameter 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
| Annotation | Target | Module | Purpose |
|---|---|---|---|
@SproogyApplication | Class | Core | Main application class |
@Component | Class | Core | Generic managed bean |
@Service | Class | Core | Service layer bean |
@Controller | Class | Core | Request handler bean |
@Repository | Class | Core | Data access bean |
@Configuration | Class | Core | Bean definition class |
@Bean | Method | Core | Bean factory method |
@Autowired | Constructor, Field | Core | Dependency injection |
@Qualifier | Field, Parameter | Core | Bean selection by name |
@Primary | Class, Method | Core | Default bean |
@NonPrimary | Class | Core | Non-default bean |
@Value | Field | Core | Configuration value injection |
@Env | Field | Core | Environment variable injection |
@AppMapping | Method | Socket | Endpoint mapping |
@PathVariable | Parameter | Socket | Path variable extraction |
@RequestParam | Parameter | Socket | Query parameter extraction |
@Query | Method | JPA | Custom query |
@Param | Parameter | JPA | Query parameter naming |
@Transactional | Method | JPA | Transaction boundary |
Next Steps
Now that you know all available annotations, start building your first application:
Or explore socket programming in depth: