You’ve annotated your field with @Autowired, but when you try to use it, you get a NullPointerException. Spring Boot’s supposed to inject the dependency automatically, but instead you’re dealing with null references and runtime crashes.
new keyword instead of letting Spring manage the lifecycle through component scanning or configuration.
Why @Autowired Returns Null
Spring’s dependency injection only works on beans that Spring itself creates and manages. When you instantiate a class using new, you’re bypassing Spring’s container entirely. That instance won’t have any of its @Autowired fields populated because Spring never gets a chance to inject them.
Think of it this way: Spring maintains a registry of all managed beans. When it creates a bean, it looks at all the @Autowired annotations and injects the appropriate dependencies from its registry. But if you create an object yourself, Spring doesn’t even know it exists.
The Five Most Common Causes
Cause #1: Creating Objects with new
This is by far the most common mistake. You’re manually instantiating a class that has @Autowired dependencies.
❌ Before (problematic):
@Service
public class OrderService {
@Autowired
private PaymentProcessor paymentProcessor;
// This will be null!
}
// In some other class:
public class OrderController {
public void processOrder() {
OrderService service = new OrderService();
service.process(); // paymentProcessor is null → NPE!
}
}
✅ After (fixed):
@RestController
public class OrderController {
@Autowired
private OrderService orderService; // Let Spring inject it
@PostMapping("/orders")
public void processOrder() {
orderService.process(); // Works perfectly
}
}
Alternatively, use constructor injection (recommended):
@RestController
public class OrderController {
private final OrderService orderService;
public OrderController(OrderService orderService) {
this.orderService = orderService;
}
@PostMapping("/orders")
public void processOrder() {
orderService.process(); // Clean and testable
}
}
Cause #2: Missing Component Scanning Annotations
Your class needs to be discoverable by Spring’s component scanning. Without annotations like @Component, @Service, @Repository, or @Controller, Spring won’t create a bean for it.
❌ Before (problematic):
public class EmailService { // Missing annotation!
@Autowired
private JavaMailSender mailSender; // Will be null
public void sendEmail(String to, String message) {
mailSender.send(...); // NPE here
}
}
✅ After (fixed):
@Service // Now Spring knows to manage this
public class EmailService {
@Autowired
private JavaMailSender mailSender; // Gets injected properly
public void sendEmail(String to, String message) {
mailSender.send(...); // Works!
}
}
Cause #3: Component Outside Base Package
Spring Boot automatically scans the package where your main @SpringBootApplication class is located and all sub-packages. If your component is in a different package tree, Spring won’t find it.
// Main class in: com.example.app
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
// This service in: com.mycompany.services (different root!)
@Service
public class AnalyticsService { // Won't be scanned
@Autowired
private DataRepository repo; // Will be null
}
Solution 1: Move to correct package
com.example.app
├── Application.java
└── services
└── AnalyticsService.java ← Move here
Solution 2: Add explicit component scan
@SpringBootApplication
@ComponentScan(basePackages = {"com.example.app", "com.mycompany.services"})
public class Application {
// Now it finds both package trees
}
Cause #4: Using @Autowired in Non-Spring Managed Objects
Configuration classes, utility classes instantiated manually, or objects created by third-party libraries won’t have dependency injection working.
❌ This won’t work:
public class StringUtils { // Plain utility class
@Autowired
private SomeService service; // Will be null
public static String process(String input) {
StringUtils utils = new StringUtils();
return utils.service.transform(input); // NPE
}
}
If you really need Spring dependencies in a utility context, make it a bean:
✅ Better approach:
@Component
public class StringProcessor {
private final TransformService service;
public StringProcessor(TransformService service) {
this.service = service;
}
public String process(String input) {
return service.transform(input);
}
}
// Inject it wherever needed
@Service
public class DocumentService {
@Autowired
private StringProcessor processor;
}
Cause #5: Timing Issues in @PostConstruct
If you’re accessing @Autowired dependencies in a @PostConstruct method, make sure all dependencies are properly configured. Sometimes circular dependencies or complex initialization chains cause issues.
@Service
public class CacheService {
@Autowired
private DataRepository repository;
@PostConstruct
public void initialize() {
// If repository isn't fully initialized yet, this might fail
repository.findAll().forEach(this::cache);
}
}
Fix with lazy initialization:
@Service
public class CacheService {
@Autowired
@Lazy
private DataRepository repository;
@PostConstruct
public void initialize() {
repository.findAll().forEach(this::cache);
}
}
Constructor Injection: The Modern Best Practice
Field injection with @Autowired works, but constructor injection is strongly recommended for several reasons:
- Immutability: Dependencies can be
final - Testability: Easy to mock in unit tests
- Null safety: Dependencies must be provided at construction time
- No reflection needed: More efficient
Modern Spring style:
@Service
public class OrderService {
private final PaymentProcessor paymentProcessor;
private final InventoryService inventoryService;
private final NotificationService notificationService;
// Single constructor - @Autowired is implicit (Spring 4.3+)
public OrderService(
PaymentProcessor paymentProcessor,
InventoryService inventoryService,
NotificationService notificationService) {
this.paymentProcessor = paymentProcessor;
this.inventoryService = inventoryService;
this.notificationService = notificationService;
}
public void processOrder(Order order) {
// All dependencies guaranteed non-null
inventoryService.reserve(order);
paymentProcessor.charge(order);
notificationService.sendConfirmation(order);
}
}
Even cleaner with Lombok:
@Service
@RequiredArgsConstructor // Generates constructor for final fields
public class OrderService {
private final PaymentProcessor paymentProcessor;
private final InventoryService inventoryService;
private final NotificationService notificationService;
public void processOrder(Order order) {
inventoryService.reserve(order);
paymentProcessor.charge(order);
notificationService.sendConfirmation(order);
}
}
Debugging Checklist
When you encounter null @Autowired fields, work through this checklist:
-
Is the class a Spring-managed bean?
- Check for
@Component,@Service,@Repository, or@Controller - Verify it’s in a package that’s component-scanned
- Check for
-
How is the object being created?
- If you see
new ClassName(), that’s the problem - Use dependency injection instead
- If you see
-
Is the dependency itself a bean?
- The thing you’re trying to inject must also be Spring-managed
- Check for missing annotations on the dependency
-
Check your package structure
- Run your app with
--debugflag to see component scan output - Look for “Identified candidate component class” messages
- Run your app with
-
Enable Spring Boot debug logging
# application.properties
logging.level.org.springframework=DEBUG
This shows you exactly which beans Spring is creating and managing.
Real-World Example: REST Controller
Here’s a complete example showing the right way to structure a Spring Boot REST controller with multiple dependencies:
@RestController
@RequestMapping("/api/users")
@RequiredArgsConstructor
public class UserController {
private final UserService userService;
private final EmailService emailService;
private final AuditLogger auditLogger;
@GetMapping("/{id}")
public ResponseEntity<User> getUser(@PathVariable Long id) {
return userService.findById(id)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
@PostMapping
public ResponseEntity<User> createUser(@RequestBody @Valid UserDto dto) {
User user = userService.create(dto);
emailService.sendWelcomeEmail(user);
auditLogger.log("User created: " + user.getId());
return ResponseEntity.status(HttpStatus.CREATED).body(user);
}
}
@Service
@RequiredArgsConstructor
public class UserService {
private final UserRepository repository;
public Optional<User> findById(Long id) {
return repository.findById(id);
}
public User create(UserDto dto) {
User user = new User();
user.setName(dto.getName());
user.setEmail(dto.getEmail());
return repository.save(user);
}
}
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
// Spring Data JPA automatically implements this
}
Notice how everything is connected through constructor injection. No field-level @Autowired, no manual instantiation with new, and all dependencies are guaranteed to be present.
Testing Your Dependencies
When writing unit tests, constructor injection makes mocking trivial:
@Test
void shouldCreateUserAndSendEmail() {
// Arrange
UserService userService = mock(UserService.class);
EmailService emailService = mock(EmailService.class);
AuditLogger auditLogger = mock(AuditLogger.class);
UserController controller = new UserController(
userService,
emailService,
auditLogger
);
UserDto dto = new UserDto("John", "john@example.com");
User user = new User(1L, "John", "john@example.com");
when(userService.create(dto)).thenReturn(user);
// Act
ResponseEntity<User> response = controller.createUser(dto);
// Assert
assertEquals(HttpStatus.CREATED, response.getStatusCode());
verify(emailService).sendWelcomeEmail(user);
verify(auditLogger).log("User created: 1");
}
No Spring context needed for unit tests. Fast, focused, and reliable.
Prevention Tips
- Avoid field injection - Use constructor injection instead
- Never use
newfor Spring-managed classes - Use final fields - Forces constructor injection and prevents null
- Enable strict null checks - Use IDE warnings or tools like SpotBugs
- Leverage Lombok -
@RequiredArgsConstructorreduces boilerplate - Test early - Write integration tests that actually start the Spring context
Common Questions
Q: Can I use @Autowired on private fields?
A: Yes, it works, but constructor injection is preferred for testability and immutability.
Q: What’s the difference between @Autowired and @Inject?
A: @Inject is from JSR-330 (standard Java DI), @Autowired is Spring-specific. They’re mostly interchangeable, but @Autowired has additional features like required=false.
Q: Why does IntelliJ warn about field injection? A: Because constructor injection is considered best practice. IntelliJ’s inspection is nudging you toward better code.
Q: Can I mix field and constructor injection? A: Technically yes, but don’t. Pick one style (preferably constructor) and stick with it for consistency.
Key Takeaways
@Autowiredonly works on Spring-managed beans- Never instantiate Spring components with
new - Prefer constructor injection over field injection
- Ensure your classes are annotated and component-scanned
- Use
finalfields to catch null dependencies at compile time - When debugging, check if Spring is actually creating your beans
Still hitting NullPointerException with your Spring dependencies? Use Debugly’s trace formatter to quickly parse and analyze Java stack traces. It highlights the exact line where your null reference occurs and makes it easy to trace back through your dependency chain to find the root cause.
Got more complex Spring dependency issues? Check out our guide on Spring Boot BeanCreationException for related troubleshooting steps, or learn about NoSuchBeanDefinitionException if Spring can’t find your beans at all.