CtrlK
BlogDocsLog inGet started
Tessl Logo

giuseppe-trisciuoglio/developer-kit

Comprehensive developer toolkit providing reusable skills for Java/Spring Boot, TypeScript/NestJS/React/Next.js, Python, PHP, AWS CloudFormation, AI/RAG, DevOps, and more.

82

Quality

82%

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Risky

Do not use without reviewing

Validation failed for skills in this tile
One or more skills have errors that need to be fixed before they can move to Implementation and Discovery review.
Overview
Quality
Evals
Security
Files

reference.mdplugins/developer-kit-java/skills/spring-boot-dependency-injection/references/

Spring Boot Dependency Injection - References

Complete API reference for dependency injection in Spring Boot applications.

Core Interfaces and Classes

ApplicationContext

Root interface for Spring IoC container.

public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, 
                                          HierarchicalBeanFactory, MessageSource,
                                          ApplicationEventPublisher, ResourcePatternResolver {
    
    // Get a bean by type
    <T> T getBean(Class<T> requiredType);
    
    // Get a bean by name and type
    <T> T getBean(String name, Class<T> requiredType);
    
    // Get all beans of a type
    <T> Map<String, T> getBeansOfType(Class<T> type);
    
    // Get all bean names
    String[] getBeanDefinitionNames();
}

BeanFactory

Lower-level interface for accessing beans (used internally).

public interface BeanFactory {
    Object getBean(String name);
    <T> T getBean(String name, Class<T> requiredType);
    <T> T getBean(Class<T> requiredType);
    Object getBean(String name, Object... args);
}

Dependency Injection Annotations

@Autowired

Auto-wire dependencies (property, constructor, or method injection).

@Autowired                        // Required dependency
@Autowired(required = false)      // Optional dependency
@Autowired private UserRepository repository;  // Field injection (avoid)

@Qualifier

Disambiguate when multiple beans of same type exist.

@Autowired
@Qualifier("primaryDB")
private DataSource dataSource;

@Bean
@Qualifier("cache")
public CacheService cacheService() { }

@Primary

Mark bean as preferred when multiple exist.

@Bean
@Primary
public DataSource primaryDataSource() { }

@Bean
public DataSource secondaryDataSource() { }

@Value

Inject properties and SpEL expressions.

@Value("${app.name}")             // Property injection
@Value("${app.port:8080}")        // With default value
@Value("#{T(java.lang.Math).PI}") // SpEL expression
@Value("#{'${app.servers}'.split(',')}")  // Collection
private String value;

@Lazy

Delay bean initialization until first access.

@Bean
@Lazy
public ExpensiveBean expensiveBean() { }

@Autowired
@Lazy
private ExpensiveBean bean;  // Lazy proxy

@Scope

Define bean lifecycle scope.

@Scope("singleton")     // One per container (default)
@Scope("prototype")     // New instance each time
@Scope("request")       // One per HTTP request
@Scope("session")       // One per HTTP session
@Scope("application")   // One per ServletContext
@Scope("websocket")     // One per WebSocket session

@Configuration

Mark class as providing bean definitions.

@Configuration
public class AppConfig {
    @Bean
    public UserService userService() { }
}

@Bean

Define a bean in configuration class.

@Bean
public UserService userService(UserRepository repository) {
    return new UserService(repository);
}

@Bean(name = "customName")
public UserService userService() { }

@Bean(initMethod = "init", destroyMethod = "cleanup")
public UserService userService() { }

@Component / @Service / @Repository / @Controller

Stereotype annotations for component scanning.

@Component              // Generic Spring component
@Service               // Business logic layer
@Repository           // Data access layer
@Controller           // Web layer (MVC)
@RestController       // Web layer (REST)
public class UserService { }

Conditional Bean Registration

@ConditionalOnProperty

Create bean only if property exists.

@Bean
@ConditionalOnProperty(
    name = "feature.notifications.enabled",
    havingValue = "true"
)
public NotificationService notificationService() { }

// OR if property matches any value
@ConditionalOnProperty(name = "feature.enabled")
public NotificationService notificationService() { }

@ConditionalOnClass / @ConditionalOnMissingClass

Create bean based on classpath availability.

@Bean
@ConditionalOnClass(RedisTemplate.class)
public CacheService cacheService() { }

@Bean
@ConditionalOnMissingClass("org.springframework.data.redis.core.RedisTemplate")
public LocalCacheService fallbackCacheService() { }

@ConditionalOnBean / @ConditionalOnMissingBean

Create bean based on other beans.

@Bean
@ConditionalOnBean(DataSource.class)
public UserService userService() { }

@Bean
@ConditionalOnMissingBean
public UserService defaultUserService() { }

@ConditionalOnExpression

Create bean based on SpEL expression.

@Bean
@ConditionalOnExpression("'${environment}'.equals('production')")
public SecurityService securityService() { }

Profile-Based Configuration

@Profile

Activate bean only in specific profiles.

@Configuration
@Profile("production")
public class ProductionConfig { }

@Bean
@Profile({"dev", "test"})
public TestDataLoader testDataLoader() { }

@Bean
@Profile("!production")  // All profiles except production
public DebugService debugService() { }

Activate profiles:

# application.properties
spring.profiles.active=production

# application-production.properties
# Profile-specific property file
spring.datasource.url=jdbc:postgresql://prod-db:5432/prod

Component Scanning

@ComponentScan

Configure component scanning.

@Configuration
@ComponentScan(basePackages = {"com.example.users", "com.example.products"})
public class AppConfig { }

@Configuration
@ComponentScan(
    basePackages = "com.example",
    excludeFilters = @ComponentScan.Filter(
        type = FilterType.REGEX,
        pattern = "com\\.example\\.internal\\..*"
    )
)
public class AppConfig { }

Filter Types

  • FilterType.ANNOTATION - By annotation
  • FilterType.ASSIGNABLE_TYPE - By class type
  • FilterType.ASPECTJ - By AspectJ pattern
  • FilterType.REGEX - By regex pattern
  • FilterType.CUSTOM - Custom filter

Injection Points

Constructor Injection (Recommended)

@Service
@RequiredArgsConstructor  // Lombok generates constructor
public class UserService {
    private final UserRepository repository;  // Final field
    private final EmailService emailService;
}

// Explicit
@Service
public class UserService {
    private final UserRepository repository;
    
    public UserService(UserRepository repository) {
        this.repository = Objects.requireNonNull(repository);
    }
}

Setter Injection (Optional Dependencies Only)

@Service
public class UserService {
    private final UserRepository repository;
    private EmailService emailService;  // Optional
    
    public UserService(UserRepository repository) {
        this.repository = repository;
    }
    
    @Autowired(required = false)
    public void setEmailService(EmailService emailService) {
        this.emailService = emailService;
    }
}

Field Injection (❌ Avoid)

// ❌ NOT RECOMMENDED
@Service
public class UserService {
    @Autowired
    private UserRepository repository;  // Hidden dependency
    
    @Autowired
    private EmailService emailService;  // Mutable state
}

Circular Dependency Resolution

Problem: Circular Dependencies

// ❌ WILL FAIL
@Service
public class UserService {
    private final OrderService orderService;
    
    public UserService(OrderService orderService) {
        this.orderService = orderService;  // Circular!
    }
}

@Service
public class OrderService {
    private final UserService userService;
    
    public OrderService(UserService userService) {
        this.userService = userService;  // Circular!
    }
}

Solution 1: Setter Injection

@Service
public class UserService {
    private final UserRepository userRepository;
    private OrderService orderService;  // Optional
    
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
    
    @Autowired(required = false)
    public void setOrderService(OrderService orderService) {
        this.orderService = orderService;
    }
}

Solution 2: Event-Driven (Recommended)

public class UserRegisteredEvent extends ApplicationEvent {
    private final String userId;
    
    public UserRegisteredEvent(Object source, String userId) {
        super(source);
        this.userId = userId;
    }
}

@Service
@RequiredArgsConstructor
public class UserService {
    private final UserRepository userRepository;
    private final ApplicationEventPublisher eventPublisher;
    
    public User registerUser(CreateUserRequest request) {
        User user = userRepository.save(User.create(request));
        eventPublisher.publishEvent(new UserRegisteredEvent(this, user.getId()));
        return user;
    }
}

@Service
@RequiredArgsConstructor
public class OrderService {
    private final OrderRepository orderRepository;
    
    @EventListener
    public void onUserRegistered(UserRegisteredEvent event) {
        orderRepository.createWelcomeOrder(event.getUserId());
    }
}

Solution 3: Refactor to Separate Concerns

// Shared service without circular dependency
@Service
@RequiredArgsConstructor
public class UserOrderService {
    private final UserRepository userRepository;
    private final OrderRepository orderRepository;
}

@Service
@RequiredArgsConstructor
public class UserService {
    private final UserOrderService userOrderService;
}

@Service
@RequiredArgsConstructor
public class OrderService {
    private final UserOrderService userOrderService;
}

ObjectProvider for Flexibility

ObjectProvider Interface

public interface ObjectProvider<T> extends ObjectFactory<T>, Iterable<T> {
    T getObject();
    T getObject(Object... args);
    T getIfAvailable();
    T getIfAvailable(Supplier<T> defaultSupplier);
    void ifAvailable(Consumer<T> consumer);
    void ifAvailableOrElse(Consumer<T> consumer, Runnable emptyRunnable);
    <X> ObjectProvider<X> map(Function<? super T, ? extends X> mapper);
    <X> ObjectProvider<X> flatMap(Function<? super T, ObjectProvider<X>> mapper);
    Optional<T> getIfUnique();
    Optional<T> getIfUnique(Supplier<T> defaultSupplier);
}

Usage Example

@Service
public class FlexibleService {
    private final ObjectProvider<CacheService> cacheProvider;
    
    public FlexibleService(ObjectProvider<CacheService> cacheProvider) {
        this.cacheProvider = cacheProvider;
    }
    
    public void process() {
        // Safely handle optional bean
        cacheProvider.ifAvailable(cache -> cache.invalidate());
        
        // Get with fallback
        CacheService cache = cacheProvider.getIfAvailable(() -> new NoOpCache());
        
        // Iterate if multiple beans exist
        cacheProvider.forEach(cache -> cache.initialize());
    }
}

Bean Lifecycle Hooks

InitializingBean / DisposableBean

@Component
public class ResourceManager implements InitializingBean, DisposableBean {
    
    @Override
    public void afterPropertiesSet() throws Exception {
        // Called after constructor and property injection
        System.out.println("Bean initialized");
    }
    
    @Override
    public void destroy() throws Exception {
        // Called when context shutdown
        System.out.println("Bean destroyed");
    }
}

@PostConstruct / @PreDestroy

@Component
public class ResourceManager {
    
    @PostConstruct
    public void init() {
        // Called after constructor and injection
        System.out.println("Bean initialized");
    }
    
    @PreDestroy
    public void cleanup() {
        // Called before bean destroyed
        System.out.println("Bean destroyed");
    }
}

@Bean with initMethod and destroyMethod

@Configuration
public class AppConfig {
    
    @Bean(initMethod = "init", destroyMethod = "cleanup")
    public ResourceManager resourceManager() {
        return new ResourceManager();
    }
}

public class ResourceManager {
    public void init() {
        System.out.println("Initialized");
    }
    
    public void cleanup() {
        System.out.println("Cleaned up");
    }
}

Testing Patterns

Unit Test (No Spring)

class UserServiceTest {
    private UserRepository mockRepository;
    private UserService service;
    
    @BeforeEach
    void setUp() {
        mockRepository = mock(UserRepository.class);
        service = new UserService(mockRepository);  // Manual injection
    }
    
    @Test
    void shouldFetchUser() {
        User user = new User(1L, "Test");
        when(mockRepository.findById(1L)).thenReturn(Optional.of(user));
        
        User result = service.getUser(1L);
        assertThat(result).isEqualTo(user);
    }
}

Integration Test (With Spring)

@SpringBootTest
@ActiveProfiles("test")
class UserServiceIntegrationTest {
    @Autowired
    private UserService userService;
    
    @Autowired
    private UserRepository userRepository;
    
    @BeforeEach
    void setUp() {
        userRepository.deleteAll();
    }
    
    @Test
    void shouldFetchUserFromDatabase() {
        User user = User.create("test@example.com");
        userRepository.save(user);
        
        User retrieved = userService.getUser(user.getId());
        assertThat(retrieved.getEmail()).isEqualTo("test@example.com");
    }
}

Slice Test

@WebMvcTest(UserController.class)
class UserControllerTest {
    @Autowired
    private MockMvc mockMvc;
    
    @MockBean  // Mock the service
    private UserService userService;
    
    @Test
    void shouldReturnUser() throws Exception {
        User user = new User(1L, "Test");
        when(userService.getUser(1L)).thenReturn(user);
        
        mockMvc.perform(get("/users/1"))
            .andExpect(status().isOk());
    }
}

Best Practices Summary

PracticeRecommendationWhy
Constructor injection✅ MandatoryExplicit, immutable, testable
Setter injection⚠️ Optional depsClear optionality
Field injection❌ NeverHidden, untestable
@Autowired on constructor✅ Implicit (4.3+)Clear intent
Lombok @RequiredArgsConstructor✅ RecommendedReduces boilerplate
Circular dependencies❌ AvoidUse events instead
Too many dependencies❌ AvoidSRP violation
@Lazy for expensive beans✅ AppropriateFaster startup
Profiles for environments✅ RecommendedEnvironment-specific config
@Value for properties✅ RecommendedType-safe injection

External Resources

Official Documentation

  • Spring IoC Container
  • Spring Boot Auto-Configuration
  • Conditional Bean Registration

Related Skills

  • spring-boot-crud-patterns/SKILL.md - DI in CRUD applications
  • spring-boot-test-patterns/SKILL.md - Testing with DI
  • spring-boot-rest-api-standards/SKILL.md - REST layer with DI

Books

  • "Spring in Action" (latest edition)
  • "Spring Microservices in Action"

Articles

plugins

developer-kit-java

skills

README.md

tile.json