Spring Framework Tutorial For Beginners - 100 Steps

blog details
author Ranga Karanam October 06, 2025 45 minutes

Discover the Secrets of the Spring Framework!

Dive into the worlds of IOC (Inversion of Control), DI (Dependency Injection), and Application Context, and explore Spring Boot, AOP, JDBC, and JPA. Prepare yourself for an exciting journey through one of the most powerful and widely used Java frameworks.

Even after more than a decade, the Spring Framework remains as popular today as it was 12 years ago when I first started using it.

How is this feasible in a very dynamic environment where designs have altered dramatically?

Image

What You Will Learn

  • Spring Framework fundamentals, including Dependency Injection, IOC Container, Application Context, and BeanFactory.
  • How to use essential Spring annotations such as @Autowired, @Component, @Service, @Repository, @Configuration, and @Primary.
  • A thorough understanding of Spring MVC, including DispatcherServlet, Model, Controllers, and ViewResolver.
  • How to work with Spring Boot Starters, including Spring Boot Starter Web, Starter Data JPA, and Starter Test.
  • Fundamentals of Spring Boot, Spring AOP, Spring JDBC, and JPA.
  • Basics of development tools such as Eclipse, Maven, JUnit, and Mockito.
  • Step-by-step guidance to create a simple web application using JSP, Servlets, and Spring MVC.
  • How to construct unit tests using XML, Java Application Contexts, and Mockito.

Getting Started

Installing Tools

Running Examples

  1. Clone the Git repository or download the ZIP file.
  2. Unzip the archive (if you downloaded a ZIP).
  3. Open Command Prompt and navigate to the folder containing pom.xml.
  4. Open Eclipse:
    • Go to File → Import → Existing Maven Project
    • Navigate to the folder where you unzipped the project
    • Select the correct project and finish the import
  5. Locate the Spring Boot Application file annotated with @SpringBootApplication.
  6. Right-click the file and select Run as Java Program.
  7. You are all set!

Spring Level 1 through Level 6 Section Overview

Title Category GitHub
Spring Framework in 10 Steps Spring - Level 1 Project Folder on GitHub
Spring in Depth Spring - Level 2 Project Folder on GitHub
Unit Testing with Spring Framework Spring - Level 3 Project Folder on GitHub
Spring Boot in 10 Steps Spring - Level 4 Project Folder on GitHub
Spring AOP Spring - Level 5 Project Folder on GitHub
Spring JDBC and JPA Spring - Level 6 Project Folder on GitHub

5 Bonus Sections - Introduction to Tools and Frameworks

Title Category GitHub
Eclipse in 5 Steps Introduction Project Folder on GitHub
Maven in 5 Steps Introduction Project Folder on GitHub
JUnit in 5 Steps Introduction Project Folder on GitHub
Mockito in 5 Steps Introduction Project Folder on GitHub
Basic Web Application with Spring MVC Introduction Project Folder on GitHub

Section Details

Spring Level 1 - First 10 Steps in Spring

Title Category GitHub
Spring Framework in 10 Steps Spring - Level 1 Project Folder on GitHub

Steps Covered:

  1. Setting up a Spring Project using start.spring.io
  2. Understanding Tight Coupling using the Binary Search Algorithm example
  3. Making the Binary Search Algorithm example loosely coupled
  4. Using Spring to manage dependencies - @Component, @Autowired
  5. Understanding what happens in the background
  6. Dynamic autowiring and troubleshooting - @Primary
  7. Constructor and Setter Injection
  8. Overview of Spring Modules
  9. Overview of Spring Projects
  10. Why is Spring popular?

Step 1: Setting up a Spring Project using start.spring.io

Dependency Injection (DI) is a crucial element of the Spring Framework. Spring helps in developing loosely coupled applications, which makes your code easier to maintain and test.

To understand dependency injection, you first need to grasp the concept of tight coupling and how to build loosely coupled programs. We’ll start with a simple example to illustrate tight coupling and the benefits of DI.

Creating a Spring project using Spring Initializr is straightforward and quick.

Spring Initializr (http://start.spring.io/) is a powerful tool to bootstrap your Spring Boot projects with the required dependencies and project structure.

Image

As shown in the image above, follow these steps to set up your Spring project:

  1. Launch Spring Initializr and configure the project:
    • Group: com.in28minutes.spring.basics
    • Artifact: spring-in-5-steps
    • Dependencies: Do not select any dependencies
      • By default, the Basic Starter is included, which contains the core Spring Framework and the Spring Test starter.
  2. Click “Generate Project” to download the project archive.

  3. Import the project into Eclipse or IntelliJ IDE:
    • Go to File → Import → Existing Maven Project
    • Navigate to the folder where you downloaded/extracted the project
    • Select the project and finish the import.
  4. Optional: If you want to understand all the files included in the project, refer to the detailed guide here.

Step 2: Understanding Tight Coupling using the Binary Search Algorithm Example

To illustrate tight coupling, we will create an example using the Binary Search and Bubble Sort algorithms.

In a tightly coupled design, the Binary Search class directly depends on a specific implementation of the Bubble Sort algorithm, making it difficult to change or test the sorting logic independently.

This example will help you understand why tight coupling is undesirable and how Dependency Injection in Spring can help decouple components.

Image

Unfortunately, the above implementation has a limitation: if we want to use Binary Search with a different sorting algorithm, we would need to update the code.

Our goal is to loosely couple the Binary Search algorithm so that it can work with any sorting algorithm without modifying the Binary Search class itself.

Consider the solution before advancing to next stage!

Step 3: Making the Binary Search Algorithm Example Loosely Coupled

To loosen the coupling between the Binary Search and the sorting algorithm, we introduce an interface for the sorting behavior.

By programming to an interface instead of a concrete class, the Binary Search class can now work with any sorting algorithm that implements this interface.

This is the foundation of Dependency Injection in Spring, allowing components to be easily replaced or tested independently.

package com.in28minutes.spring.basics.springin5steps;

public interface SortAlgorithm {
	int[] sort(int[] numbers);
}
public class BinarySearchImpl {

    private SortAlgorithm sortAlgorithm;
}

Step 4 : Using Spring to Manage Dependencies - @Component, @Autowired

We developed code to construct objects for the bubble sort algorithm and binary search in the previous phases. We also took care of the dependencies. It would be wonderful indeed if some framework can take charge of generation of the beans and autowiring the dependencies.

This is where Spring Framework comes into play!

Let’s get started with autowiring using Spring.

Notes

  • Sort algorithm is a dependency of the binary search.
@Component
public class BinarySearchImpl {

    @Autowired
    private SortAlgorithm sortAlgorithm;
}
@Component
public class BubbleSortAlgorithm implements SortAlgorithm {
	public int[] sort(int[] numbers) {
		// Logic for Bubble Sort
		return numbers;
	}
}

Step 5: What is Happening in the Background?

You can activate debug logging to understand what Spring is doing behind the scenes.

Add the following to your /src/main/resources/application.properties:

logging.level.org.springframework=debug
  • Spring performs a component scan on the parent package com.in28minutes.spring.basics.springin5steps to locate all components — classes annotated with @Component.
  • It identifies all components and their dependencies.
  • It detects that BinarySearchImpl has a dependency on SortAlgorithm.
  • Since SortAlgorithm has no further dependencies, Spring creates an instance of it and autowires it into BinarySearchImpl automatically.

Step 6 : Dynamic auto wiring and Troubleshooting - @Primary

What if we add one more SortAlgorithm?

package com.in28minutes.spring.basics.springin5steps;

import org.springframework.stereotype.Component;

@Component
public class QuickSortAlgorithm implements SortAlgorithm {
	public int[] sort(int[] numbers) {
		// Logic for Quick Sort
		return numbers;
	}
}

There are now two SortAlgorithm implementations available. Spring throws an exception because it doesn’t know which one to use.

We can use the @Primary annotation to tell Spring which SortAlgorithm implementation should be preferred when multiple options exist.

package com.in28minutes.spring.basics.springin5steps;

import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;

@Component
@Primary
public class BubbleSortAlgorithm implements SortAlgorithm {
	public int[] sort(int[] numbers) {
		// Logic for Bubble Sort
		return numbers;
	}
}

Step 7 : Constructor and Setter Injection

Constructor Injection Image

Setter Injection Image

Step 8 : Spring Modules

Spring’s modular design lets us use only the modules we need, without depending on the rest.

Image

Step 9 : Spring Projects

The Spring projects offer comprehensive solutions to common problems encountered in enterprise application development.

Image

We’re looking at just seven Spring projects — that’s only the tip of the iceberg! There are many others like Spring Web Services, Spring Session, Spring Social, Spring Mobile, and Spring Android, each solving different kinds of problems.

Spring has evolved beyond the core framework and now encompasses a wide range of projects addressing different aspects of application development.

Image Spring is one of the very few frameworks that remains as popular today as it was 15 years back.

Spring Level 2 - Spring in Depth

Title Category GitHub
Spring in Depth Spring - Level 2 Project Folder on GitHub

Topics Covered

  • Step 11 – Dependency Injection: More Examples
  • Step 12 – Autowiring in Depth: by Name and @Primary
  • Step 13 – Autowiring in Depth: Using @Qualifier Annotation
  • Step 14 – Scope of a Bean: Prototype and Singleton
  • Step 15 – Complex Scenarios with Bean Scopes: Mixing Prototype and Singleton
  • Step 15B – Difference Between Spring Singleton and GoF Singleton
  • Step 16 – Using Component Scan to Detect and Register Beans
  • Step 17 – Lifecycle of a Bean: @PostConstruct and @PreDestroy
  • Step 18 – Container and Dependency Injection (CDI): @Named, @Inject
  • Step 19 – Removing Spring Boot from a Basic Application
  • Step 20 – Fixing Minor Issues: Add Logback and Close Application Context
  • Step 21 – Defining Spring Application Context Using XML – Part 1
  • Step 22 – Defining Spring Application Context Using XML – Part 2
  • Step 23 – Mixing XML Configuration with Component Scan for Annotation-Based Beans
  • Step 24 – IoC Container vs ApplicationContext vs BeanFactory
  • Step 25@Component vs @Service vs @Repository vs @Controller
  • Step 26 – Reading Values from an External Properties File

Step 11 - Dependency Injection - A few more examples

Step 12 - Autowiring in Depth - by Name and @Primary

Step 13 - Autowiring in Depth - @Qualifier annotation

@Component
@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
public class BinarySearchImpl {

	@Autowired
	@Qualifier("bubble")
	private SortAlgorithm sortAlgorithm;
@Component
@Qualifier("bubble")
public class BubbleSortAlgorithm implements SortAlgorithm {
@Component
@Qualifier("quick")
public class QuickSortAlgorithm implements SortAlgorithm {

Step 14 - Scope of a Bean - Prototype and Singleton

Step 15 - Complex scenarios with Scope of a Spring Bean - Mix of Prototype and Singleton

package com.in28minutes.spring.basics.springin5steps.scope;

import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.stereotype.Component;

@Component
@Scope(value=ConfigurableBeanFactory.SCOPE_PROTOTYPE, 
		proxyMode = ScopedProxyMode.TARGET_CLASS)
public class JdbcConnection {
	public JdbcConnection() {
		System.out.println("JDBC Connection");
	}
}
package com.in28minutes.spring.basics.springin5steps.scope;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class PersonDAO {

	@Autowired
	JdbcConnection jdbcConnection;

	public JdbcConnection getJdbcConnection() {
		return jdbcConnection;
	}

	public void setJdbcConnection(JdbcConnection jdbcConnection) {
		this.jdbcConnection = jdbcConnection;
	}
}
package com.in28minutes.spring.basics.springin5steps;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;

import com.in28minutes.spring.basics.springin5steps.scope.PersonDAO;

@SpringBootApplication
public class SpringIn5StepsScopeApplication {
	
	private static final Logger LOGGER = LoggerFactory.getLogger(SpringIn5StepsScopeApplication.class); 
	
	public static void main(String[] args) {

		var applicationContext = 
				SpringApplication.run(SpringIn5StepsScopeApplication.class, args);
		
		var personDao = 
				applicationContext.getBean(PersonDAO.class);
		
		var personDao2 = 
				applicationContext.getBean(PersonDAO.class);
		
		LOGGER.info("{}", personDao);
		LOGGER.info("{}", personDao.getJdbcConnection());
		
		LOGGER.info("{}", personDao2);
		LOGGER.info("{}", personDao.getJdbcConnection());
		
	}
}

Step 15B - Difference Between Spring Singleton and GOF Singleton

Step 16 - Using Component Scan to scan for beans

package com.in28minutes.spring.basics.componentscan;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class ComponentDAO {

	@Autowired
	ComponentJdbcConnection jdbcConnection;

	public ComponentJdbcConnection getJdbcConnection() {
		return jdbcConnection;
	}

	public void setComponentJdbcConnection(ComponentJdbcConnection jdbcConnection) {
		this.jdbcConnection = jdbcConnection;
	}
}
package com.in28minutes.spring.basics.componentscan;

import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.stereotype.Component;

@Component
@Scope(value=ConfigurableBeanFactory.SCOPE_PROTOTYPE, 
		proxyMode = ScopedProxyMode.TARGET_CLASS)
public class ComponentJdbcConnection {
	public ComponentJdbcConnection() {
		System.out.println("JDBC Connection");
	}
}
package com.in28minutes.spring.basics.springin5steps;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.ComponentScan;

import com.in28minutes.spring.basics.componentscan.ComponentDAO;

@SpringBootApplication
@ComponentScan("com.in28minutes.spring.basics.componentscan")
public class SpringIn5StepsComponentScanApplication {
	
	private static Logger LOGGER = 
			LoggerFactory.getLogger(SpringIn5StepsComponentScanApplication.class); 
	
	public static void main(String[] args) {

		var applicationContext = SpringApplication.run(SpringIn5StepsComponentScanApplication.class, args);
		
		var componentDAO = applicationContext.getBean(ComponentDAO.class);
		
		LOGGER.info("{}", componentDAO);
		
	}
}

Step 17 - Lifecycle of a Bean - @PostConstruct and @PreDestroy

BinarySearchImpl.java

	@PostConstruct
	public void postConstruct() {
		logger.info("postConstruct");
	}

	@PreDestroy
	public void preDestroy() {
		logger.info("preDestroy");
	}

Step 18 - Container and Dependency Injection (CDI) - @Named, @Inject

/pom.xml

<dependency>
    <groupId>jakarta.inject</groupId>
    <artifactId>jakarta.inject-api</artifactId>
    <version>2.0.1</version>
</dependency>

package com.in28minutes.spring.basics.springin5steps;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;

import com.in28minutes.spring.basics.springin5steps.cdi.SomeCdiBusiness;

@SpringBootApplication
public class SpringIn5StepsCdiApplication {
	
	private static Logger LOGGER = 
			LoggerFactory.getLogger(SpringIn5StepsCdiApplication.class); 
	
	public static void main(String[] args) {

		var applicationContext = 
				SpringApplication.run(SpringIn5StepsCdiApplication.class, args);
		
		var business = 
				applicationContext.getBean(SomeCdiBusiness.class);
		
		LOGGER.info("{} dao-{}", business, business.getSomeCDIDAO());
	}
}
package com.in28minutes.spring.basics.springin5steps.cdi;

import jakarta.inject.Inject;
import jakarta.inject.Named;

@Named
public class SomeCdiBusiness {
	
	@Inject
	SomeCdiDao someCdiDao;

	public SomeCdiDao getSomeCDIDAO() {
		return someCdiDao;
	}

	public void setSomeCDIDAO(SomeCdiDao someCdiDao) {
		this.someCdiDao = someCdiDao;
	}
}
package com.in28minutes.spring.basics.springin5steps.cdi;

import jakarta.inject.Named;

@Named
public class SomeCdiDao {

}

Step 19 - Removing Spring Boot in Basic Application

pom.xml

<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-core</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-context</artifactId>
</dependency>
<dependency>
	<groupId>org.slf4j</groupId>
	<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
	<groupId>ch.qos.logback</groupId>
	<artifactId>logback-classic</artifactId>
</dependency>

package com.in28minutes.spring.basics.springin5steps;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

import com.in28minutes.spring.basics.springin5steps.basic.BinarySearchImpl;

@Configuration
@ComponentScan
public class SpringIn5StepsBasicApplication {

	public static void main(String[] args) {

		var applicationContext =
				new AnnotationConfigApplicationContext(SpringIn5StepsBasicApplication.class);

Step 20 - Fixing minor stuff - Add Logback and Close Application Context

<dependency>
	<groupId>ch.qos.logback</groupId>
	<artifactId>logback-classic</artifactId>
</dependency>
@Configuration
@ComponentScan
public class SpringIn5StepsBasicApplication {

	public static void main(String[] args) {

		try (var applicationContext = 
				new AnnotationConfigApplicationContext(
				SpringIn5StepsBasicApplication.class)) {
			//No change in code
		}
	}
}

Same changes in

  • SpringIn5StepsCdiApplication
  • SpringIn5StepsComponentScanApplication
  • SpringIn5StepsScopeApplication

Step 21 - Defining Spring Application Context using XML - Part 1

Step 22 - Defining Spring Application Context using XML - Part 2

package com.in28minutes.spring.basics.springin5steps;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.in28minutes.spring.basics.springin5steps.xml.XmlPersonDAO;

@Configuration
@ComponentScan
public class SpringIn5StepsXMLContextApplication {

	public static void main(String[] args) {

		try (var applicationContext = new ClassPathXmlApplicationContext(
				"applicationContext.xml")) {

			var personDao = applicationContext.getBean(XmlPersonDAO.class);
			System.out.println(personDao);
			System.out.println(personDao.getXmlJdbcConnection());
		}
	}
}
package com.in28minutes.spring.basics.springin5steps.xml;

public class XmlJdbcConnection {
	public XmlJdbcConnection() {
		System.out.println("JDBC Connection");
	}
}
package com.in28minutes.spring.basics.springin5steps.xml;

public class XmlPersonDAO {

	XmlJdbcConnection xmlJdbcConnection;

	public XmlJdbcConnection getXmlJdbcConnection() {
		return xmlJdbcConnection;
	}

	public void setXmlJdbcConnection(XmlJdbcConnection jdbcConnection) {
		this.xmlJdbcConnection = jdbcConnection;
	}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="xmlJdbcConnection" 
    	class="com.in28minutes.spring.basics.springin5steps.xml.XmlJdbcConnection">
    </bean>

    <bean id="xmlPersonDAO" class="com.in28minutes.spring.basics.springin5steps.xml.XmlPersonDAO">
    		<property name="xmlJdbcConnection" ref="xmlJdbcConnection"/>
    </bean>

</beans>

Step 23 - Mixing XML Context with Component Scan for Beans defined with Annotations

public class SpringIn5StepsXMLContextApplication {

	private static final Logger LOGGER = LoggerFactory.getLogger(SpringIn5StepsScopeApplication.class);

	public static void main(String[] args) {

		try (var applicationContext = new ClassPathXmlApplicationContext(
				"applicationContext.xml")) {

			LOGGER.info("Beans Loaded -> {}", (Object) applicationContext.getBeanDefinitionNames());
			// [xmlJdbcConnection, xmlPersonDAO]
	<context:component-scan base-package="com.in28minutes.spring.basics"/>

Step 24 - IOC Container vs Application Context vs Bean Factory

Step 25 - @Component vs @Service vs @Repository vs @Controller

@Repository
public class ComponentDAO { }

@Service
@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
public class BinarySearchImpl { }
@Service
@Qualifier("bubble")
public class BubbleSortAlgorithm implements SortAlgorithm { }
@Service
@Qualifier("quick")
public class QuickSortAlgorithm implements SortAlgorithm { }
@Repository
public class PersonDAO { }

Step 26 - Read values from external properties file

package com.in28minutes.spring.basics.springin5steps;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

import com.in28minutes.spring.basics.springin5steps.properties.SomeExternalService;

@Configuration
@ComponentScan
// 
@PropertySource("classpath:app.properties")
public class SpringIn5StepsPropertiesApplication {

	public static void main(String[] args) {

		try (var applicationContext = new AnnotationConfigApplicationContext(
				SpringIn5StepsPropertiesApplication.class)) {

			var service = applicationContext.getBean(SomeExternalService.class);
			System.out.println(service.returnServiceURL());
		}
	}
}
package com.in28minutes.spring.basics.springin5steps.properties;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class SomeExternalService {
	
	@Value("${external.service.url}")
	private String url;
	
	public String returnServiceURL(){
		return url;
	}

}

/src/main/resources/app.properties

external.service.url=http://someserver.dev.com/service

Spring Level 3 - Unit Testing with Spring Framework

Spring Level 3 - Unit Testing with Spring Framework

Title Category GitHub
Unit Testing with Spring Framework Spring - Level 3 Project Folder on GitHub

Topics Covered

  • Step 27 – Spring Unit Testing with Java-Based Configuration
  • Step 28 – Spring Unit Testing with XML-Based Configuration
  • Step 29 – Spring Unit Testing with Mockito

Step 27 - Spring Unit Testing with a Java Context

<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-test</artifactId>
</dependency>
<dependency>
	<groupId>junit</groupId>
	<artifactId>junit</artifactId>
</dependency>
@RunWith(SpringRunner.class)
//@SpringBootTest
public class SpringIn5StepsBasicApplicationTests {
package com.in28minutes.spring.basics.springin5steps.basic;

import static org.junit.jupiter.api.Assertions.assertEquals;

import com.in28minutes.spring.basics.springin5steps.SpringIn5StepsBasicApplication;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;

// Load the context
// replaced @RunWith with @ExtendWith
// replaced SpringRunner.class with SpringExtension.class
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = SpringIn5StepsBasicApplication.class)
public class BinarySearchTest {

    // Get this bean from the context
    @Autowired
    BinarySearchImpl binarySearch;

    @Test
    public void testBasicScenario() {

        // call method on binarySearch
        int actualResult = binarySearch.binarySearch(new int[]{}, 5);

        // check if the value is correct
        assertEquals(3, actualResult);

    }

}

Step 28 - Spring Unit Testing with an XML Context

/src/test/resources/testContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">
	
	<import resource="classpath:applicationContext.xml"/>
	
</beans>
package com.in28minutes.spring.basics.springin5steps.basic;

import static org.junit.jupiter.api.Assertions.assertEquals;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;

// Load the context
// replaced @RunWith with @ExtendWith
// replaced SpringRunner.class with SpringExtension.class
@ExtendWith(SpringExtension.class)
@ContextConfiguration(locations = "/testContext.xml")
public class BinarySearchXMLConfigurationTest {

    // Get this bean from the context
    @Autowired
    BinarySearchImpl binarySearch;

    @Test
    public void testBasicScenario() {

        // call method on binarySearch
        int actualResult = binarySearch.binarySearch(new int[]{}, 5);

        // check if the value is correct
        assertEquals(3, actualResult);

    }

}

Step 29 - Spring Unit Testing with Mockito

public class SomeCdiBusiness {

	// SAME OLD CODE

	public int findGreatest() {
		int greatest = Integer.MIN_VALUE;
        int[] data = someCdiDao.getData();
        var result = Arrays.stream(data).max();

        if (result.isPresent()) {
            greatest = result.getAsInt();
        }

        return greatest;
	}

}

Add a new method

package com.in28minutes.spring.basics.springin5steps.cdi;

import jakarta.inject.Named;

@Named
public class SomeCdiDao {
	
	public int[] getData() {
		return new int[] {5, 89,100};
	}

}
package com.in28minutes.spring.basics.springin5steps.cdi;

import static org.junit.jupiter.api.Assertions.assertEquals;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;

// replaced @RunWith with @ExtendWith
// replaced MockitoJUnitRunner.class with MockitoExtension.class
@ExtendWith(MockitoExtension.class)
public class SomeCdiBusinessTest {

    // Inject Mock
    @InjectMocks
    SomeCdiBusiness business;

    // Create Mock
    @Mock
    SomeCdiDao daoMock;

    @Test
    public void testBasicScenario() {
        Mockito.when(daoMock.getData()).thenReturn(new int[]{2, 4});
        assertEquals(4, business.findGreatest());
    }

    @Test
    public void testBasicScenario_NoElements() {
        Mockito.when(daoMock.getData()).thenReturn(new int[]{});
        assertEquals(Integer.MIN_VALUE, business.findGreatest());
    }

    @Test
    public void testBasicScenario_EqualElements() {
        Mockito.when(daoMock.getData()).thenReturn(new int[]{2, 2});
        assertEquals(2, business.findGreatest());
    }

}

<dependency>
	<groupId>org.mockito</groupId>
	<artifactId>mockito-core</artifactId>
</dependency>

Spring Level 4 - Introduction To Spring Boot

Spring Level 4 - Spring Boot in 10 Steps

Title Category GitHub
Spring Boot in 10 Steps Spring - Level 4 Project Folder on GitHub

Topics Covered

  • Step 1 – Introduction to Spring Boot: Goals and Key Features
  • Step 2 – Developing Spring Applications Before Spring Boot
  • Step 3 – Using Spring Initializr to Create a Spring Boot Application
  • Step 4 – Creating a Simple REST Controller
  • Step 5 – Understanding Spring Boot Auto-Configuration
  • Step 6 – Spring Boot vs Spring MVC
  • Step 7 – Spring Boot Starter Projects: Starter Web and Starter JPA
  • Step 8 – Overview of Different Spring Boot Starter Projects
  • Step 9 – Exploring Spring Boot Actuator
  • Step 10 – Using Spring Boot Developer Tools

Step 1 : Introduction to Spring Boot - Goals and Important Features

Spring Boot Overview

Goals

  • Enable building production-ready applications quickly
  • Provide common non-functional features:
    • Embedded servers
    • Metrics
    • Health checks
    • Externalized configuration

What Spring Boot is NOT

  • ZERO code generation
  • Neither an application server nor a web server

Key Features

  • Quick Starter Projects with Auto-Configuration
    • Web
    • JPA
  • Embedded Servers
    • Tomcat, Jetty, or Undertow
  • Production-Ready Features
    • Metrics and health checks
    • Externalized configuration

Step 2 : Developing Spring Applications before Spring Boot

Recommended Reading - http://www.springboottutorial.com/spring-boot-vs-spring-mvc-vs-spring

Step 3 : Using Spring Initializr to create a Spring Boot Application

https://start.spring.io

Step 4 : Creating a Simple REST Controller

/src/main/java/com/in28minutes/springboot/basics/springbootin10steps/BooksController.java

package com.in28minutes.springboot.basics.springbootin10steps;

import java.util.List;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class BooksController {
	@GetMapping("/books")
	public List<Book> getAllBooks() {
		return List.of(
				new Book(1L, "Mastering Spring 5.2", "Ranga Karanam"));
	}
}

Step 5 : What is Spring Boot Auto Configuration?

Recommended Reading - http://www.springboottutorial.com/spring-boot-auto-configuration

Spring-based applications often require extensive configuration. For example:

When using Spring MVC, we need to configure: component scanning, DispatcherServlet, a view resolver, WebJars (for serving static content), and other components.

When using Hibernate/JPA, we need to configure: a datasource, an EntityManagerFactory, a TransactionManager, among several other things.

Spring Boot introduces a new approach to simplify this process. It asks: Can we make configuration smarter? For instance, when a Spring MVC jar is added to an application, can some beans be auto-configured automatically? Spring Boot provides exactly this capability, reducing the need for manual configuration.

Step 6 : Spring Boot vs Spring MVC

Recommended Reading - http://www.springboottutorial.com/spring-boot-vs-spring-mvc-vs-spring

Spring, Spring MVC, and Spring Boot Overview

  • Spring is all about Dependency Injection.
    • It makes it easy to develop loosely coupled applications.
    • It improves testability of applications.
  • Spring MVC brings loose coupling to web application development with features like:
    • DispatcherServlet
    • View Resolver
    • And other components
  • Spring Boot eliminates the need for extensive manual configuration with Spring and Spring MVC.
    • You can use Spring and Spring MVC without writing a lot of boilerplate configuration.
    • Goal: Enable production-ready applications quickly

    Key features of Spring Boot:

    • Actuator – Enables advanced monitoring and tracing of applications
    • Embedded Server Integrations – Since the server is integrated into the application, there’s no need for a separate application server
    • Default Error Handling – Provides standard error pages and exception handling

Step 7 : Spring Boot Starter Projects - Starter Web and Starter JPA

Recommended Reading - http://www.springboottutorial.com/spring-boot-starter-projects

Spring Boot Starters

Starters are convenient sets of dependency descriptors that you can include in your application. They provide a one-stop-shop for all the Spring and related technologies you need, without having to hunt through sample code or manually copy multiple dependency descriptors.

For example, if you want to get started with Spring and JPA for database access, simply include the spring-boot-starter-data-jpa dependency in your project — and you’re ready to go.


Step 8: Overview of Different Spring Boot Starter Projects

Spring Boot starter projects help you quickly get started with developing specific types of applications.

Examples of Spring Boot Starters:

  • spring-boot-starter-web-services – SOAP Web Services
  • spring-boot-starter-web – Web & RESTful applications
  • spring-boot-starter-test – Unit testing and integration testing
  • spring-boot-starter-jdbc – Traditional JDBC
  • spring-boot-starter-hateoas – Add HATEOAS features to your services
  • spring-boot-starter-security – Authentication and authorization using Spring Security
  • spring-boot-starter-data-jpa – Spring Data JPA with Hibernate
  • spring-boot-starter-cache – Enable Spring Framework’s caching support
  • spring-boot-starter-data-rest – Expose simple REST services using Spring Data REST
  • spring-boot-starter-actuator – Add monitoring and tracing capabilities out-of-the-box
  • spring-boot-starter-undertow, spring-boot-starter-jetty, spring-boot-starter-tomcat – Choose your embedded servlet container
  • spring-boot-starter-logging – Logging using Logback
  • spring-boot-starter-log4j2 – Logging using Log4j2

Step 9: Spring Boot Actuator

Spring Boot Actuator provides a wide range of REST endpoints, all compatible with the HAL specification. You can use a HAL browser to explore the data offered by these services.

Actuator exposes various data about your application, including:

application info, metrics, dump, beans, environment properties, configuration properties, audit events, heap dump, loggers, trace, health mappings, and auto-configuration.

In short, Actuator provides rich metadata to monitor and manage your Spring Boot application.


Step 10: Spring Boot Developer Tools

  • Normally, you need to restart your server for every Java or JSP change.
  • Spring Boot Developer Tools enables automatic reloading of modified changes, improving development productivity.

Spring Level 5 - Spring AOP

Spring Level 5 - Spring AOP

Title Category GitHub
Spring AOP Spring - Level 5 Project Folder on GitHub

Steps Covered

  • Step 01 – Setting up AOP Example: Part 1
  • Step 02 – Setting up AOP Example: Part 2
  • Step 03 – Defining a @Before Advice
  • Step 04 – Understanding AOP Terminology: Pointcut, Advice, Aspect, Join Point, Weaving, and Weaver
  • Step 05 – Using @After, @AfterReturning, @AfterThrowing Advices
  • Step 06 – Using @Around Advice to Implement Performance Tracing
  • Step 07 – Best Practice: Use a Common Pointcut Configuration
  • Step 08 – Quick Summary of Other Pointcuts
  • Step 09 – Creating Custom Annotation and an Aspect for Tracking Execution Time

Step 01 – Setting up AOP Example: Part 1

Creating a Spring AOP project with Spring Initializr is straightforward.

Spring Initializr: http://start.spring.io/ is a great tool to bootstrap your Spring Boot projects.

Steps

  1. Launch Spring Initializr and configure the project:
    • Group: com.in28minutes.spring.aop
    • Artifact: spring-aop
    • Dependencies:
      • AOP
  2. Click Generate Project.
  3. Import the generated project into Eclipse.
  4. (Optional) To understand all the files included in the project, refer to the Spring Initializr project structure.

Step 02 - Setting up AOP Example - Part 2

package com.in28minutes.spring.aop.springaop;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class Business1 {
	
	private final Logger logger = LoggerFactory.getLogger(this.getClass());

	@Autowired
	private Dao1 dao1;
	
	public String calculateSomething(){
		String value = dao1.retrieveSomething();
		logger.info("In Business - {}", value);
		return value;
	}
}

package com.in28minutes.spring.aop.springaop;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class Business2 {
	
	@Autowired
	private Dao2 dao2;
	
	public String calculateSomething(){
		//Business Logic
		return dao2.retrieveSomething();
	}
}

package com.in28minutes.spring.aop.springaop;

import org.springframework.stereotype.Repository;

@Repository
public class Dao1 {

	public String retrieveSomething(){
		return "Dao1";
	}

}

package com.in28minutes.spring.aop.springaop;

import org.springframework.stereotype.Repository;

@Repository
public class Dao2 {

	public String retrieveSomething(){
		return "Dao2";
	}

}

Step 03 - Defining an @Before advice

public class SpringAopApplication implements CommandLineRunner {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private Business1 business1;

    @Autowired
    private Business2 business2;

    @Override
    public void run(String... args) throws Exception {
        logger.info(business1.calculateSomething());
        logger.info(business2.calculateSomething());
    }
}
package com.in28minutes.spring.aop.springaop.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;

//AOP
//Configuration
@Aspect
@Configuration
public class UseAccessAspect {
	
	private final Logger logger = LoggerFactory.getLogger(this.getClass());
	
	//What kind of method calls I would intercept
	//execution(* PACKAGE.*.*(..))
	
	@Before("execution(* com.in28minutes.spring.aop.springaop.business.*.*(..))")
	public void before(JoinPoint joinPoint){
		logger.info(" Check for user access ");
		logger.info(" Allowed execution for {}", joinPoint);
	}
}

Step 04 - Understand AOP Terminology - Pointcut, Advice, Aspect, Join Point, Weaving and Weaver

Step 05 - Using @After, @AfterReturning, @AfterThrowing advices

Step 06 - Using @Around advice to implement performance tracing

package com.in28minutes.spring.aop.springaop.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;

//AOP
//Configuration
@Aspect
@Configuration
public class AfterAopAspect {

	private final Logger logger = LoggerFactory.getLogger(this.getClass());

	@AfterReturning(value = "execution(* com.in28minutes.spring.aop.springaop.business.*.*(..))", 
			returning = "result")
	public void afterReturning(JoinPoint joinPoint, Object result) {
		logger.info("{} returned with value {}", joinPoint, result);
	}
	
	@After(value = "execution(* com.in28minutes.spring.aop.springaop.business.*.*(..))")
	public void after(JoinPoint joinPoint) {
		logger.info("after execution of {}", joinPoint);
	}
}
package com.in28minutes.spring.aop.springaop.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;

@Aspect
@Configuration
public class MethodExecutionCalculationAspect {

	private final Logger logger = LoggerFactory.getLogger(this.getClass());

	@Around("execution(* com.in28minutes.spring.aop.springaop.business.*.*(..))")
	public void around(ProceedingJoinPoint joinPoint) throws Throwable {
		long startTime = System.currentTimeMillis();

		joinPoint.proceed();

		long timeTaken = System.currentTimeMillis() - startTime;
		logger.info("Time Taken by {} is {}", joinPoint, timeTaken);
	}
}
package com.in28minutes.spring.aop.springaop.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;

//AOP
//Configuration
@Aspect
@Configuration
public class UserAccessAspect {
	
	private final Logger logger = LoggerFactory.getLogger(this.getClass());
	
	//What kind of method calls I would intercept
	//execution(* PACKAGE.*.*(..))
	//Weaving & Weaver
	@Before("execution(* com.in28minutes.spring.aop.springaop.data.*.*(..))")
	public void before(JoinPoint joinPoint){
		//Advice
		logger.info(" Check for user access ");
		logger.info(" Allowed execution for {}", joinPoint);
	}
}

Step 07 - Best Practice : Use common Pointcut Configuration

package com.in28minutes.spring.aop.springaop.aspect;

import org.aspectj.lang.annotation.Pointcut;

public class CommonJoinPointConfig {
	
	@Pointcut("execution(* com.in28minutes.spring.aop.springaop.data.*.*(..))")
	public void dataLayerExecution(){}
	
	@Pointcut("execution(* com.in28minutes.spring.aop.springaop.business.*.*(..))")
	public void businessLayerExecution(){}

}
public class MethodExecutionCalculationAspect {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Around("com.in28minutes.spring.aop.springaop.aspect.CommonJoinPointConfig.businessLayerExecution()")
}

public class AfterAopAspect

	@AfterReturning(value = "com.in28minutes.spring.aop.springaop.aspect.CommonJoinPointConfig.businessLayerExecution()", returning = "result")
	@After(value = "com.in28minutes.spring.aop.springaop.aspect.CommonJoinPointConfig.businessLayerExecution()")
public class UserAccessAspect {

    @Before("com.in28minutes.spring.aop.springaop.aspect.CommonJoinPointConfig.dataLayerExecution()")
}

Step 08 - Quick summary of other Pointcuts

Step 09 - Creating Custom Annotation and an Aspect for Tracking Time

package com.in28minutes.spring.aop.springaop.aspect;

import org.aspectj.lang.annotation.Pointcut;

public class CommonJoinPointConfig {
	
	@Pointcut("execution(* com.in28minutes.spring.aop.springaop.data.*.*(..))")
	public void dataLayerExecution(){}
	
	@Pointcut("execution(* com.in28minutes.spring.aop.springaop.business.*.*(..))")
	public void businessLayerExecution(){}
	
	@Pointcut("dataLayerExecution() && businessLayerExecution()")
	public void allLayerExecution(){}
	
	@Pointcut("bean(*dao*)")
	public void beanContainingDao(){}
	
	@Pointcut("within(com.in28minutes.spring.aop.springaop.data..*)")
	public void dataLayerExecutionWithWithin(){}

	@Pointcut("@annotation(com.in28minutes.spring.aop.springaop.aspect.TrackTime)")
	public void trackTimeAnnotation(){}

}
package com.in28minutes.spring.aop.springaop.aspect;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TrackTime {

}
@Aspect
@Configuration
public class MethodExecutionCalculationAspect {

	@Around("com.in28minutes.spring.aop.springaop.aspect.CommonJoinPointConfig.trackTimeAnnotation()")
	public void around(ProceedingJoinPoint joinPoint) throws Throwable {
public class Business1 {
		
	@TrackTime
	public String calculateSomething(){

@Repository
public class Dao1 {

    @TrackTime
    public String retrieveSomething() {
    }
}

Spring Level 6 - Spring JDBC and JPA

Spring Level 6 – Spring JDBC and JPA

Title Category GitHub
Spring JDBC and JPA Spring - Level 6 Project Folder on GitHub

Steps Covered

  • Step 01 – Setting up a project with JDBC, JPA, H2, and Web dependencies
  • Step 02 – Launching the H2 Console
  • Step 03 – Creating a database table in H2
  • Step 04 – Populating data into the Person table
  • Step 05 – Implementing findAll persons Spring JDBC query method
  • Step 06 – Executing the findAll method using CommandLineRunner
  • Step 07 – Quick Review: JDBC vs Spring JDBC
  • Step 08 – Understanding Spring Boot auto-configuration (What’s in the background?)
  • Step 09 – Implementing findById Spring JDBC query method
  • Step 10 – Implementing deleteById Spring JDBC update method
  • Step 11 – Implementing insert and update Spring JDBC update methods
  • Step 12 – Creating a custom Spring JDBC RowMapper
  • Step 13 – Quick introduction to JPA
  • Step 14 – Defining the Person entity
  • Step 15 – Implementing findById JPA repository method
  • Step 16 – Implementing insert and update JPA repository methods
  • Step 17 – Implementing deleteById JPA repository method
  • Step 18 – Implementing findAll using JPQL Named Query
  • Step 19 – Introduction to Spring Data JPA
  • Step 20 – Connecting to other databases

Step 01 – Setting up a Project with JDBC, JPA, H2, and Web Dependencies

Creating a Spring JDBC project with Spring Initializr is straightforward.

Spring Initializr: http://start.spring.io/ is a great tool to bootstrap Spring Boot projects.

Steps

  1. Launch Spring Initializr and configure the project:
    • Group: com.in28minutes.database
    • Artifact: database-demo
    • Dependencies:
      • Web
      • JDBC
      • JPA
      • H2
  2. Click Generate Project.
  3. Import the generated project into Eclipse.
  4. (Optional) To understand all the files included in the project, refer to the Spring Initializr project structure.

Step 02 - Launching up H2 Console

/src/main/resources/application.properties

spring.h2.console.enabled=true

Launching H2

  • URL - http://localhost:8080/h2-console
  • Make sure to check the db url - jdbc:h2:mem:testdb

Step 03 - Creating a Database Table in H2

/src/main/resources/data.sql

create table person
(
   id integer not null,
   name varchar(255) not null,
   location varchar(255),
   birth_date timestamp,
   primary key(id)
);

Step 04 - Populate data into Person Table

Step 05 - Implement findAll persons Spring JDBC Query Method

package com.in28minutes.database.databasedemo.entity;

import java.util.Date;

public class Person {
	private int id;
	private String name;
	private String location;
	private Date birthDate;

	public Person(int id, String name, String location, Date birthDate) {
		super();
		this.id = id;
		this.name = name;
		this.location = location;
		this.birthDate = birthDate;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getLocation() {
		return location;
	}

	public void setLocation(String location) {
		this.location = location;
	}

	public Date getBirthDate() {
		return birthDate;
	}

	public void setBirthDate(Date birthDate) {
		this.birthDate = birthDate;
	}

}

/src/main/java/com/in28minutes/database/databasedemo/jdbc/PersonJbdcDao.java

package com.in28minutes.database.databasedemo.jdbc;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

import com.in28minutes.database.databasedemo.entity.Person;

@Repository
public class PersonJbdcDao {
	@Autowired
	JdbcTemplate jdbcTemplate;

	public List<Person> findAll() {
		return jdbcTemplate.query("select * from person", 
				new BeanPropertyRowMapper(Person.class));
	}
}

Add insert statements into data.sql /src/main/resources/data.sql

INSERT INTO PERSON (ID, NAME, LOCATION, BIRTH_DATE ) 
VALUES(10001,  'Ranga', 'Hyderabad',CURRENT_DATE());
INSERT INTO PERSON (ID, NAME, LOCATION, BIRTH_DATE ) 
VALUES(10002,  'James', 'New York',CURRENT_DATE());
INSERT INTO PERSON (ID, NAME, LOCATION, BIRTH_DATE ) 
VALUES(10003,  'Pieter', 'Amsterdam',CURRENT_DATE());

Step 06 - Execute the findAll method using CommandLineRunner

public class DatabaseDemoApplication implements CommandLineRunner {
	
	private final Logger logger = LoggerFactory.getLogger(this.getClass());
	
	@Autowired
	PersonJbdcDao dao;
	
	@Override
	public void run(String... args) throws Exception {
		logger.info("All users -> {}", dao.findAll());

Modified

@Repository
public class PersonJbdcDao {
	@Autowired
	JdbcTemplate jdbcTemplate;

	public List<Person> findAll() {
		return jdbcTemplate.query("select * from person", 
				new BeanPropertyRowMapper<Person>(Person.class));
	}
}

Step 07 - A Quick Review - JDBC vs Spring JDBC

Step 08 - What’s in the background? Understanding Spring Boot Autoconfiguration

Step 09 - Implementing findById Spring JDBC Query Method

Step 10 - Implementing deleteById Spring JDBC Update Method

Modified

package com.in28minutes.database.databasedemo.jdbc;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

import com.in28minutes.database.databasedemo.entity.Person;

@Repository
public class PersonJbdcDao {

	@Autowired
	JdbcTemplate jdbcTemplate;

	public List<Person> findAll() {
		return jdbcTemplate.query("select * from person", new BeanPropertyRowMapper<Person>(Person.class));
	}

	public Person findById(int id) {
		return jdbcTemplate.queryForObject
				("select * from person where id=?", new Object[] { id },
				new BeanPropertyRowMapper<Person>(Person.class));
	}
	
	public int deleteById(int id) {
		return jdbcTemplate.update
				("delete from person where id=?", new Object[] { id });
	}

	
}

DatabaseDemoApplication

		logger.info("User id 10001 -> {}", dao.findById(10001));
		logger.info("Deleting 10002 -> No of Rows Deleted - {}", dao.deleteById(10002));

Step 11 - Implementing insert and update Spring JDBC Update Methods

	public int deleteById(int id) {
		return jdbcTemplate.update("delete from person where id=?", new Object[] { id });
	}

	public int insert(Person person) {
		return jdbcTemplate.update("insert into person (id, name, location, birth_date) " + "values(?,  ?, ?, ?)",
				new Object[] { person.getId(), person.getName(), person.getLocation(),
						new Timestamp(person.getBirthDate().getTime()) });
	}

	public int update(Person person) {
		return jdbcTemplate.update("update person " + " set name = ?, location = ?, birth_date = ? " + " where id = ?",
				new Object[] { person.getName(), person.getLocation(), new Timestamp(person.getBirthDate().getTime()),
						person.getId() });
	}
	logger.info("Deleting 10002 -> No of Rows Deleted - {}", 
				dao.deleteById(10002));
		
		logger.info("Inserting 10004 -> {}", 
				dao.insert(new Person(10004, "Tara", "Berlin", new Date())));
		
		logger.info("Update 10003 -> {}", 
				dao.update(new Person(10003, "Pieter", "Utrecht", new Date())));
	

Step 12 - Creating a custom Spring JDBC RowMapper

Inner class in PersonJbdcDao

static class PersonRowMapper implements RowMapper<Person>{
	@Override
	public Person mapRow(ResultSet rs, int rowNum) throws SQLException {
		Person person = new Person();
		person.setId(rs.getInt("id"));
		person.setName(rs.getString("name"));
		person.setLocation(rs.getString("location"));
		person.setBirthDate(rs.getTimestamp("birth_date"));
		return person;
	}
	
}

public List<Person> findAll() {
	return jdbcTemplate.query("select * from person", new PersonRowMapper());
}

PersonJbdcDao

Step 13 - Quick introduction to JPA

Step 14 - Defining Person Entity

package com.in28minutes.database.databasedemo.entity;

import java.util.Date;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;

@Entity
public class Person {
	
	@Id
	@GeneratedValue
	private int id;
	
	//No change in rest of the code	
	
}

Step 15 - Implementing findById JPA Repository Method

DatabaseDemoApplication renamed to SpringJdbcDemoApplication

package com.in28minutes.database.databasedemo.jpa;

import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import jakarta.transaction.Transactional;

import org.springframework.stereotype.Repository;

import com.in28minutes.database.databasedemo.entity.Person;

@Repository
@Transactional
public class PersonJpaRepository {
	
	//connect to the database
	@PersistenceContext
	EntityManager entityManager;
	
	public Person findById(int id) {
		return entityManager.find(Person.class, id);//JPA
	}
}

/src/main/resources/application.properties

spring.jpa.show-sql=true
spring.datasource.url=jdbc:h2:mem:testdb
spring.data.jpa.repositories.bootstrap-mode=default

/src/main/resources/data.sql - Comment Everything

/*
*/

JpaDemoApplication

package com.in28minutes.database.databasedemo;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import com.in28minutes.database.databasedemo.jpa.PersonJpaRepository;

@SpringBootApplication
public class JpaDemoApplication implements CommandLineRunner {

	private final Logger logger = LoggerFactory.getLogger(this.getClass());

	@Autowired
	PersonJpaRepository repository;

	public static void main(String[] args) {
		SpringApplication.run(JpaDemoApplication.class, args);
	}

	@Override
	public void run(String... args) throws Exception {
		
		logger.info("User id 10001 -> {}", repository.findById(10001));

		/*
		logger.info("All users -> {}", repository.findAll());
		logger.info("Deleting 10002 -> No of Rows Deleted - {}", 
				repository.deleteById(10002));
		
		logger.info("Inserting 10004 -> {}", 
				repository.insert(new Person(10004, "Tara", "Berlin", new Date())));
		
		logger.info("Update 10003 -> {}", 
				repository.update(new Person(10003, "Pieter", "Utrecht", new Date())));
		*/
	}
}

Step 16 - Implementing insert and update JPA Repository Methods

Step 17 - Implementing deleteById JPA Repository Method

Step 18 - Implementing findAll using JPQL Named Query

    logger.info("Inserting -> {}", 
            repository.insert(new Person("Tara", "Berlin", new Date())));
    repository.deleteById(10002);

@Entity
@NamedQuery(name="find_all_persons", query="select p from Person p")
public class Person { }
package com.in28minutes.database.databasedemo.jpa;

import java.util.List;

import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import jakarta.persistence.TypedQuery;
import jakarta.transaction.Transactional;

import org.springframework.stereotype.Repository;

import com.in28minutes.database.databasedemo.entity.Person;

@Repository
@Transactional
public class PersonJpaRepository {

	// connect to the database
	@PersistenceContext
	EntityManager entityManager;

	public List<Person> findAll() {
		var namedQuery = entityManager.createNamedQuery("find_all_persons", Person.class);
		return namedQuery.getResultList();
	}

	public Person findById(int id) {
		return entityManager.find(Person.class, id);// JPA
	}

	public Person update(Person person) {
		return entityManager.merge(person);
	}

	public Person insert(Person person) {
		return entityManager.merge(person);
	}

	public void deleteById(int id) {
		var person = findById(id);
		entityManager.remove(person);
	}

}

Step 19 - Introduction to Spring Data JPA

JpaDemoApplication - comment out @SpringBootApplication

package com.in28minutes.database.databasedemo.springdata;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import com.in28minutes.database.databasedemo.entity.Person;

@Repository
public interface PersonSpringDataRepository 
				extends JpaRepository<Person, Integer>{
}
package com.in28minutes.database.databasedemo;

import java.util.Date;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import com.in28minutes.database.databasedemo.entity.Person;
import com.in28minutes.database.databasedemo.springdata.PersonSpringDataRepository;

@SpringBootApplication
public class SpringDataDemoApplication implements CommandLineRunner {

	private final Logger logger = LoggerFactory.getLogger(this.getClass());

	@Autowired
	PersonSpringDataRepository repository;

	public static void main(String[] args) {
		SpringApplication.run(SpringDataDemoApplication.class, args);
	}

	@Override
	public void run(String... args) throws Exception {
		
		logger.info("User id 10001 -> {}", repository.findById(10001));
		
		logger.info("Inserting -> {}", 
				repository.save(new Person("Tara", "Berlin", new Date())));
		
		logger.info("Update 10003 -> {}", 
				repository.save(new Person(10003, "Pieter", "Utrecht", new Date())));
		
		repository.deleteById(10002);

		logger.info("All users -> {}", repository.findAll());
	}
}

Step 20 - Connecting to Other Databases

Connecting to My SQL and Other Databases

Spring Boot makes it easy to switch databases! Yeah, really simple.

Steps
  • Install MySQL and Setup Schema
  • Remove H2 dependency from pom.xml
  • Add MySQL (or your database) dependency to pom.xml ```xml
mysql mysql-connector-java
- Configure application.properties

```properties
spring.jpa.hibernate.ddl-auto=none
spring.datasource.url=jdbc:mysql://localhost:3306/person_example
spring.datasource.username=personuser
spring.datasource.password=YOUR_PASSWORD
  • Restart the app and You are ready!

Spring Boot can set up the database for you using Hibernate

Things to Note About Spring Boot JPA Schema Management

  • Spring Boot chooses a default value for spring.jpa.hibernate.ddl-auto based on whether your database is embedded or not:
    • Embedded database → default is create-drop
    • Non-embedded database → default is none
  • spring.jpa.hibernate.ddl-auto controls automatic schema management in Hibernate:
    • none – No action will be performed
    • create-only – Database creation will be generated
    • drop – Database dropping will be generated
    • create – Database will be dropped and then created
    • validate – Validates the database schema
    • update – Updates the database schema to match the entities
  • Reference: Hibernate User Guide – Schema Management

application.properties

#none, validate, update, create, create-drop
spring.jpa.hibernate.ddl-auto=create
Installing and Setting Up MySQL
  • Install MySQL:
  • Start the MySQL server (as a service)

  • Create a database and user:
    1. Open Command Prompt (Windows) or Terminal (macOS/Linux)
    2. Execute the following commands:
      -- Example commands
      CREATE DATABASE my_database;
      CREATE USER 'my_user'@'localhost' IDENTIFIED BY 'my_password';
      GRANT ALL PRIVILEGES ON my_database.* TO 'my_user'@'localhost';
      FLUSH PRIVILEGES;
      
mysql --user=user_name --password db_name
create database person_example;
create user 'personuser'@'localhost' identified by 'YOUR_PASSWORD';
grant all on person_example.* to 'personuser'@'localhost';
  • Execute following sql queries to create the table and insert the data

Table

create table person
(
	id integer not null,
	birth_date timestamp,
	location varchar(255),
	name varchar(255),
	primary key (id)
);

Data

INSERT INTO PERSON (ID, NAME, LOCATION, BIRTH_DATE ) VALUES(10001,  'Ranga', 'Hyderabad',CURRENT_DATE());
INSERT INTO PERSON (ID, NAME, LOCATION, BIRTH_DATE ) VALUES(10002,  'James', 'New York',CURRENT_DATE());
INSERT INTO PERSON (ID, NAME, LOCATION, BIRTH_DATE ) VALUES(10003,  'Pieter', 'Amsterdam',CURRENT_DATE());

Notes

JdbcTemplate AutoConfiguration
=========================
AUTO-CONFIGURATION REPORT
=========================

DataSourceAutoConfiguration matched:
   - @ConditionalOnClass found required classes 'javax.sql.DataSource', 'org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType'; @ConditionalOnMissingClass did not find unwanted class (OnClassCondition)

DataSourceTransactionManagerAutoConfiguration matched:
   - @ConditionalOnClass found required classes 'org.springframework.jdbc.core.JdbcTemplate', 'org.springframework.transaction.PlatformTransactionManager'; @ConditionalOnMissingClass did not find unwanted class (OnClassCondition)

H2ConsoleAutoConfiguration matched:
   - @ConditionalOnClass found required class 'org.h2.server.web.WebServlet'; @ConditionalOnMissingClass did not find unwanted class (OnClassCondition)
   - found ConfigurableWebEnvironment (OnWebApplicationCondition)
   - @ConditionalOnProperty (spring.h2.console.enabled=true) matched (OnPropertyCondition)

JdbcTemplateAutoConfiguration matched:
   - @ConditionalOnClass found required classes 'javax.sql.DataSource', 'org.springframework.jdbc.core.JdbcTemplate'; @ConditionalOnMissingClass did not find unwanted class (OnClassCondition)
   - @ConditionalOnSingleCandidate (types: javax.sql.DataSource; SearchStrategy: all) found a primary bean from beans 'dataSource' (OnBeanCondition)

JdbcTemplateAutoConfiguration.JdbcTemplateConfiguration#jdbcTemplate matched:
   - @ConditionalOnMissingBean (types: org.springframework.jdbc.core.JdbcOperations; SearchStrategy: all) did not find any beans (OnBeanCondition)

Bonus Section – Basic Web Application

This section covers the fundamentals of building a web application using Spring MVC, JSP, and Servlets.

Topics Covered

  • Understand the basics of HTTP
  • HTTP Requests – GET/POST, request parameters
  • HTTP Responses – Status codes: 200, 404, 500, etc.
  • Introduction to JSP, Servlets, Scriptlets, and EL (Expression Language)
  • HTML Forms – Method, action, and form data
  • Basics of using Maven, Tomcat, and Eclipse
  • Using request attributes to pass data (model) between Servlet and view

Spring MVC Steps

  • Step 11 – Configure the application to use Spring MVC
  • Step 12 – First Spring MVC Controller (@Controller, @ResponseBody)
  • Step 13 – Redirect to Login JSP (LoginController, view resolver, @ResponseBody)
  • Step 14 – DispatcherServlet configuration and Log4j integration
  • Step 15 – Show user ID and password on the welcome page (ModelMap, @RequestParam)
  • Step 16 – Create LoginService and remove all legacy JEE Servlets code
  • Step 17 – Spring autowiring and dependency management (@Autowired, @Service)

Step 01 – Up and Running with a Web Application in Tomcat

In this step, we will quickly set up a running web application.

Tip: This is one of the few steps where you copy code directly! The goal is to ensure that your web application runs correctly without errors.

Run the project:

  • Use EclipseRun AsMaven Buildtomcat7:run

You can copy code from

\pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.in28minutes</groupId>
	<artifactId>in28Minutes-first-webapp</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>war</packaging>
	<dependencies>
		<dependency>
			<groupId>javax</groupId>
			<artifactId>javaee-web-api</artifactId>
			<version>8.0.1</version>
			<scope>provided</scope>
		</dependency>
	</dependencies>
	<build>
		<pluginManagement>
			<plugins>
				<plugin>
					<groupId>org.apache.maven.plugins</groupId>
					<artifactId>maven-compiler-plugin</artifactId>
					<version>3.2</version>
					<configuration>
						<verbose>true</verbose>
						<source>1.7</source>
						<target>1.7</target>
						<showWarnings>true</showWarnings>
					</configuration>
				</plugin>
				<plugin>
					<groupId>org.apache.tomcat.maven</groupId>
					<artifactId>tomcat7-maven-plugin</artifactId>
					<version>2.2</version>
					<configuration>
						<path>/</path>
						<contextReloadable>true</contextReloadable>
					</configuration>
				</plugin>
			</plugins>
		</pluginManagement>
	</build>
</project>

\src\main\java\webapp\LoginServlet.java

package webapp;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/*
 * Browser sends Http Request to Web Server
 * 
 * Code in Web Server => Input:HttpRequest, Output: HttpResponse
 * JEE with Servlets
 * 
 * Web Server responds with Http Response
 */


@WebServlet(urlPatterns = "/login.do")
public class LoginServlet extends HttpServlet {

	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
		PrintWriter out = response.getWriter();
		out.println("<html>");
		out.println("<head>");
		out.println("<title>Yahoo!!!!!!!!</title>");
		out.println("</head>");
		out.println("<body>");
		out.println("My First Servlet");
		out.println("</body>");
		out.println("</html>");

	}

}

\src\main\webapp\WEB-INF\web.xml

<!-- webapp/WEB-INF/web.xml -->
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
	version="3.0">

	<display-name>To do List</display-name>

	<welcome-file-list>
		<welcome-file>login.do</welcome-file>
	</welcome-file-list>

</web-app>

Java Platform, Enterprise Edition (Java EE) JEE6

Servlet is a Java programming language class used to extend the capabilities of servers that host applications accessed by means of a request-response programming model.

Notes

  • extends javax.servlet.http.HttpServlet - All servlets should extend HttpServlet class
  • @WebServlet(urlPatterns = "/login.do") - Provide the url pattern to access the servlet
  • doGet(HttpServletRequest request, HttpServletResponse response) - To handle the RequestMethod GET we need to implement doGet method.

Configuring welcome-file-list in web.xml will ensure that url http://localhost:8080/ redirects to http://localhost:8080/login.do

<welcome-file-list>
	<welcome-file>login.do</welcome-file>
</welcome-file-list>

Step 02 : First JSP

Complete code

Notes

  • Create LoginServlet again
  • Redirect to a view - JSP

Code Snippets and Examples

Redirect to a view - JSP

\src\main\java\webapp\LoginServlet.java

request
 .getRequestDispatcher("/WEB-INF/views/login.jsp")
 .forward(request, response);

\src\main\webapp\WEB-INF\views\login.jsp

<html>
<head>
<title>Yahoo!!</title>
</head>
<body>
My First JSP!!!
</body>
</html>

Step 03 : Adding a Get Parameter name

Complete code

Notes

  • Passing a Request Parameter Name

Code Snippets and Examples

We read the request parameter and set it as a request attribute. Request attributes can be accessed from the view (jsp).

\src\main\java\webapp\LoginServlet.java

request.setAttribute("name", 
		request.getParameter("name"));

\src\main\webapp\WEB-INF\views\login.jsp

My First JSP!!! My name is ${name}

Step 04 : Adding another Get Parameter Password

Complete code

Code Snippets and Examples

\src\main\java\webapp\LoginServlet.java

request.setAttribute("password",
                  request.getParameter("password"));

\src\main\webapp\WEB-INF\views\login.jsp

My First JSP!!! My name is ${name} and password is ${password}

Step 05 : Let’s add a form

Complete code

Code Snippets and Examples

\src\main\java\webapp\LoginServlet.java

@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
		throws IOException, ServletException {
	request
		.getRequestDispatcher("/WEB-INF/views/login.jsp")
		.forward(request, response);
}

\src\main\webapp\WEB-INF\views\login.jsp

<html>
<head>
<title>Yahoo!!</title>
</head>
<body>
	<form action="/login.do" method="POST">
		Name : <input type="text" /> <input type="submit" />
	</form>
</body>
</html>

\src\main\webapp\WEB-INF\views\welcome.jsp

<html>
<head>
<title>Yahoo!!</title>
</head>
<body>
Welcome ${name}
</body>
</html>

Step 06 : New Form and doPost

Complete code

\src\main\java\webapp\LoginServlet.java

@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
		throws IOException, ServletException {
	request.setAttribute("name", request.getParameter("name"));
	request.getRequestDispatcher("/WEB-INF/views/welcome.jsp").forward(request, response);
}

\src\main\webapp\WEB-INF\views\welcome.jsp

<html>
<head>
<title>Yahoo!!</title>
</head>
<body>
Welcome ${name}
</body>
</html>

Step 07 : Adding Password and Validation of User Id

Complete code

Code Snippets and Examples

\src\main\java\webapp\LoginService.java

public class LoginService {
	public boolean validateUser(String user, String password) {
		return user.equalsIgnoreCase("in28Minutes") && password.equals("dummy");
	}

}

\src\main\java\webapp\LoginServlet.java

@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
		throws IOException, ServletException {
	String name = request.getParameter("name");
	String password = request.getParameter("password");

	boolean isValidUser = service.validateUser(name, password);

	if (isValidUser) {
		request.setAttribute("name", name);
		request.getRequestDispatcher("/WEB-INF/views/welcome.jsp")
				.forward(request, response);
	} else {
		request.setAttribute("errorMessage", "Invalid Credentials!!");
		request.getRequestDispatcher("/WEB-INF/views/login.jsp")
				.forward(request, response);
	}
}

\src\main\webapp\WEB-INF\views\login.jsp

<html>
<head>
<title>Yahoo!!</title>
</head>
<body>
	<p><font color="red">${errorMessage}</font></p>
	<form action="/login.do" method="POST">
		Name : <input name="name" type="text" /> Password : <input name="password" type="password" /> <input type="submit" />
	</form>
</body>
</html>

Step 11 : Configure application to use Spring MVC

What we will do

Before we start with the Flows, we need to configure application to use Spring MVC

  • Lets do a little bit of Refactoring. Mini Step 1: Rename package webapp to com.in28minutes.jee
  • We need Spring MVC Framework and its dependencies. Mini Step 2 : Add required jars to the project
  • Spring MVC uses Front Controller Pattern -> Dispatcher Servlet. Mini Step 3 : Add Dispatcher Servlet to web.xml
  • DispatcherServlet needs an Spring Application Context to launch. We will create an xml (/WEB-INF/todo-servlet.xml). Mini Step 4: Add Spring Context

Useful Snippets

pom.xml

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>6.0.6</version>
    </dependency>

web.xml

    <servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>
            org.springframework.web.servlet.DispatcherServlet
        </servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/todo-servlet.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <url-pattern>/spring-mvc/*</url-pattern>
    </servlet-mapping>

todo-servlet.xml

	<beans xmlns="http://www.springframework.org/schema/beans"
	    xmlns:context="http://www.springframework.org/schema/context"
	    xmlns:mvc="http://www.springframework.org/schema/mvc"
	    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
	    http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
	    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
	
	    <context:component-scan base-package="com.in28minutes" />
	
	    <mvc:annotation-driven />
	    
	</beans>

Flows:

  • Flow 1. Login Servlet -> GET -> login.jsp
  • Flow 2. Login Servlet -> POST (Success) -> welcome.jsp
  • Flow 3. Login Servlet -> POST (Failure) -> login.jsp (with error message)

Files List

\src\main\webapp\WEB-INF\views\login.jsp Deleted

\pom.xml Deleted

\src\main\java\webapp\LoginService.java Deleted

\src\main\java\webapp\LoginServlet.java Deleted

\src\main\webapp\WEB-INF\views\welcome.jsp Deleted

\src\main\webapp\WEB-INF\web.xml Deleted

/pom.xml New
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.in28minutes</groupId>
	<artifactId>in28Minutes-springmvc</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>war</packaging>

	<dependencies>
		<dependency>
			<groupId>javax</groupId>
			<artifactId>javaee-web-api</artifactId>
			<version>8.0.1</version>
			<scope>provided</scope>
		</dependency>

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>6.0.6</version>
		</dependency>
	</dependencies>

	<build>
		<pluginManagement>
			<plugins>
				<plugin>
					<groupId>org.apache.maven.plugins</groupId>
					<artifactId>maven-compiler-plugin</artifactId>
					<version>3.2</version>
					<configuration>
						<verbose>true</verbose>
						<source>1.8</source>
						<target>1.8</target>
						<showWarnings>true</showWarnings>
					</configuration>
				</plugin>
				<plugin>
					<groupId>org.apache.tomcat.maven</groupId>
					<artifactId>tomcat7-maven-plugin</artifactId>
					<version>2.2</version>
					<configuration>
						<path>/</path>
						<contextReloadable>true</contextReloadable>
					</configuration>
				</plugin>
			</plugins>
		</pluginManagement>
	</build>
</project>
/src/main/java/com/in28minutes/jee/LoginService.java New
package com.in28minutes.jee;

public class LoginService {
	public boolean validateUser(String user, String password) {
		return user.equalsIgnoreCase("in28Minutes") && password.equals("dummy");
	}

}
/src/main/java/com/in28minutes/jee/LoginServlet.java New
package com.in28minutes.jee;

import java.io.IOException;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

@WebServlet(urlPatterns = "/login.do")
public class LoginServlet extends HttpServlet {

	private LoginService service = new LoginService();

	@Override
	protected void doGet(HttpServletRequest request,
			HttpServletResponse response) throws IOException, ServletException {
		request.getRequestDispatcher("/WEB-INF/views/login.jsp").forward(
				request, response);
	}

	@Override
	protected void doPost(HttpServletRequest request,
			HttpServletResponse response) throws IOException, ServletException {
		String name = request.getParameter("name");
		String password = request.getParameter("password");

		boolean isValidUser = service.validateUser(name, password);

		if (isValidUser) {
			request.setAttribute("name", name);
			request.getRequestDispatcher("/WEB-INF/views/welcome.jsp").forward(
					request, response);
		} else {
			request.setAttribute("errorMessage", "Invalid Credentials!!");
			request.getRequestDispatcher("/WEB-INF/views/login.jsp").forward(
					request, response);
		}
	}

}
/src/main/webapp/WEB-INF/todo-servlet.xml New
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="com.in28minutes" />

    <mvc:annotation-driven />
    
</beans>
/src/main/webapp/WEB-INF/views/login.jsp New
<html>
<head>
<title>Yahoo!!</title>
</head>
<body>
    <p><font color="red">${errorMessage}</font></p>
    <form action="/login.do" method="POST">
        Name : <input name="name" type="text" /> Password : <input name="password" type="password" /> <input type="submit" />
    </form>
</body>
</html>
/src/main/webapp/WEB-INF/views/welcome.jsp New
<html>
<head>
<title>Yahoo!!</title>
</head>
<body>
Welcome ${name}
</body>
</html>
/src/main/webapp/WEB-INF/web.xml New
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
    version="3.0">

    <display-name>To do List</display-name>

    <servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>
            org.springframework.web.servlet.DispatcherServlet
        </servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/todo-servlet.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <url-pattern>/spring-mvc/*</url-pattern>
    </servlet-mapping>
</web-app>

Step 12 : First Spring MVC Controller, @ResponseBody, @Controller

First Spring MVC Controller

  • @RequestMapping(value = “/login”, method = RequestMethod.GET)
  • http://localhost:8080/spring-mvc/login
  • web.xml - /spring-mvc/*
  • Why @ResponseBody?
  • Importance of RequestMapping method
  • Can I have multiple urls rendered from Same Controller?
package com.in28minutes.springmvc;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class LoginController {

	@RequestMapping(value = "/login")
	@ResponseBody
	public String sayHello() {
		return "Hello World dummy";
	}
}

Step 13 : Redirect to Login JSP - LoginController, @ResponseBody - and View Resolver

/src/main/java/com/in28minutes/springmvc/login/LoginController.java New
package com.in28minutes.springmvc.login;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class LoginController {
	@RequestMapping(value = "/login", method = RequestMethod.GET)
	public String showLoginPage() {
		return "login";
	}
}
/src/main/webapp/WEB-INF/todo-servlet.xml Modified

New Lines

    <bean
        class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix">
            <value>/WEB-INF/views/</value>
        </property>
        <property name="suffix">
            <value>.jsp</value>
        </property>
    </bean>

Redirect to Log in JSP

  • View Resolver in todo-servlet.xml
  • Update LoginController
  • Remove @ResponseBody
  • More about View Resolver

Snippets

  <bean
        class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix">
            <value>/WEB-INF/views/</value>
        </property>
        <property name="suffix">
            <value>.jsp</value>
        </property>
    </bean>

Step 14 : DispatcherServlet and Log4j

/pom.xml Modified

New Lines

<dependency>
	<groupId>log4j</groupId>
	<artifactId>log4j</artifactId>
	<version>1.2.17</version>
</dependency>
/src/main/resources/log4j.properties New
log4j.rootLogger=TRACE, Appender1, Appender2
 
log4j.appender.Appender1=org.apache.log4j.ConsoleAppender
log4j.appender.Appender1.layout=org.apache.log4j.PatternLayout
log4j.appender.Appender1.layout.ConversionPattern=%-7p %d [%t] %c %x - %m%n
 
/src/main/webapp/WEB-INF/views/login.jsp Modified

New Lines

    <form action="/spring-mvc/login" method="POST">
/src/main/webapp/WEB-INF/views/welcome.jsp Modified

New Lines

Welcome ${name}. 

Understanding DispatcherServlet and Spring MVC Request Flow

What We Want to Do

  • Understand the importance of DispatcherServlet in Spring MVC.
  • Add Log4j for logging to better understand the request flow.

Spring MVC Request Flow

  1. DispatcherServlet receives the HTTP request.
  2. It identifies the appropriate controller based on the URL.
  3. Controller executes the business logic.
  4. Controller returns:
    • Model (data)
    • View Name (logical view)
  5. DispatcherServlet resolves the view using the ViewResolver.
  6. DispatcherServlet makes the model available to the view and executes it.
  7. DispatcherServlet returns the HTTP response back to the client.

Flow diagram reference: Spring MVC Flow


Step 15 – Show UserID and Password on the Welcome Page (ModelMap and @RequestParam)

  • Display user ID and password on the welcome page (without using Spring Security).
  • Use ModelMap to pass data from controller to view.
  • Use @RequestParam to capture request parameters.

Example Controller

@Controller
public class WelcomeController {

    @RequestMapping(value = "/welcome", method = RequestMethod.POST)
    public String showWelcomePage(@RequestParam String name,
                                  @RequestParam String password,
                                  ModelMap model) {
        model.addAttribute("name", name);
        model.addAttribute("password", password);
        return "welcome"; // Returns welcome.jsp
    }
}
/src/main/java/com/in28minutes/springmvc/login/LoginController.java Modified

New Lines


	@RequestMapping(value = "/login", method = RequestMethod.POST)
	public String handleUserLogin(ModelMap model, @RequestParam String name,
			@RequestParam String password) {
		model.put("name", name);
		model.put("password", password);
		return "welcome";
	}

/src/main/webapp/WEB-INF/views/welcome.jsp Modified

New Lines

Welcome ${name}. You entered ${password}

Step 16 : LoginService and Remove all JEE Servlets based code

  • Use LoginService to validate userid and password.
  • Remove all the old controller code and lets use only Spring MVC here on.
  • For now : We are not using Spring Autowiring for LoginService.
  • Change URL to http://localhost:8080/login
/src/main/java/com/in28minutes/jee/LoginService.java Deleted
/src/main/java/com/in28minutes/jee/LoginServlet.java Deleted
/src/main/java/com/in28minutes/springmvc/login/LoginController.java Deleted
/src/main/java/com/in28minutes/login/LoginController.java New
package com.in28minutes.login;

import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

import com.in28minutes.login.LoginService;

@Controller
public class LoginController {

	private LoginService loginService = new LoginService();

	@RequestMapping(value = "/login", method = RequestMethod.GET)
	public String showLoginPage() {
		return "login";
	}

	@RequestMapping(value = "/login", method = RequestMethod.POST)
	public String handleUserLogin(ModelMap model, @RequestParam String name,
			@RequestParam String password) {

		if (!loginService.validateUser(name, password)) {
			model.put("errorMessage", "Invalid Credentials");
			return "login";
		}

		model.put("name", name);
		return "welcome";
	}
}
/src/main/java/com/in28minutes/login/LoginService.java New
package com.in28minutes.login;

public class LoginService {
	public boolean validateUser(String user, String password) {
		return user.equalsIgnoreCase("in28Minutes") && password.equals("dummy");
	}

}
/src/main/webapp/WEB-INF/views/login.jsp Modified

New Lines

    <form action="/login" method="POST">
/src/main/webapp/WEB-INF/views/welcome.jsp Modified

New Lines

Welcome ${name}. You are now authenticated.
/src/main/webapp/WEB-INF/web.xml Modified

New Lines

        <url-pattern>/</url-pattern>

Step 17 – Spring Auto-wiring and Dependency Management (@Autowired and @Service)

In this step, we will learn about Spring’s dependency injection and how auto-wiring simplifies wiring of beans.

Key Concepts

  • Dependency Management: Spring manages object creation and wiring, reducing boilerplate code.
  • @Service: Marks a class as a service layer component (Spring-managed bean).
  • @Autowired: Automatically injects dependent beans into a class.
/src/main/java/com/in28minutes/login/LoginController.java Modified

New Lines

import org.springframework.beans.factory.annotation.Autowired;
	@Autowired
	private LoginService loginService;
/src/main/java/com/in28minutes/login/LoginService.java Modified

New Lines

import org.springframework.stereotype.Service;
@Service
public class LoginService { }

Other Introduction Sections

| Title | Category | Github | |——————–|:————:|—————————————————————————————————————————————————————| | Eclipse in 5 Steps | Introduction | Project Folder on Github | | Maven in 5 Steps | Introduction | Project Folder on Github | | JUnit in 5 Steps | Introduction | Project Folder on Github | | Mockito in 5 Steps | Introduction | Project Folder on Github | | IntelliJ IDEA | Introduction | Reference Link |

Just Released