google-site-verification: google61fe8ba583a51912.html
Spring Core in Java

Spring Core is the foundation of the Spring Framework, providing fundamental features like Dependency Injection (DI) and Inversion of Control (IoC) that are essential for building enterprise Java applications.

1. Introduction to Spring Core

Key Concepts

  • Inversion of Control (IoC): Objects don't create their dependencies, they get injected
  • Dependency Injection (DI): Mechanism for implementing IoC
  • Bean: Objects managed by Spring IoC container
  • ApplicationContext: Spring container that manages beans

Benefits

  • Loose coupling between components
  • Easier testing
  • Better code organization
  • Reduced boilerplate code

2. Setup and Configuration

Maven Dependencies

<!-- pom.xml --> <properties> <spring.version>6.1.0</spring.version> </properties> <dependencies> <!-- Spring Core --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <!-- Spring Core (transitive) --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <!-- For annotation configuration --> <dependency> <groupId>jakarta.annotation</groupId> <artifactId>jakarta.annotation-api</artifactId> <version>2.1.1</version> </dependency> </dependencies>

3. Configuration Methods

1. XML Configuration (Traditional)

<!-- src/main/resources/applicationContext.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"> <!-- Enable component scanning --> <context:component-scan base-package="com.example"/> <!-- Bean definitions --> <bean id="userService" class="com.example.service.UserServiceImpl"> <constructor-arg ref="userRepository"/> </bean> <bean id="userRepository" class="com.example.repository.UserRepositoryImpl"/> <!-- Bean with properties --> <bean id="dataSource" class="com.example.config.DataSource"> <property name="url" value="jdbc:mysql://localhost:3306/mydb"/> <property name="username" value="root"/> <property name="password" value="password"/> <property name="maxConnections" value="10"/> </bean> </beans>

2. Java Configuration (Modern)

// AppConfig.java package com.example.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; @Configuration @ComponentScan(basePackages = "com.example") @PropertySource("classpath:application.properties") public class AppConfig { @Bean public UserService userService(UserRepository userRepository) { return new UserServiceImpl(userRepository); } @Bean public UserRepository userRepository() { return new UserRepositoryImpl(); } @Bean public DataSource dataSource() { DataSource dataSource = new DataSource(); dataSource.setUrl("jdbc:mysql://localhost:3306/mydb"); dataSource.setUsername("root"); dataSource.setPassword("password"); dataSource.setMaxConnections(10); return dataSource; } @Bean public ObjectMapper objectMapper() { return new ObjectMapper(); } }

3. Annotation Configuration (Most Common)

// AnnotationBasedConfig.java package com.example.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration @ComponentScan(basePackages = "com.example") public class AnnotationBasedConfig { // Beans will be automatically discovered via @Component, @Service, etc. }

4. Core Annotations

Stereotype Annotations

package com.example.service; import org.springframework.stereotype.Service; import org.springframework.beans.factory.annotation.Autowired; @Service // Specialized @Component for service layer public class UserService { private final UserRepository userRepository; @Autowired // Constructor injection (recommended) public UserService(UserRepository userRepository) { this.userRepository = userRepository; } // Business methods... }
package com.example.repository; import org.springframework.stereotype.Repository; @Repository // Specialized @Component for persistence layer public class UserRepositoryImpl implements UserRepository { // Data access methods... }
package com.example.controller; import org.springframework.stereotype.Controller; @Controller // Specialized @Component for web layer public class UserController { // Web handling methods... }
package com.example.component; import org.springframework.stereotype.Component; @Component // Generic Spring-managed component public class EmailService { // Utility methods... }

5. Dependency Injection Types

1. Constructor Injection (Recommended)

package com.example.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class OrderService { private final PaymentService paymentService; private final NotificationService notificationService; private final InventoryService inventoryService; // Constructor injection - dependencies are clearly defined and immutable @Autowired public OrderService(PaymentService paymentService, NotificationService notificationService, InventoryService inventoryService) { this.paymentService = paymentService; this.notificationService = notificationService; this.inventoryService = inventoryService; } public void processOrder(Order order) { inventoryService.checkAvailability(order); paymentService.processPayment(order); notificationService.sendConfirmation(order); } }

2. Setter Injection

package com.example.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class ProductService { private ProductRepository productRepository; private CategoryService categoryService; // Setter injection - useful for optional dependencies @Autowired public void setProductRepository(ProductRepository productRepository) { this.productRepository = productRepository; } @Autowired public void setCategoryService(CategoryService categoryService) { this.categoryService = categoryService; } public Product getProductById(Long id) { Product product = productRepository.findById(id); if (product != null) { product.setCategory(categoryService.getCategory(product.getCategoryId())); } return product; } }

3. Field Injection

package com.example.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class ShoppingCartService { @Autowired // Field injection - concise but less testable private ProductService productService; @Autowired private PricingService pricingService; @Autowired private DiscountService discountService; public Cart calculateTotal(Cart cart) { // Use injected dependencies return pricingService.calculateTotal(cart); } }

6. Bean Scopes

Scope Annotations

package com.example.model; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; import org.springframework.web.context.annotation.RequestScope; import org.springframework.web.context.annotation.SessionScope; @Component @Scope("singleton") // Default - one instance per Spring container public class ApplicationConfig { // Application-wide configuration } @Component @Scope("prototype") // New instance every time bean is requested public class PrototypeBean { // Stateful component that shouldn't be shared } @Component @RequestScope // Web-aware scope - one instance per HTTP request public class RequestScopedBean { // Request-specific data } @Component @SessionScope // Web-aware scope - one instance per HTTP session public class UserSession { private User currentUser; public void setCurrentUser(User user) { this.currentUser = user; } public User getCurrentUser() { return currentUser; } }

7. Complete Example Application

Domain Models

package com.example.model; public class User { private Long id; private String username; private String email; private String firstName; private String lastName; // Constructors public User() {} public User(String username, String email, String firstName, String lastName) { this.username = username; this.email = email; this.firstName = firstName; this.lastName = lastName; } // Getters and setters public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } @Override public String toString() { return "User{id=" + id + ", username='" + username + "', email='" + email + "'}"; } }

Repository Layer

package com.example.repository; import com.example.model.User; import org.springframework.stereotype.Repository; import java.util.*; @Repository public class UserRepository { private final Map<Long, User> users = new HashMap<>(); private Long currentId = 1L; public User save(User user) { if (user.getId() == null) { user.setId(currentId++); } users.put(user.getId(), user); return user; } public Optional<User> findById(Long id) { return Optional.ofNullable(users.get(id)); } public List<User> findAll() { return new ArrayList<>(users.values()); } public Optional<User> findByUsername(String username) { return users.values().stream() .filter(user -> username.equals(user.getUsername())) .findFirst(); } public void deleteById(Long id) { users.remove(id); } public boolean existsByUsername(String username) { return users.values().stream() .anyMatch(user -> username.equals(user.getUsername())); } }

Service Layer

package com.example.service; import com.example.model.User; import com.example.repository.UserRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; import java.util.Optional; @Service public class UserService { private final UserRepository userRepository; @Autowired public UserService(UserRepository userRepository) { this.userRepository = userRepository; } public User createUser(User user) { if (userRepository.existsByUsername(user.getUsername())) { throw new IllegalArgumentException("Username already exists: " + user.getUsername()); } return userRepository.save(user); } public Optional<User> getUserById(Long id) { return userRepository.findById(id); } public List<User> getAllUsers() { return userRepository.findAll(); } public Optional<User> updateUser(Long id, User userDetails) { return userRepository.findById(id) .map(existingUser -> { existingUser.setEmail(userDetails.getEmail()); existingUser.setFirstName(userDetails.getFirstName()); existingUser.setLastName(userDetails.getLastName()); return userRepository.save(existingUser); }); } public boolean deleteUser(Long id) { if (userRepository.findById(id).isPresent()) { userRepository.deleteById(id); return true; } return false; } public Optional<User> getUserByUsername(String username) { return userRepository.findByUsername(username); } }

Additional Services with Dependencies

package com.example.service; import com.example.model.User; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class NotificationService { private final EmailService emailService; private final SmsService smsService; @Autowired public NotificationService(EmailService emailService, SmsService smsService) { this.emailService = emailService; this.smsService = smsService; } public void sendWelcomeNotification(User user) { emailService.sendWelcomeEmail(user); smsService.sendWelcomeSms(user); } public void sendPasswordResetNotification(User user, String resetToken) { emailService.sendPasswordResetEmail(user, resetToken); } } @Service class EmailService { public void sendWelcomeEmail(User user) { System.out.println("Sending welcome email to: " + user.getEmail()); // Actual email sending logic } public void sendPasswordResetEmail(User user, String resetToken) { System.out.println("Sending password reset email to: " + user.getEmail()); // Actual email sending logic } } @Service class SmsService { public void sendWelcomeSms(User user) { System.out.println("Sending welcome SMS to user: " + user.getUsername()); // Actual SMS sending logic } }

Configuration Class

package com.example.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; import org.springframework.core.env.Environment; @Configuration @ComponentScan(basePackages = "com.example") @PropertySource("classpath:application.properties") public class AppConfig { // Additional beans can be defined here if needed @Bean public String applicationName(Environment env) { return env.getProperty("app.name", "Spring Core Demo Application"); } @Bean public Integer maxUsers(Environment env) { return env.getProperty("app.max-users", Integer.class, 1000); } }

Application Properties

# src/main/resources/application.properties app.name=Spring Core Demo app.version=1.0.0 app.max-users=500 database.url=jdbc:mysql://localhost:3306/demo database.username=admin database.password=secret logging.level.com.example=DEBUG

Main Application Class

package com.example; import com.example.config.AppConfig; import com.example.model.User; import com.example.service.NotificationService; import com.example.service.UserService; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import java.util.Scanner; public class SpringCoreApplication { public static void main(String[] args) { // Create Spring application context ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); // Get beans from context UserService userService = context.getBean(UserService.class); NotificationService notificationService = context.getBean(NotificationService.class); // Demo application runUserManagementDemo(userService, notificationService); // Close context ((AnnotationConfigApplicationContext) context).close(); } private static void runUserManagementDemo(UserService userService, NotificationService notificationService) { Scanner scanner = new Scanner(System.in); while (true) { System.out.println("\n=== User Management System ==="); System.out.println("1. Create User"); System.out.println("2. List All Users"); System.out.println("3. Find User by ID"); System.out.println("4. Exit"); System.out.print("Choose option: "); int choice = scanner.nextInt(); scanner.nextLine(); // consume newline switch (choice) { case 1: createUserDemo(userService, notificationService, scanner); break; case 2: listAllUsersDemo(userService); break; case 3: findUserByIdDemo(userService, scanner); break; case 4: System.out.println("Exiting..."); return; default: System.out.println("Invalid option!"); } } } private static void createUserDemo(UserService userService, NotificationService notificationService, Scanner scanner) { System.out.print("Enter username: "); String username = scanner.nextLine(); System.out.print("Enter email: "); String email = scanner.nextLine(); System.out.print("Enter first name: "); String firstName = scanner.nextLine(); System.out.print("Enter last name: "); String lastName = scanner.nextLine(); try { User user = new User(username, email, firstName, lastName); User createdUser = userService.createUser(user); System.out.println("User created: " + createdUser); // Send welcome notification notificationService.sendWelcomeNotification(createdUser); } catch (Exception e) { System.out.println("Error creating user: " + e.getMessage()); } } private static void listAllUsersDemo(UserService userService) { System.out.println("\n=== All Users ==="); userService.getAllUsers().forEach(System.out::println); } private static void findUserByIdDemo(UserService userService, Scanner scanner) { System.out.print("Enter user ID: "); Long id = scanner.nextLong(); userService.getUserById(id).ifPresentOrElse( user -> System.out.println("Found user: " + user), () -> System.out.println("User not found with ID: " + id) ); } }

8. Advanced Spring Core Features

Bean Lifecycle Callbacks

package com.example.component; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.stereotype.Component; import jakarta.annotation.PostConstruct; import jakarta.annotation.PreDestroy; @Component public class DatabaseConnectionPool implements InitializingBean, DisposableBean { private boolean connected = false; // 1. @PostConstruct annotation (recommended) @PostConstruct public void init() { System.out.println("Initializing database connection pool..."); connect(); connected = true; System.out.println("Database connection pool initialized."); } // 2. InitializingBean interface @Override public void afterPropertiesSet() throws Exception { System.out.println("afterPropertiesSet() called"); // This runs after @PostConstruct } // 3. @PreDestroy annotation (recommended) @PreDestroy public void cleanup() { System.out.println("Cleaning up database connection pool..."); disconnect(); connected = false; System.out.println("Database connection pool cleaned up."); } // 4. DisposableBean interface @Override public void destroy() throws Exception { System.out.println("destroy() called"); // This runs after @PreDestroy } private void connect() { // Actual connection logic System.out.println("Connected to database"); } private void disconnect() { // Actual disconnection logic System.out.println("Disconnected from database"); } public boolean isConnected() { return connected; } }

Conditional Bean Configuration

package com.example.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; @Configuration public class FeatureConfig { @Bean @Profile("dev") // Only created when 'dev' profile is active public DevelopmentService developmentService() { return new DevelopmentService(); } @Bean @Profile("prod") // Only created when 'prod' profile is active public ProductionService productionService() { return new ProductionService(); } @Bean @Conditional(WindowsCondition.class) // Conditional based on custom condition public WindowsService windowsService() { return new WindowsService(); } @Bean @Conditional(LinuxCondition.class) public LinuxService linuxService() { return new LinuxService(); } } // Custom condition for Windows class WindowsCondition implements org.springframework.context.annotation.Condition { @Override public boolean matches(org.springframework.context.annotation.ConditionContext context, org.springframework.core.type.AnnotatedTypeMetadata metadata) { return System.getProperty("os.name").toLowerCase().contains("windows"); } } // Custom condition for Linux class LinuxCondition implements org.springframework.context.annotation.Condition { @Override public boolean matches(org.springframework.context.annotation.ConditionContext context, org.springframework.core.type.AnnotatedTypeMetadata metadata) { return System.getProperty("os.name").toLowerCase().contains("linux"); } } class DevelopmentService { public String getEnvironment() { return "Development"; } } class ProductionService { public String getEnvironment() { return "Production"; } } class WindowsService { public String getOS() { return "Windows"; } } class LinuxService { public String getOS() { return "Linux"; } }

Factory Beans

package com.example.factory; import org.springframework.beans.factory.FactoryBean; import org.springframework.stereotype.Component; @Component public class ConnectionFactoryBean implements FactoryBean<DatabaseConnection> { private String url; private String username; private String password; @Override public DatabaseConnection getObject() throws Exception { DatabaseConnection connection = new DatabaseConnection(); connection.setUrl(this.url); connection.setUsername(this.username); connection.setPassword(this.password); connection.connect(); return connection; } @Override public Class<?> getObjectType() { return DatabaseConnection.class; } @Override public boolean isSingleton() { return true; // Return same instance every time } // Setters for configuration public void setUrl(String url) { this.url = url; } public void setUsername(String username) { this.username = username; } public void setPassword(String password) { this.password = password; } } class DatabaseConnection { private String url; private String username; private String password; private boolean connected; public void connect() { System.out.println("Connecting to: " + url); this.connected = true; } public void disconnect() { this.connected = false; } // Getters and setters public void setUrl(String url) { this.url = url; } public void setUsername(String username) { this.username = username; } public void setPassword(String password) { this.password = password; } public boolean isConnected() { return connected; } }

9. Testing Spring Applications

Spring Test Configuration

package com.example.service; 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; import com.example.config.TestConfig; import com.example.model.User; import static org.junit.jupiter.api.Assertions.*; @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = TestConfig.class) class UserServiceTest { @Autowired private UserService userService; @Test void testCreateUser() { User user = new User("testuser", "[email protected]", "Test", "User"); User created = userService.createUser(user); assertNotNull(created.getId()); assertEquals("testuser", created.getUsername()); assertEquals("[email protected]", created.getEmail()); } @Test void testFindUserById() { User user = new User("finduser", "[email protected]", "Find", "User"); User created = userService.createUser(user); var found = userService.getUserById(created.getId()); assertTrue(found.isPresent()); assertEquals(created.getId(), found.get().getId()); } } // Test configuration package com.example.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration class TestConfig { @Bean public UserService userService() { return new UserService(userRepository()); } @Bean public UserRepository userRepository() { return new UserRepository(); } }

10. Best Practices

Configuration Best Practices

package com.example.bestpractices; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class BestPracticesExample { /* SPRING CORE BEST PRACTICES: 1. Use constructor injection for mandatory dependencies 2. Use @ComponentScan with specific packages 3. Prefer Java configuration over XML 4. Use @Profile for environment-specific configuration 5. Make beans immutable when possible 6. Use interface-based programming 7. Avoid circular dependencies 8. Use @Configuration for configuration classes 9. Prefer @PostConstruct/@PreDestroy over InitializingBean/DisposableBean 10. Use property files for external configuration */ // Good practice: Constructor injection with final fields @Bean public ServiceWithDependencies serviceWithDependencies(Repository repository, Helper helper) { return new ServiceWithDependencies(repository, helper); } // Good practice: Configuration in properties files @Bean public DataSource dataSource() { // Configuration should come from application.properties return new DataSource(); } } // Good practice: Interface-based design interface UserService { User createUser(User user); Optional<User> getUserById(Long id); } // Good practice: Immutable dependencies class ServiceWithDependencies { private final Repository repository; private final Helper helper; // Constructor injection makes dependencies clear and immutable public ServiceWithDependencies(Repository repository, Helper helper) { this.repository = repository; this.helper = helper; } }

Summary

Key Spring Core Concepts:

  • IoC Container: Manages object creation and dependencies
  • Dependency Injection: Three types (Constructor, Setter, Field)
  • Bean: Objects managed by Spring container
  • ApplicationContext: Central interface for configuration

Common Annotations:

  • @Component, @Service, @Repository, @Controller
  • @Autowired for dependency injection
  • @Configuration for configuration classes
  • @Bean for method-level bean definitions
  • @Profile for environment-specific beans

Configuration Methods:

  1. XML Configuration: Traditional, verbose
  2. Java Configuration: Type-safe, modern
  3. Annotation Configuration: Concise, most popular

Spring Core provides the foundation for building modular, testable, and maintainable Java applications through its powerful IoC container and dependency injection features.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper