Adnotări Core
Adnotarea principală care marchează clasa de pornire. Combină trei adnotări: @Configuration, @EnableAutoConfiguration și @ComponentScan.
package com.example.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
Plasează această clasă în pachetul rădăcină pentru ca @ComponentScan să detecteze automat toate componentele din sub-pachete.
Marchează o clasă ca sursă de definiții pentru bean-uri. Înlocuiește fișierele XML de configurare tradiționale.
@Configuration public class AppConfig { @Bean public EmailService emailService() { return new EmailServiceImpl(); } @Bean public NotificationService notificationService() { // Putem injecta alte bean-uri return new NotificationService(emailService()); } }
Specifică pachetele pe care Spring le scanează pentru a găsi componente (@Component, @Service, etc.).
@Configuration @ComponentScan(basePackages = { "com.example.services", "com.example.repositories" }) public class AppConfig { }
Configurare
Injectează valori din application.properties sau application.yml direct în câmpuri.
app.name=Aplicatia Mea app.version=1.0.0 app.max-connections=100
@Component public class AppSettings { @Value("${app.name}") private String appName; @Value("${app.max-connections:50}") // default: 50 private int maxConnections; }
Mapează un grup de proprietăți într-o clasă Java. Oferă type-safety și validare.
database:
host: localhost
port: 5432
credentials:
username: admin
password: secret
@Configuration @ConfigurationProperties(prefix = "database") public class DatabaseProperties { private String host; private int port; private Credentials credentials; public static class Credentials { private String username; private String password; // getters & setters } // getters & setters }
Activează bean-uri sau configurări doar pentru anumite profiluri (dev, test, prod).
@Configuration @Profile("dev") public class DevConfig { @Bean public DataSource dataSource() { // H2 in-memory pentru development return new EmbeddedDatabaseBuilder() .setType(EmbeddedDatabaseType.H2).build(); } } @Configuration @Profile("prod") public class ProdConfig { @Bean public DataSource dataSource() { // PostgreSQL pentru producție return DataSourceBuilder.create() .url("jdbc:postgresql://server:5432/db").build(); } }
Activezi un profil cu spring.profiles.active=dev în properties sau -Dspring.profiles.active=prod la rulare.
Creează bean-uri condiționat, în funcție de prezența unor clase, proprietăți sau alte condiții.
@Bean @ConditionalOnProperty(name = "feature.email", havingValue = "true") public EmailService emailService() { return new EmailServiceImpl(); } @Bean @ConditionalOnClass(name = "redis.clients.jedis.Jedis") public CacheService redisCacheService() { return new RedisCacheService(); } @Bean @ConditionalOnMissingBean(CacheService.class) public CacheService defaultCacheService() { return new InMemoryCacheService(); }
Beans & Componente
Marchează o clasă ca fiind un component Spring, gestionat de container. Adnotarea de bază din care derivă celelalte stereotipuri.
@Component public class PdfGenerator { public byte[] generateReport(ReportData data) { // Logică de generare PDF return pdfBytes; } } @Component("customName") // nume custom pentru bean public class AdvancedPdfGenerator { }
Specializare a @Component pentru clasele din layer-ul de service (logică de business).
@Service public class UserService { private final UserRepository userRepository; public UserService(UserRepository userRepository) { this.userRepository = userRepository; } public User createUser(UserDto dto) { User user = new User(); user.setEmail(dto.getEmail()); return userRepository.save(user); } }
Specializare a @Component pentru layer-ul de persistență. Oferă traducerea automată a excepțiilor specifice bazei de date.
@Repository public class UserRepositoryImpl implements UserRepository { private final JdbcTemplate jdbcTemplate; public UserRepositoryImpl(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } public Optional<User> findById(Long id) { String sql = "SELECT * FROM users WHERE id = ?"; return jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(User.class), id) .stream().findFirst(); } }
Declară o metodă într-o clasă @Configuration al cărei rezultat va fi gestionat ca bean de container.
@Configuration public class AppConfig { @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean(name = "mainMapper") public ObjectMapper objectMapper() { ObjectMapper mapper = new ObjectMapper(); mapper.registerModule(new JavaTimeModule()); return mapper; } @Bean(initMethod = "init", destroyMethod = "cleanup") public ConnectionPool connectionPool() { return new ConnectionPool(); } }
Controlează durata de viață și momentul inițializării bean-urilor.
// Singleton (default) - o singură instanță @Component @Scope("singleton") public class SingletonBean { } // Prototype - instanță nouă la fiecare injectare @Component @Scope("prototype") public class PrototypeBean { } // Lazy - inițializat la prima utilizare @Service @Lazy public class HeavyService { }
| Scope | Descriere |
|---|---|
singleton | O singură instanță per container (default) |
prototype | Instanță nouă la fiecare cerere |
request | Per HTTP request (web) |
session | Per HTTP session (web) |
Marchează un bean ca fiind preferențial când există mai multe implementări ale aceluiași tip.
public interface MessageSender { void send(String msg); } @Service @Primary // Va fi ales implicit public class EmailSender implements MessageSender { } @Service public class SmsSender implements MessageSender { }
Dependency Injection
Marchează un câmp, constructor sau setter pentru injectarea automată a dependențelor.
@Service public class OrderService { // 1. CONSTRUCTOR INJECTION (RECOMANDAT) private final OrderRepository orderRepository; private final PaymentService paymentService; // @Autowired e opțional pentru un singur constructor public OrderService(OrderRepository repo, PaymentService pay) { this.orderRepository = repo; this.paymentService = pay; } // 2. FIELD INJECTION (simplu dar nu e recomandat) @Autowired private NotificationService notificationService; // 3. SETTER INJECTION private AuditService auditService; @Autowired public void setAuditService(AuditService audit) { this.auditService = audit; } }
Constructor injection este recomandat: permite câmpuri final, face dependențele explicite și ușurează testarea.
Specifică exact care bean să fie injectat când există mai multe implementări ale aceluiași tip.
@Service("emailChannel") public class EmailChannel implements NotificationChannel { } @Service("smsChannel") public class SmsChannel implements NotificationChannel { } @Service public class AlertService { private final NotificationChannel email; private final NotificationChannel sms; public AlertService( @Qualifier("emailChannel") NotificationChannel email, @Qualifier("smsChannel") NotificationChannel sms) { this.email = email; this.sms = sms; } }
Generează automat constructorul cu toate câmpurile final. Simplifică mult codul.
import lombok.RequiredArgsConstructor; @Service @RequiredArgsConstructor public class ProductService { // Lombok generează constructorul automat! private final ProductRepository productRepository; private final CategoryService categoryService; private final PriceCalculator priceCalculator; public Product create(ProductDto dto) { // folosim dependențele direct } }
JPA & Date
Marchează o clasă Java ca fiind o entitate JPA, mapată la o tabelă din baza de date.
import jakarta.persistence.*; @Entity @Table(name = "users") public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false, unique = true, length = 100) private String email; @Column(name = "full_name") private String fullName; @Enumerated(EnumType.STRING) private UserStatus status; @CreationTimestamp private LocalDateTime createdAt; // constructors, getters, setters }
Adnotări pentru definirea relațiilor între entități.
@Entity public class Order { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; // Many-to-One: Multe comenzi -> Un user @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "user_id") private User user; // One-to-Many: O comandă -> Multe items @OneToMany(mappedBy = "order", cascade = CascadeType.ALL) private List<OrderItem> items = new ArrayList<>(); // One-to-One @OneToOne(cascade = CascadeType.ALL) @JoinColumn(name = "invoice_id") private Invoice invoice; } @Entity public class Product { // Many-to-Many @ManyToMany @JoinTable(name = "product_categories", joinColumns = @JoinColumn(name = "product_id"), inverseJoinColumns = @JoinColumn(name = "category_id")) private Set<Category> categories; }
Gestionează tranzacțiile. Dacă apare o excepție, toate operațiile sunt anulate (rollback).
@Service public class TransferService { @Transactional public void transfer(Long fromId, Long toId, BigDecimal amount) { Account from = accountRepo.findById(fromId).orElseThrow(); Account to = accountRepo.findById(toId).orElseThrow(); from.withdraw(amount); to.deposit(amount); accountRepo.save(from); accountRepo.save(to); // Dacă orice eșuează -> totul e anulat } @Transactional(readOnly = true) public List<Account> getAll() { return accountRepo.findAll(); } }
@Transactional funcționează doar pe metode publice apelate din exterior. Apelurile interne din aceeași clasă nu activează tranzacția!
Interfețe care oferă operații CRUD automate și query methods generate din numele metodelor.
public interface UserRepository extends JpaRepository<User, Long> { // Query methods - generate automat din nume Optional<User> findByEmail(String email); List<User> findByStatusAndCreatedAtAfter(UserStatus s, LocalDateTime d); boolean existsByEmail(String email); long countByStatus(UserStatus status); // JPQL query custom @Query("SELECT u FROM User u WHERE u.status = :status") List<User> findByStatusCustom(@Param("status") UserStatus status); // Native SQL @Query(value = "SELECT * FROM users WHERE email LIKE %:d", nativeQuery = true) List<User> findByDomain(@Param("d") String domain); // Update/Delete @Modifying @Query("UPDATE User u SET u.status = :s WHERE u.id = :id") int updateStatus(@Param("id") Long id, @Param("s") UserStatus s); }
Ciclul de Viață
Metode executate după crearea bean-ului și înainte de distrugerea lui.
import jakarta.annotation.PostConstruct; import jakarta.annotation.PreDestroy; @Service public class CacheService { private Map<String, Object> cache; @PostConstruct public void init() { System.out.println("Inițializez cache-ul..."); this.cache = new ConcurrentHashMap<>(); loadInitialData(); } @PreDestroy public void cleanup() { System.out.println("Curăț cache-ul..."); cache.clear(); } }
Ascultă evenimente din aplicație sau evenimente custom.
@Component public class AppEventListener { @EventListener(ApplicationReadyEvent.class) public void onReady() { System.out.println("✅ Aplicația e gata!"); } @EventListener(ContextClosedEvent.class) public void onShutdown() { System.out.println("👋 Se oprește..."); } } // Event custom public class UserCreatedEvent { private final User user; // constructor, getter } @Component public class UserEventHandler { @EventListener public void handle(UserCreatedEvent event) { sendWelcomeEmail(event.getUser()); } }
Programează execuția periodică a unei metode (cron jobs).
// Activează în configurare @Configuration @EnableScheduling public class SchedulingConfig { } @Service public class ScheduledTasks { @Scheduled(fixedRate = 300000) // la 5 minute public void syncData() { System.out.println("Sincronizez..."); } @Scheduled(fixedDelay = 10000) // 10s după terminare public void processQueue() { } @Scheduled(cron = "0 0 2 * * ?") // zilnic la 2:00 public void nightlyBackup() { } @Scheduled(cron = "0 0 9 * * MON") // luni la 9:00 public void weeklyReport() { } }
Format cron: secunde minute ore ziua_lunii luna ziua_saptamanii
Execută metoda asincron, într-un thread separat.
@Configuration @EnableAsync public class AsyncConfig { } @Service public class EmailService { @Async public void sendBulkEmails(List<String> recipients) { // Nu blochează apelantul for (String email : recipients) { sendEmail(email); } } @Async public CompletableFuture<Report> generateReport(Long id) { Report report = buildReport(id); return CompletableFuture.completedFuture(report); } }
Sumar Rapid
🎯 Toate Adnotările
@SpringBootApplicationPunct de start@ConfigurationClasă de config@BeanDefinire bean@ComponentComponent generic@ServiceBusiness logic@RepositoryData access@AutowiredInjectare DI@QualifierSelectare bean@ValueInjectare valori@ConfigurationPropertiesMapare props@ProfileConfig per mediu@ConditionalOn...Bean condiționat@EntityEntitate JPA@TransactionalTranzacții@PostConstructInit bean@PreDestroyCleanup bean@ScheduledTask programat@AsyncExecuție async@ScopeDurată viață@LazyInit întârziat📁 Structură Proiect
com.example.myapp/ ├── Application.java // @SpringBootApplication ├── config/ │ ├── AppConfig.java // @Configuration │ └── DatabaseProperties.java // @ConfigurationProperties ├── model/ │ └── User.java // @Entity ├── repository/ │ └── UserRepository.java // extends JpaRepository ├── service/ │ └── UserService.java // @Service └── util/ └── PdfGenerator.java // @Component
⚡ Best Practices
Constructor Injection - Folosește pentru dependențe obligatorii
@ConfigurationProperties - Preferă în loc de @Value pentru grupuri de proprietăți
@Transactional(readOnly=true) - Pentru metode read-only
Field Injection - Evită @Autowired pe câmpuri