Spring Framework
Spring Framework is the most popular application development framework for enterprise Java. It provides comprehensive infrastructure support for developing Java applications.
Framework Overview
What is Spring?
Spring Framework is an open-source framework that makes Java/J2EE development easier. It provides a lightweight container that manages the lifecycle and configuration of application objects.
Core Modules
Core Container
Beans : Bean factory
Core : IoC and DI
Context : ApplicationContext
SpEL : Spring Expression Language
Data Access
JDBC : Database access
ORM : Hibernate, JPA integration
Transactions : Transaction management
Web
Web : Basic web support
WebMVC : MVC framework
WebFlux : Reactive web
AOP
Aspect-Oriented Programming
Cross-cutting concerns
Declarative transactions
Inversion of Control (IoC)
Understanding IoC
Inversion of Control is a principle where the control of object creation and dependency management is inverted from the application code to the framework.Traditional Approach: public class UserService {
private UserRepository userRepository = new UserRepository ();
// Tightly coupled
}
IoC Approach: public class UserService {
private UserRepository userRepository ;
// Dependency injected by Spring
public UserService ( UserRepository userRepository ) {
this . userRepository = userRepository;
}
}
Dependency Injection (DI)
Constructor Injection
Setter Injection
Field Injection
Recommended approach - immutable dependencies@ Service
public class UserService {
private final UserRepository userRepository ;
private final EmailService emailService ;
@ Autowired // Optional in Spring 4.3+
public UserService ( UserRepository userRepository ,
EmailService emailService ) {
this . userRepository = userRepository;
this . emailService = emailService;
}
}
Optional dependencies @ Service
public class UserService {
private UserRepository userRepository ;
@ Autowired
public void setUserRepository ( UserRepository userRepository ) {
this . userRepository = userRepository;
}
}
Not recommended - hard to test @ Service
public class UserService {
@ Autowired
private UserRepository userRepository ;
}
Spring Beans
Bean Definition
Annotation-Based
Java Configuration
XML Configuration
@ Component
public class UserService {
// Automatically detected by component scanning
}
@ Repository // Specialized @Component for DAOs
public class UserRepository {}
@ Service // Specialized @Component for services
public class EmailService {}
@ Controller // Specialized @Component for web controllers
public class UserController {}
Bean Scopes
Singleton
Prototype
Request
Session
Default scope - One instance per Spring container@ Component
@ Scope ( "singleton" ) // Default, can be omitted
public class UserService {}
New instance for each request @ Component
@ Scope ( "prototype" )
public class ShoppingCart {}
One instance per HTTP request (Web) @ Component
@ Scope ( value = WebApplicationContext . SCOPE_REQUEST )
public class LoginForm {}
One instance per HTTP session (Web) @ Component
@ Scope ( value = WebApplicationContext . SCOPE_SESSION )
public class UserPreferences {}
Bean Lifecycle
@ Component
public class LifecycleBean {
// 1. Constructor
public LifecycleBean () {
System . out . println ( "1. Constructor called" );
}
// 2. Dependency Injection
@ Autowired
public void setDependency ( SomeDependency dependency ) {
System . out . println ( "2. Dependency injected" );
}
// 3. Post-initialization
@ PostConstruct
public void init () {
System . out . println ( "3. @PostConstruct" );
}
// 4. Custom init method
@ Bean ( initMethod = "customInit" )
public void customInit () {
System . out . println ( "4. Custom init" );
}
// 5. Pre-destruction
@ PreDestroy
public void cleanup () {
System . out . println ( "5. @PreDestroy" );
}
}
Aspect-Oriented Programming (AOP)
AOP Concepts
AOP allows you to modularize cross-cutting concerns (logging, security, transactions) that would otherwise be scattered across multiple classes. Key Concepts:
Aspect : Module containing cross-cutting logic
Join Point : Point in program execution
Advice : Action taken at join point
Pointcut : Expression that matches join points
@Before : Before method execution
@After : After method execution (finally)
@AfterReturning : After successful execution
@AfterThrowing : After exception
@Around : Before and after execution
AOP Examples
Logging Aspect
Performance Monitoring
Transaction Management
@ Aspect
@ Component
public class LoggingAspect {
@ Before ( "execution(* com.example.service.*.*(..))" )
public void logBefore ( JoinPoint joinPoint ) {
System . out . println ( "Executing: " +
joinPoint . getSignature (). getName ());
}
@ AfterReturning (
pointcut = "execution(* com.example.service.*.*(..))" ,
returning = "result"
)
public void logAfterReturning ( JoinPoint joinPoint , Object result ) {
System . out . println ( "Method returned: " + result);
}
@ AfterThrowing (
pointcut = "execution(* com.example.service.*.*(..))" ,
throwing = "error"
)
public void logAfterThrowing ( JoinPoint joinPoint , Throwable error ) {
System . out . println ( "Exception: " + error . getMessage ());
}
}
Spring Configuration
Application Configuration
@ Configuration
@ ComponentScan ( basePackages = "com.example" )
@ EnableTransactionManagement
@ EnableAspectJAutoProxy
public class AppConfig {
@ Bean
public DataSource dataSource () {
DriverManagerDataSource dataSource = new DriverManagerDataSource ();
dataSource . setDriverClassName ( "com.mysql.cj.jdbc.Driver" );
dataSource . setUrl ( "jdbc:mysql://localhost:3306/mydb" );
dataSource . setUsername ( "root" );
dataSource . setPassword ( "password" );
return dataSource;
}
@ Bean
public JdbcTemplate jdbcTemplate ( DataSource dataSource ) {
return new JdbcTemplate (dataSource);
}
}
@ Configuration
@ PropertySource ( "classpath:application.properties" )
public class AppConfig {
@ Value ( "${db.url}" )
private String dbUrl ;
@ Value ( "${db.username}" )
private String dbUsername ;
@ Bean
public DataSource dataSource () {
DriverManagerDataSource dataSource = new DriverManagerDataSource ();
dataSource . setUrl (dbUrl);
dataSource . setUsername (dbUsername);
return dataSource;
}
}
# application.properties
db.url =jdbc:mysql://localhost:3306/mydb
db.username =root
db.password =secret
Profiles
@ Configuration
@ Profile ( "dev" )
public class DevConfig {
@ Bean
public DataSource devDataSource () {
// Development database
return new EmbeddedDatabaseBuilder ()
. setType ( EmbeddedDatabaseType . H2 )
. build ();
}
}
@ Configuration
@ Profile ( "prod" )
public class ProdConfig {
@ Bean
public DataSource prodDataSource () {
// Production database
return new DriverManagerDataSource ( "jdbc:mysql://prod-server/db" );
}
}
Activate profile:
# Command line
java -Dspring.profiles.active=prod -jar myapp.jar
# application.properties
spring.profiles.active =dev
Data Access
JDBC Template
@ Repository
public class UserRepositoryImpl implements UserRepository {
@ Autowired
private JdbcTemplate jdbcTemplate ;
@ Override
public User findById ( Long id ) {
String sql = "SELECT * FROM users WHERE id = ?" ;
return jdbcTemplate . queryForObject (sql, new Object []{id},
(rs, rowNum) -> {
User user = new User ();
user . setId ( rs . getLong ( "id" ));
user . setName ( rs . getString ( "name" ));
user . setEmail ( rs . getString ( "email" ));
return user;
});
}
@ Override
public List < User > findAll () {
String sql = "SELECT * FROM users" ;
return jdbcTemplate . query (sql,
(rs, rowNum) -> {
User user = new User ();
user . setId ( rs . getLong ( "id" ));
user . setName ( rs . getString ( "name" ));
return user;
});
}
@ Override
public int save ( User user ) {
String sql = "INSERT INTO users (name, email) VALUES (?, ?)" ;
return jdbcTemplate . update (sql, user . getName (), user . getEmail ());
}
}
Transaction Management
Always use transactions for operations that modify data to ensure data consistency.
@ Service
public class UserService {
@ Autowired
private UserRepository userRepository ;
@ Transactional
public void createUser ( User user ) {
userRepository . save (user);
// If exception occurs, transaction will rollback
}
@ Transactional ( readOnly = true )
public User getUser ( Long id ) {
return userRepository . findById (id);
}
@ Transactional (
propagation = Propagation . REQUIRED ,
isolation = Isolation . READ_COMMITTED ,
timeout = 30 ,
rollbackFor = Exception . class
)
public void complexOperation () {
// Complex transactional operation
}
}
Testing
Unit Testing
@ ExtendWith ( MockitoExtension . class )
public class UserServiceTest {
@ Mock
private UserRepository userRepository ;
@ InjectMocks
private UserService userService ;
@ Test
public void testFindUser () {
User expectedUser = new User ( 1L , "John" );
when ( userRepository . findById ( 1L )). thenReturn (expectedUser);
User actualUser = userService . findUser ( 1L );
assertEquals (expectedUser, actualUser);
verify (userRepository). findById ( 1L );
}
}
Integration Testing
@ SpringBootTest
@ AutoConfigureMockMvc
public class UserControllerIntegrationTest {
@ Autowired
private MockMvc mockMvc ;
@ Autowired
private UserRepository userRepository ;
@ Test
public void testGetUser () throws Exception {
User user = new User ( 1L , "John" );
userRepository . save (user);
mockMvc . perform ( get ( "/users/1" ))
. andExpect ( status (). isOk ())
. andExpect ( jsonPath ( "$.name" ). value ( "John" ));
}
}
Best Practices
Use Constructor Injection
Promotes immutability
Makes dependencies explicit
Easier to test
Favor Composition Over Inheritance
More flexible design
Easier to maintain
Better testability
Keep Beans Stateless
Especially for singleton beans
Thread-safe by design
Easier to scale
Use Appropriate Bean Scopes
Singleton for stateless beans
Prototype for stateful beans
Request/Session for web-specific data
Leverage Spring Boot
Auto-configuration
Embedded servers
Production-ready features
Spring Boot Rapid application development
Java Fundamentals Java basics
JVM Internals Understanding JVM