Preparation for Enterprise Frameworks
Before tackling frameworks like Spring, it's essential to understand the architectural patterns that underlie them: Dependency Injection and Inversion of Control.
What You'll Learn
- Inversion of Control (IoC)
- Dependency Injection (DI)
- Repository and Service patterns
- Preparation for Spring Framework
Inversion of Control (IoC)
With IoC, the control of creating and managing dependencies is "inverted" from application code to an external container.
// WITHOUT IoC: the application creates dependencies
class UserService {
private UserRepository repository;
public UserService() {
// The class decides how to create the dependency
this.repository = new MySqlUserRepository();
}
}
// WITH IoC: dependencies are injected from outside
class UserService {
private final UserRepository repository;
// The dependency is provided by an external container
public UserService(UserRepository repository) {
this.repository = repository;
}
}
Dependency Injection
// 1. CONSTRUCTOR INJECTION (preferred)
class OrderService {
private final OrderRepository repository;
private final PaymentService paymentService;
public OrderService(OrderRepository repository, PaymentService paymentService) {
this.repository = repository;
this.paymentService = paymentService;
}
}
// 2. SETTER INJECTION
class OrderService {
private OrderRepository repository;
public void setRepository(OrderRepository repository) {
this.repository = repository;
}
}
// 3. FIELD INJECTION (not recommended without framework)
class OrderService {
@Inject // Requires DI framework
private OrderRepository repository;
}
Simple DI Container
import java.util.*;
import java.util.function.Supplier;
// Simple DI container
class DIContainer {
private Map<Class<?>, Supplier<?>> factories = new HashMap<>();
private Map<Class<?>, Object> singletons = new HashMap<>();
public <T> void register(Class<T> type, Supplier<T> factory) {
factories.put(type, factory);
}
public <T> void registerSingleton(Class<T> type, T instance) {
singletons.put(type, instance);
}
@SuppressWarnings("unchecked")
public <T> T get(Class<T> type) {
if (singletons.containsKey(type)) {
return (T) singletons.get(type);
}
Supplier<?> factory = factories.get(type);
if (factory == null) {
throw new RuntimeException("No registration for: " + type);
}
return (T) factory.get();
}
}
// Usage
public class Application {
public static void main(String[] args) {
DIContainer container = new DIContainer();
// Register dependencies
container.registerSingleton(UserRepository.class,
new InMemoryUserRepository());
container.register(UserService.class,
() -> new UserService(container.get(UserRepository.class)));
// Resolve
UserService service = container.get(UserService.class);
}
}
Repository Pattern
// Repository interface
interface Repository<T, ID> {
Optional<T> findById(ID id);
List<T> findAll();
T save(T entity);
void delete(T entity);
boolean existsById(ID id);
}
// Specific implementation
interface UserRepository extends Repository<User, Long> {
Optional<User> findByEmail(String email);
List<User> findByRole(String role);
}
// Concrete implementation
class JpaUserRepository implements UserRepository {
// Implementation with JPA/JDBC
}
class InMemoryUserRepository implements UserRepository {
private Map<Long, User> storage = new HashMap<>();
private long nextId = 1;
@Override
public Optional<User> findById(Long id) {
return Optional.ofNullable(storage.get(id));
}
@Override
public User save(User user) {
if (user.getId() == null) {
user.setId(nextId++);
}
storage.put(user.getId(), user);
return user;
}
// ... other methods
}
Layered Architecture
// LAYER 1: Controller (handles HTTP requests)
class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
public Response createUser(CreateUserRequest request) {
User user = userService.createUser(request);
return Response.ok(user);
}
}
// LAYER 2: Service (business logic)
class UserService {
private final UserRepository repository;
private final EmailService emailService;
public UserService(UserRepository repository, EmailService emailService) {
this.repository = repository;
this.emailService = emailService;
}
public User createUser(CreateUserRequest request) {
// Validation
if (repository.findByEmail(request.getEmail()).isPresent()) {
throw new UserAlreadyExistsException();
}
// Creation
User user = new User(request.getName(), request.getEmail());
User saved = repository.save(user);
// Side effect
emailService.sendWelcome(saved);
return saved;
}
}
// LAYER 3: Repository (data access)
// Already defined above
Next Steps: Spring Framework
What You'll Learn with Spring
- Spring Core: IoC container, Beans, @Autowired
- Spring Boot: auto-configuration, starters
- Spring Data: JPA, MongoDB, Redis
- Spring MVC: REST API, @Controller
- Spring Security: authentication, authorization
// With Spring Boot, the code becomes much simpler
@RestController
@RequestMapping("/api/users")
public class UserController {
@Autowired
private UserService userService;
@PostMapping
public ResponseEntity<User> createUser(@RequestBody CreateUserRequest request) {
User user = userService.createUser(request);
return ResponseEntity.status(HttpStatus.CREATED).body(user);
}
@GetMapping("/{id}")
public ResponseEntity<User> getUser(@PathVariable Long id) {
return userService.findById(id)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
}
@Service
public class UserService {
@Autowired
private UserRepository repository;
// Business logic...
}
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByEmail(String email);
}
Congratulations!
You've completed the Java course! Now you have a solid foundation to tackle enterprise frameworks like Spring, Jakarta EE, or Quarkus.
Related deep dive: 16 - Build Tools: Maven e Gradle.
Related deep dive: 17 - Java nel Mondo Reale.
Frequently asked questions
What is Inversion of Control (IoC) in the context of enterprise frameworks?
IoC is a design pattern where the control of creating and managing dependencies is "inverted" from application code to an external container.
What is Dependency Injection, and how does it relate to IoC?
Dependency Injection is a technique that uses an external container to manage dependencies, which is a key aspect of Inversion of Control (IoC).
What are some essential architectural patterns to understand before working with frameworks like Spring?
The article mentions Dependency Injection and Inversion of Control as essential architectural patterns to understand.
What Java course or foundation is required to tackle enterprise frameworks like Spring, Jakarta EE, or Quarkus?
A solid foundation in the Java course is necessary to tackle enterprise frameworks like Spring, Jakarta EE, or Quarkus.
Commenti
Caricamento commenti...
Sign in to comment