spring-boot-dependency-injection — Claude Code skill
Sets up constructor-first dependency injection patterns for Spring Boot projects. It eliminates field injection and organizes bean selection via @Primary and @Qualifier.
npx skills add giuseppe-trisciuoglio/developer-kit --skill spring-boot-dependency-injection -a claude-codeWhat spring-boot-dependency-injection does
This skill guides Spring Boot architecture toward constructor injection with final fields. Mandatory collaborators are injected directly without @Autowired when a single constructor exists. Optional integrations use ObjectProvider or explicit no-op fallback implementations instead of nullable fields.
Infrastructure wiring moves into dedicated @Configuration classes, keeping domain services clean. When multiple beans share a type, the skill uses @Primary for the default and @Qualifier for specific variants. Attempting to hide circular dependencies behind @Lazy is treated as an architectural defect that needs redesign, not a quick fix.
Wiring validation follows a stepped approach. First, verify the component loads with a minimal @SpringBootTest and @ContextConfiguration. Then, test service behavior with direct constructor instantiation without starting the Spring container.
What it helps with
- replace legacy field injection with clean constructor injection
- handle optional collaborators using ObjectProvider or no-op fallbacks
- disambiguate multiple bean implementations using @Primary and @Qualifier
- enable fast unit testing without spinning up the Spring container
Example prompts for the agent
- Refactor UserService to use constructor injection with final fields instead of @Autowired on fields.
- Wire an optional NotificationGateway into ReportService using ObjectProvider and a no-op fallback.
- Create a minimal context test for UserService to verify bean wiring before adding business logic.
How to install spring-boot-dependency-injection
1Skills CLI (Vercel)
npx skills add giuseppe-trisciuoglio/developer-kit --skill spring-boot-dependency-injection -a claude-code2Manually: put the files in place
~/.claude/skills/spring-boot-dependency-injection/SKILL.mdCopy the whole skill folder; for one project use .claude/skills/spring-boot-dependency-injection/
1Skills CLI (Vercel)
npx skills add giuseppe-trisciuoglio/developer-kit --skill spring-boot-dependency-injection -a codex2Manually: put the files in place
.agents/skills/spring-boot-dependency-injection/SKILL.mdFor all projects use ~/.agents/skills/
1Skills CLI (Vercel)
npx skills add giuseppe-trisciuoglio/developer-kit --skill spring-boot-dependency-injection -a cursor2Manually: put the files in place
.cursor/skills/spring-boot-dependency-injection/SKILL.mdCursor also reads .agents/skills/ and .claude/skills/
1GitHub CLI
gh skill install giuseppe-trisciuoglio/developer-kit spring-boot-dependency-injection2Manually: put the files in place
.github/skills/spring-boot-dependency-injection/SKILL.md1Skills CLI (Vercel)
npx skills add giuseppe-trisciuoglio/developer-kit --skill spring-boot-dependency-injection -a gemini-cli2Platform CLI
gemini skills install https://github.com/giuseppe-trisciuoglio/developer-kit --path plugins/developer-kit-java/skills/spring-boot-dependency-injectionManually: put the files in place
.devin/skills/spring-boot-dependency-injection/SKILL.mdThe legacy .windsurf/skills/ path still works
Skills CLI (Vercel)
npx skills add giuseppe-trisciuoglio/developer-kit --skill spring-boot-dependency-injection -a opencodeClaude.ai
Zip the folder and upload it in Settings → Capabilities → Skills
Step by step, with commands for each platform: How to install skills in Claude Code: five ways
Questions about spring-boot-dependency-injection
How do I install spring-boot-dependency-injection in Claude Code?
Run npx skills add giuseppe-trisciuoglio/developer-kit --skill spring-boot-dependency-injection -a claude-code. The skill will be configured in ~/.claude/skills/spring-boot-dependency-injection/SKILL.md.
What does the spring-boot-dependency-injection skill do?
It provides structured rules for Spring Boot dependency management. This includes constructor-first wiring, fallback handling for optional beans, and wiring verification with lightweight tests.
How does the skill suggest handling circular dependencies?
The skill advises against patching circular references with @Lazy. It treats circular dependencies as a design issue that requires rethinking class responsibilities.
Source text
SKILL.md · MIT
---
name: spring-boot-dependency-injection
description: Provides dependency injection patterns for Spring Boot projects, including constructor-first design, optional collaborator handling, bean selection, and wiring validation. Use when creating services and configurations, replacing field injection, or troubleshooting ambiguous or fragile Spring wiring.
allowed-tools: Read, Write, Edit, Bash, Glob, Grep
---
# Spring Boot Dependency Injection
## Overview
Provides constructor-first dependency injection patterns for Spring Boot:
- mandatory collaborators via constructor injection
- optional collaborators via `ObjectProvider` or no-op fallbacks
- bean selection via `@Primary` and `@Qualifier`
- validation via minimal context tests before full integration
## When to Use
Use this skill when:
- creating a new `@Service`, `@Component`, `@Repository`, or `@Configuration` class
- replacing field injection in legacy Spring code
- resolving multiple beans of the same type with qualifiers or primary beans
- handling optional features, adapters, or integrations without null-driven wiring
- reviewing circular dependencies or brittle context startup failures
- preparing code for direct constructor-based unit testing
## Instructions
### 1. Separate mandatory and optional collaborators
For each class, identify:
- mandatory collaborators required for correct behavior
- optional collaborators that enable integrations, caching, notifications, or feature-flagged behavior
Mandatory collaborators belong in the constructor. Optional ones need an explicit strategy such as `ObjectProvider`, conditional beans, or a no-op implementation.
### 2. Default to constructor injection
For application services and adapters:
- inject mandatory dependencies through the constructor
- keep injected fields `final`
- instantiate the class directly in unit tests without starting Spring
A single constructor is usually enough; `@Autowired` is unnecessary in that case.
### 3. Resolve optional behavior intentionally
Good options include:
- `ObjectProvider<T>` when lazy access is useful
- `@ConditionalOnProperty` or `@ConditionalOnMissingBean` when wiring should change by configuration
- a no-op implementation when the caller should not care whether the feature is enabled
Avoid nullable collaborators that leave runtime behavior ambiguous.
### 4. Use bean selection annotations only when needed
When multiple beans share the same type:
- use `@Primary` for the default implementation
- use `@Qualifier` for named variants
- keep the qualifier names stable and easy to grep
If selection rules become complex, move them into a dedicated configuration class instead of spreading them across services.
### 5. Keep wiring in configuration, not business code
Use `@Configuration` and `@Bean` methods when:
- the object comes from a third-party library
- conditional creation logic is needed
- you need environment-specific wiring or explicit composition
Business services should not know how infrastructure collaborators are instantiated.
### 6. Validate wiring explicitly
After writing a new service or configuration:
1. **Verify the bean loads** with a minimal context test:
```java
@SpringBootTest
@ContextConfiguration(classes = UserService.class)
class UserServiceWiringTest {
@Autowired UserService userService;
@Test void serviceIsInstantiated() { assertNotNull(userService); }
}
```
2. **Run constructor-based unit tests** for service behavior (no Spring needed).
3. **Add slice tests** only when MVC, JPA, or messaging integration must be verified.
4. **Reserve `@SpringBootTest`** for container-wide wiring validation.
Failures at step 1 indicate wiring issues before business logic is added.
## Examples
### Example 1: Constructor-first application service
```java
@Service
public class UserService {
private final UserRepository userRepository;
private final EmailSender emailSender;
public UserService(UserRepository userRepository, EmailSender emailSender) {
this.userRepository = userRepository;
this.emailSender = emailSender;
}
public User register(UserRegistrationRequest request) {
User user = userRepository.save(User.from(request));
emailSender.sendWelcome(user);
return user;
}
}
```
This class is easy to instantiate directly in a unit test with mocks.
### Example 2: Optional dependency with a no-op fallback
```java
@Service
public class ReportService {
private final ReportRepository reportRepository;
private final NotificationGateway notificationGateway;
public ReportService(
ReportRepository reportRepository,
ObjectProvider<NotificationGateway> notificationGatewayProvider
) {
this.reportRepository = reportRepository;
this.notificationGateway = notificationGatewayProvider.getIfAvailable(NotificationGateway::noOp);
}
}
```
This keeps optional behavior explicit without leaking `null` handling through the rest of the class.
### Example 3: Multiple beans with clear selection
```java
@Configuration
public class PaymentConfiguration {
@Bean
@Primary
PaymentGateway stripeGateway() {
return new StripePaymentGateway();
}
@Bean
@Qualifier("fallbackGateway")
PaymentGateway mockGateway() {
return new MockPaymentGateway();
}
}
```
Use `@Primary` for the default path and `@Qualifier` only where a specific variant is required.
## Best Practices
- Prefer constructor injection for mandatory dependencies.
- Keep service constructors small; if a class needs too many collaborators, the design probably wants another abstraction.
- Use no-op or conditional beans instead of nullable optional dependencies.
- Keep framework-specific creation logic in configuration classes.
- Test services without Spring first, then add container tests only where they add value.
- Remove field injection during refactors instead of extending it.
## Constraints and Warnings
- Field injection hides dependencies and makes tests harder to write.
- Circular dependencies are usually a design problem, not a wiring trick to solve with `@Lazy`.
- Overusing qualifiers can make the codebase hard to reason about; prefer better abstractions or clearer configuration.
- Optional collaborators still need deterministic behavior when absent.
- Full-context tests can hide the real source of wiring failures if used too early.
## References
- `references/reference.md`
- `references/examples.md`
- `references/spring-official-dependency-injection.md`
## Related Skills
- `spring-boot-crud-patterns`
- `spring-boot-rest-api-standards`
- `unit-test-service-layer`
© giuseppe-trisciuoglio · MIT · giuseppe-trisciuoglio/developer-kit