Spring Beans

A bean is an object that the Spring IoC container creates, configures, and manages for you, instead of you calling new. Spring finds beans by scanning for stereotype annotations (@Component, @Service, @Repository, @RestController) or by reading @Bean methods in a @Configuration class. It wires them together with dependency injection, so the Controller gets the Service and the Service gets the Repository automatically. It is used for loose coupling, easy testing, a single managed lifecycle, and less boilerplate.

Types

  • Stereotype annotations (auto-detected): @Component (generic), @Service (business logic), @Repository (data access, adds exception translation), @Controller / @RestController (web layer).
  • Java config beans: @Configuration with @Bean methods, for third-party or hand-built objects.
  • Scopes: singleton (default, one per container), prototype (new each time), and web scopes request, session, application.
  • Injection styles: constructor injection (recommended), setter injection, field injection (@Autowired).

Hands-on Experiment: Creating a Spring Boot application using different environment profiles

The following steps are involved in developing this hands-on experiment.

Step 1: Generate the Project

Step 2: Project Structure

Step 3: pom.xml

Step 4: application.properties

Step 5: Create Entity Class

Step 6: Create Repository Interface

Step 7: Create Service Class

Step 8: Create Controller Class

Step 9: Create Configuration classes

Step 10: Main Application Class

Step 11: Run the Main Application Class

Step 1: Configure the Project on Spring Initializr

Go to start.spring.io and set:

  • Project: Maven
  • Language: Java
  • Spring Boot version: 3.2.x
  • Group: com.example
  • Artifact: studentbeans
  • Name: studentbeans
  • Package name: com.example.studentbeans
  • Packaging: Jar
  • Java: 17
  • Dependencies: Spring Web, Spring Data JPA, MySQL Driver

Click Generate to download the ZIP, then extract and import it into your IDE as a Maven project.

Step 2: Generated Project Structure

Step 3: Explore pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.3.5</version>
        <relativePath/>
    </parent>

    <groupId>com.example</groupId>
    <artifactId>student-beans-demo</artifactId>
    <version>1.0.0</version>
    <name>student-beans-demo</name>
    <description>Spring Beans demo - Controller, Service, Repository</description>

    <properties>
        <java.version>17</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

Step 4: application.yml

spring:
  application:
    name: student-beans-demo
  datasource:
    url: jdbc:mysql://localhost:3306/student_beans_db?createDatabaseIfNotExist=true&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC
    username: root
    password: password
    driver-class-name: com.mysql.cj.jdbc.Driver
  jpa:
    hibernate:
      ddl-auto: update
    show-sql: true
    properties:
      hibernate:
        dialect: org.hibernate.dialect.MySQLDialect

server:
  port: 8082

Step 5: Create Entity class

package com.example.studentbeans.entity;

import jakarta.persistence.*;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;

@Entity
@Table(name = "students")
public class Student {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @NotBlank(message = "Name is required")
    private String name;

    @Email(message = "Invalid email")
    @NotBlank(message = "Email is required")
    @Column(unique = true)
    private String email;

    private String course;

    public Student() {}

    public Student(String name, String email, String course) {
        this.name = name;
        this.email = email;
        this.course = course;
    }

    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }
    public String getCourse() { return course; }
    public void setCourse(String course) { this.course = course; }
}

Step 6: Create Repository interface

package com.example.studentbeans.repository;

import com.example.studentbeans.entity.Student;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.util.List;

// @Repository -> Spring registers this as a bean (persistence layer)
@Repository
public interface StudentRepository extends JpaRepository<Student, Long> {
    List<Student> findByCourse(String course);
    boolean existsByEmail(String email);
}

Step 7: Create Service class

package com.example.studentbeans.service;

import com.example.studentbeans.entity.Student;
import com.example.studentbeans.repository.StudentRepository;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.NoSuchElementException;

// @Service -> Spring registers this as a bean (business layer)
@Service
public class StudentService {

    private final StudentRepository studentRepository;

    // Constructor injection: Spring supplies the StudentRepository bean
    public StudentService(StudentRepository studentRepository) {
        this.studentRepository = studentRepository;
        System.out.println(">>> StudentService bean created, repository injected");
    }

    public Student create(Student student) {
        if (studentRepository.existsByEmail(student.getEmail())) {
            throw new IllegalArgumentException("Email already exists: " + student.getEmail());
        }
        return studentRepository.save(student);
    }

    public List<Student> findAll() {
        return studentRepository.findAll();
    }

    public List<Student> findByCourse(String course) {
        return studentRepository.findByCourse(course);
    }

    public Student findById(Long id) {
        return studentRepository.findById(id)
                .orElseThrow(() -> new NoSuchElementException("Student not found with id " + id));
    }

    public Student update(Long id, Student updated) {
        Student existing = findById(id);
        existing.setName(updated.getName());
        existing.setEmail(updated.getEmail());
        existing.setCourse(updated.getCourse());
        return studentRepository.save(existing);
    }

    public void delete(Long id) {
        studentRepository.delete(findById(id));
    }
}

Step 8: Create Controller Class

package com.example.studentbeans.controller;

import com.example.studentbeans.entity.Student;
import com.example.studentbeans.service.StudentService;
import jakarta.validation.Valid;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

// @RestController -> Spring registers this as a bean (web layer)
@RestController
@RequestMapping("/api/students")
public class StudentController {

    private final StudentService studentService;

    // Constructor injection: Spring supplies the StudentService bean
    public StudentController(StudentService studentService) {
        this.studentService = studentService;
        System.out.println(">>> StudentController bean created, service injected");
    }

    @PostMapping
    public ResponseEntity<Student> create(@Valid @RequestBody Student student) {
        return ResponseEntity.status(HttpStatus.CREATED).body(studentService.create(student));
    }

    @GetMapping
    public List<Student> getAll(@RequestParam(required = false) String course) {
        return (course == null) ? studentService.findAll() : studentService.findByCourse(course);
    }

    @GetMapping("/{id}")
    public Student getById(@PathVariable Long id) {
        return studentService.findById(id);
    }

    @PutMapping("/{id}")
    public Student update(@PathVariable Long id, @Valid @RequestBody Student student) {
        return studentService.update(id, student);
    }

    @DeleteMapping("/{id}")
    public ResponseEntity<Void> delete(@PathVariable Long id) {
        studentService.delete(id);
        return ResponseEntity.noContent().build();
    }
}

Step 9: Create below Configuration classes

package com.example.studentbeans.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.time.LocalDateTime;

// @Configuration + @Bean -> manually define a bean (useful for third-party classes)
@Configuration
public class BeanConfig {

    @Bean
    public String appStartedAt() {
        return "Student app started at " + LocalDateTime.now();
    }
}
package com.example.studentbeans.config;

import org.springframework.boot.CommandLineRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

import java.util.Arrays;

// @Component -> generic bean. Prints our beans at startup so you can SEE the container.
@Component
public class BeanInspector implements CommandLineRunner {

    private final ApplicationContext context;

    public BeanInspector(ApplicationContext context) {
        this.context = context;
    }

    @Override
    public void run(String... args) {
        System.out.println("===== Application beans in the Spring container =====");
        Arrays.stream(context.getBeanDefinitionNames())
                .filter(n -> {
                    Class<?> type = context.getType(n);
                    return n.equals("appStartedAt")
                            || (type != null && type.getName().startsWith("com.example.studentbeans"));
                })
                .sorted()
                .forEach(n -> System.out.println(" -> " + n + " : " + context.getType(n).getSimpleName()));
        System.out.println(context.getBean("appStartedAt"));
    }
}
package com.example.studentbeans.config;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.util.HashMap;
import java.util.Map;
import java.util.NoSuchElementException;

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(NoSuchElementException.class)
    public ResponseEntity<Map<String, String>> notFound(NoSuchElementException ex) {
        return ResponseEntity.status(HttpStatus.NOT_FOUND).body(Map.of("error", ex.getMessage()));
    }

    @ExceptionHandler(IllegalArgumentException.class)
    public ResponseEntity<Map<String, String>> badRequest(IllegalArgumentException ex) {
        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(Map.of("error", ex.getMessage()));
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<Map<String, String>> validation(MethodArgumentNotValidException ex) {
        Map<String, String> errors = new HashMap<>();
        ex.getBindingResult().getFieldErrors().forEach(e -> errors.put(e.getField(), e.getDefaultMessage()));
        return ResponseEntity.badRequest().body(errors);
    }
}

Step 10: Create MainApplication class

package com.example.studentbeans;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

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

Step 11: Run the StudentBeansApplication

Scroll to Top