Frequently Asked Questions About Mosh Spring Boot Architecture Skill

22 answers covering everything from basics to advanced usage.

// Basics

What is the IoC container in Spring?

The IoC (Inversion of Control) container is Spring's core mechanism that inverts the responsibility of creating objects and injecting dependencies away from your code and into the framework. It's accessed as ApplicationContext in code. You hand control of object creation to Spring, and objects it manages are called beans.

What is the pom.xml file used for?

The pom.xml (Project Object Model) is the heart of a Maven project. It declares project metadata, dependencies, the parent POM, and build configuration. In Spring Boot projects, dependency versions are inherited from the spring-boot-starter-parent hierarchy, so you typically omit explicit version tags on managed dependencies.

What is the difference between @Service, @Repository, and @Controller?

All three are specialised aliases for @Component that register a class as a Spring bean while signalling intent. @Service marks business logic classes, @Repository marks data-access classes, and @Controller marks web request handlers. Functionally they behave the same, but using the correct stereotype documents your architecture and enables layer-specific behaviour like exception translation for @Repository.

What is the Open Closed Principle in Spring Boot?

The Open Closed Principle states a class should be open for extension but closed for modification. You add new functionality by creating new classes rather than editing existing ones — for example, adding SmsNotificationService alongside EmailNotificationService without touching the dependent class. Mosh stresses applying it with common sense; it's a tool, not a rule to enforce everywhere.

What prerequisites do I need before starting Spring Boot?

You need solid Java OOP knowledge — classes, methods, and interfaces — plus familiarity with relational databases and basic SQL. This methodology flags missing prerequisites explicitly and recommends closing the gap first rather than proceeding as if they're optional. Skipping foundational knowledge causes confusion once dependency injection and data access concepts appear.

// How To

How do I add a dependency to my Spring Boot project correctly?

Use IntelliJ's dependency search (Cmd+N / Ctrl+N → Dependency) instead of hand-editing pom.xml. After adding, click the Maven sync icon to download it. Critically, delete the <version> tag from any Spring Boot starter so the parent POM manages versioning. For dev-only tools like DevTools, add <optional>true</optional> so they aren't packaged into production.

How do I configure application properties in Spring Boot?

Put all environment-specific settings in src/main/resources/application.properties as key-value pairs — server.port, database credentials, and custom keys like app.page-size. Inject these into classes with @Value("${property.key}") on a private field. Never hardcode environment-specific values directly in source code, so you can vary them across environments without recompiling.

How do I build the web layer using Spring MVC?

Spring MVC splits into Model (data/business logic), View (HTML/JSON returned), and Controller (traffic director). Annotate a class with @Controller and use @RequestMapping("/path") on a method to bind it to a URL. The method returns the view name, like "index.html". Static files — HTML, CSS, JS — live in src/main/resources/static/.

How do I debug a Spring Boot application in IntelliJ?

Run in debug mode (Ctrl+D), set breakpoints, then use F8 to Step Over, F7 to Step Into, and Option+Cmd+R to Resume. Use System.out.println for quick checks. Always remove breakpoints when done — stale breakpoints interrupt future sessions. For auto-restart during development, add Spring Boot DevTools and enable 'Build project automatically' plus 'Allow automake'.

How do I run a Spring Boot app without IntelliJ?

Use the Maven Wrapper with ./mvnw spring-boot:run on Mac/Linux or mvnw.cmd spring-boot:run on Windows. The default port is 8080, so visit http://localhost:8080. Because the wrapper pins the exact Maven version, the build behaves identically on your machine and in CI.

// Troubleshooting

Why does Spring throw a NoUniqueBeanDefinitionException?

This happens when two concrete implementations of the same interface are both annotated with @Service (or @Component) and Spring can't decide which to inject. Resolve it with disambiguation — mark one as @Primary, or use @Qualifier to name the specific bean you want in the constructor parameter, so Spring knows exactly which implementation to wire.

Why is my Spring bean not being created?

The most common cause is annotating an interface with @Service or @Component instead of the concrete implementation — Spring cannot instantiate interfaces, so no bean is created. Only annotate concrete classes. Also confirm the class is within your component-scan package (a subpackage of your main application class) so Spring discovers it.

Why does my app crash with a NullPointerException at runtime instead of startup?

You're likely using setter injection for a required dependency. If the setter is never called, the field stays null and crashes at runtime. Switch to constructor injection so the dependency is required — Spring will fail loudly at startup if the bean is missing, catching the problem immediately rather than in production.

Why am I getting dependency version conflicts after upgrading Spring Boot?

You probably kept the <version> tag on Spring Boot starter dependencies, which bypasses the parent POM's tested version management. Delete the version tags so spring-boot-starter-parent resolves compatible versions automatically. Then upgrading Spring Boot's version in the parent tag cascades correct versions to all managed dependencies simultaneously.

Why should I avoid selecting a snapshot version of Spring Boot?

Snapshot versions contain experimental, in-progress features that may be changed or removed without notice, leading to unstable and unreproducible behaviour. When generating a project, always select the latest stable release so your application relies only on finalised, supported APIs — critical for both learning and production reliability.

// Comparisons

How does this methodology compare to just following start.spring.io?

start.spring.io generates a valid project, but this methodology goes further — it enforces architectural discipline: constructor injection over field injection, programming against interfaces, correct stereotype annotations, version inheritance via the parent POM, and Maven Wrapper reproducibility. It also verifies prerequisites and validates project structure before you write code, preventing downstream confusion that a bare generator can't catch.

How does programming against interfaces compare to using concrete classes directly?

Depending on interfaces decouples your components so swapping implementations — like Stripe for PayPal — requires zero changes to the dependent class. Depending on concrete classes tightly couples them, forcing edits and retesting whenever you change an implementation. Interfaces enable the Open Closed Principle, though Mosh warns against adding them where no variation is ever expected.

Should I use Maven or Gradle with this methodology?

This methodology defaults to Maven per Mosh's recommendation, and the workflow assumes it — IntelliJ's built-in generator, the pom.xml parent POM inheritance, and the Maven Wrapper (mvnw). Gradle is a valid alternative with a similar wrapper concept, but if you're following Mosh's approach directly, stick with Maven to match every step and annotation.

// Advanced

When is setter injection actually appropriate?

Setter injection is only appropriate for optional dependencies — cases where the class can function correctly even if the dependency is never supplied. For every required dependency, use constructor injection so the app fails at startup if the bean is missing. Mixing the two is fine: constructors for required, setters for genuinely optional.

When should I NOT extract an interface?

Avoid extracting an interface when there's only ever one implementation and no realistic variation is expected. Mosh explicitly warns against blindly applying the Open Closed Principle everywhere — over-engineering with unnecessary interfaces creates an 'over-engineered mess.' Add abstraction only where you anticipate swapping implementations or need it for testability.

How does version inheritance via the parent POM actually work?

Your project inherits from spring-boot-starter-parent, which itself inherits from spring-boot-dependencies. That grandparent POM declares tested, compatible versions for every Spring ecosystem library in its dependency management section. When you omit a version tag, Maven resolves it from this hierarchy. Bumping the parent version in your pom.xml re-cascades all correct versions at once.

When is @Autowired required versus optional on a constructor?

@Autowired is optional when a class has exactly one constructor — Spring infers it automatically, so adding it is redundant noise. You only need @Autowired when multiple constructors exist and you must designate which one Spring should use for injection. This keeps your code clean while remaining explicit where ambiguity would otherwise arise.