- Introduction to Spring Framework
- Inversion of Control (IoC)
- Dependency Injection (DI)
- Spring IoC Container
- Beans
- Bean Scopes
- Bean Lifecycle
- Configuration Styles
- Annotations
- Aspect-Oriented Programming (AOP)
- Spring Expression Language (SpEL)
- Validation, Data Binding, and Type Conversion
The Spring Framework is an open-source application framework and inversion of control container for the Java platform. It provides comprehensive infrastructure support for developing Java applications, promoting good programming practices by utilizing design patterns like Dependency Injection (DI) and Aspect-Oriented Programming (AOP).
Key features of Spring Framework:
- Lightweight and minimally invasive development with Plain Old Java Objects (POJOs)
- Dependency injection to promote loose coupling
- Declarative programming with Aspect-Oriented Programming (AOP)
- Reduction of boilerplate code
Inversion of Control is a principle in software engineering where the control of objects or portions of a program is transferred to a container or framework. It's a broad term that includes, but is not limited to, Dependency Injection.
In traditional programming, the custom code that expresses the purpose of the program calls into reusable libraries to take care of generic tasks. With IoC, it's the framework that calls into the custom, or task-specific, code.
Benefits of IoC:
- Decoupling the execution of a task from its implementation
- Making it easier to switch between different implementations
- Greater modularity of a program
- Greater ease in testing a program by isolating components
Dependency Injection is a specific form of IoC where the creation and binding of dependent objects are separated from the class that depends on them. Instead of a class creating its dependencies, they are "injected" into the class from the outside.
There are three common types of dependency injection:
- Constructor Injection
- Setter Injection
- Field Injection
Example of Constructor Injection:
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
}
Benefits of DI:
- Reduced coupling between classes
- Increased modularity
- Easier unit testing with mock objects
The Spring IoC Container is the core of the Spring Framework. It creates objects, configures them, and manages their complete lifecycle. The container uses Dependency Injection to manage the components that make up an application.
There are two types of IoC containers in Spring:
-
BeanFactory: This is the simplest container, providing basic support for DI. It's mostly used in light-weight applications.
-
ApplicationContext: This is an advanced container which includes all functionality of BeanFactory and adds more enterprise-specific functionality. It's the preferred container.
Example of creating an ApplicationContext:
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
// or
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
In Spring, the objects that form the backbone of your application and that are managed by the Spring IoC container are called beans. A bean is an object that is instantiated, assembled, and otherwise managed by a Spring IoC container.
Beans are created with the configuration metadata that you supply to the container, for example, in the form of XML <bean/>
definitions or Java annotations.
Example of a bean definition in XML:
<bean id="userService" class="com.example.UserService">
<constructor-arg ref="userRepository" />
</bean>
Example of a bean definition using Java annotation:
@Component
public class UserService {
// ...
}
Spring Beans can be defined as having one of the following scopes:
- singleton (default): One instance of the bean is created for the entire application
- prototype: A new instance is created every time the bean is requested
- request: One instance per HTTP request (only valid in web-aware Spring ApplicationContext)
- session: One instance per HTTP session (only valid in web-aware Spring ApplicationContext)
- application: One instance per ServletContext (only valid in web-aware Spring ApplicationContext)
- websocket: One instance per WebSocket session (only valid in web-aware Spring ApplicationContext)
Example of defining a prototype bean:
@Component
@Scope("prototype")
public class PrototypeBean {
// ...
}
Spring manages the lifecycle of a bean. The basic lifecycle of a bean is:
- Instantiate
- Populate Properties
- Call setBeanName method
- Call setBeanFactory method
- Call setApplicationContext method
- PreInitialization (BeanPostProcessors)
- AfterPropertiesSet method
- Custom Init method
- PostInitialization (BeanPostProcessors)
- Bean is ready to use
- Container is shut down
- DisposableBean's destroy method
- Custom Destroy method
You can hook into this lifecycle using various annotations or XML configurations:
@Component
public class MyBean implements InitializingBean, DisposableBean {
@PostConstruct
public void postConstruct() {
System.out.println("PostConstruct method called");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("AfterPropertiesSet method called");
}
@PreDestroy
public void preDestroy() {
System.out.println("PreDestroy method called");
}
@Override
public void destroy() throws Exception {
System.out.println("DisposableBean's Destroy method called");
}
}
Spring supports several styles of configuration:
-
XML-based configuration:
<beans> <bean id="userService" class="com.example.UserService"> <constructor-arg ref="userRepository" /> </bean> </beans>
-
Annotation-based configuration:
@Configuration public class AppConfig { @Bean public UserService userService(UserRepository userRepository) { return new UserService(userRepository); } }
-
Java-based configuration:
@Configuration public class AppConfig { @Bean public UserService userService() { return new UserService(userRepository()); } @Bean public UserRepository userRepository() { return new JpaUserRepository(); } }
-
Groovy Bean Definition DSL:
beans { userRepository(JpaUserRepository) userService(UserService, ref('userRepository')) }
Spring provides a wide range of annotations to simplify configuration and reduce boilerplate code. Some of the most commonly used annotations include:
@Component
: Indicates that an annotated class is a "component" and should be auto-detected for dependency injection.@Autowired
: Marks a constructor, field, setter method, or config method to be autowired by Spring's dependency injection facilities.@Configuration
: Indicates that a class declares one or more@Bean
methods and may be processed by the Spring container to generate bean definitions.@Bean
: Indicates that a method produces a bean to be managed by the Spring container.@Value
: Indicates that an annotated member should be injected with an externalized property value.@PropertySource
: Provides a convenient and declarative mechanism for adding a PropertySource to Spring's Environment.
Example:
@Configuration
@PropertySource("classpath:application.properties")
public class AppConfig {
@Value("${database.url}")
private String databaseUrl;
@Bean
public DataSource dataSource() {
// create and configure DataSource
}
}
AOP is a programming paradigm that aims to increase modularity by allowing the separation of cross-cutting concerns. It does this by adding additional behavior to existing code without modifying the code itself.
Key AOP concepts:
- Aspect: A modularization of a concern that cuts across multiple classes.
- Join point: A point during the execution of a program, such as the execution of a method or the handling of an exception.
- Advice: Action taken by an aspect at a particular join point.
- Pointcut: A predicate that matches join points.
Example of an aspect in Spring:
@Aspect
@Component
public class LoggingAspect {
@Before("execution(* com.example.service.*.*(..))")
public void logBefore(JoinPoint joinPoint) {
System.out.println("Before method: " + joinPoint.getSignature().getName());
}
}
SpEL is a powerful expression language that supports querying and manipulating an object graph at runtime. It can be used with XML or annotation-based configurations.
Features of SpEL:
- Literal expressions
- Boolean and relational operators
- Regular expressions
- Class expressions
- Accessing properties, arrays, lists, maps
- Method invocation
- Relational operators
- Assignment
- Calling constructors
- Bean references
- Array construction
Example:
@Value("#{systemProperties['user.region']}")
private String region;
@Value("#{T(java.lang.Math).random() * 100.0}")
private double randomNumber;
Spring provides a Validator interface that you can use to validate objects. The DataBinder is responsible for binding user input to target objects and can be configured with custom type converters and validators.
Example of a custom validator:
public class PersonValidator implements Validator {
@Override
public boolean supports(Class<?> clazz) {
return Person.class.isAssignableFrom(clazz);
}
@Override
public void validate(Object target, Errors errors) {
Person person = (Person) target;
if (person.getName().isEmpty()) {
errors.rejectValue("name", "name.empty");
}
// more validation...
}
}
Spring also supports JSR-303/JSR-349 Bean Validation via LocalValidatorFactoryBean, which adapts Spring's Validator interface to the Bean Validation API.
public class Person {
@NotNull
@Size(min=2, max=30)
private String name;
@Min(18)
private int age;
// getters and setters...
}
These core concepts form the foundation of the Spring Framework. Understanding these will greatly aid in effectively using Spring and its various modules.