Repository navigation
Expand file tree
/
Copy pathExpDependencyInjection.java
More file actions
102 lines (84 loc) · 4.2 KB
/
Copy pathExpDependencyInjection.java
File metadata and controls
102 lines (84 loc) · 4.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
import java.lang.reflect.Field;
// DI styles: constructor (preferred) vs setter vs field injection.
// Spring supports all three; this demo shows the trade-offs with plain Java.
public class ExpDependencyInjection {
interface RepoUsuario {
String buscar(int id);
}
static class RepoUsuarioImpl implements RepoUsuario {
@Override
public String buscar(int id) {
return "usuario-" + id;
}
}
// ── Constructor injection ──────────────────────────────────────────────────
// @Component
static class ServicioConstructor {
// final: la dependencia es inmutable después de construir — imposible olvidar inyectarla
private final RepoUsuario repo;
// @Autowired (implícito en Spring cuando hay un solo constructor)
ServicioConstructor(RepoUsuario repo) {
this.repo = repo;
}
public String obtener(int id) { return repo.buscar(id); }
}
// ── Setter injection ──────────────────────────────────────────────────────
// @Component
static class ServicioSetter {
// No es final: puede ser null hasta que el setter sea llamado
private RepoUsuario repo;
// @Autowired
public void setRepo(RepoUsuario repo) {
this.repo = repo;
}
public String obtener(int id) {
if (repo == null) throw new IllegalStateException("repo no inyectado");
return repo.buscar(id);
}
}
// ── Field injection (simulado vía reflexión) ──────────────────────────────
// @Component
static class ServicioCampo {
// @Autowired — Spring escribe directamente en el campo privado
private RepoUsuario repo; // Spring inyecta esto por reflexión
public String obtener(int id) {
if (repo == null) throw new IllegalStateException("repo no inyectado");
return repo.buscar(id);
}
}
// Inyecta un valor en un campo privado por reflexión, tal como hace Spring
static void inyectarCampo(Object target, String nombre, Object valor) throws Exception {
Field f = target.getClass().getDeclaredField(nombre);
f.setAccessible(true);
f.set(target, valor);
}
public static void main(String[] args) throws Exception {
RepoUsuario repo = new RepoUsuarioImpl();
System.out.println("=== Constructor injection ===");
// Spring llama al constructor con los beans resueltos del contexto
ServicioConstructor sc = new ServicioConstructor(repo);
System.out.println(sc.obtener(1));
// sc.repo no puede ser null — el compilador lo garantiza (final)
System.out.println("\n=== Setter injection ===");
ServicioSetter ss = new ServicioSetter();
// Spring llama al setter después de instanciar con el constructor por defecto
ss.setRepo(repo);
System.out.println(ss.obtener(1));
// Problema: si olvidamos llamar al setter, NPE en tiempo de ejecución
System.out.println("\n=== Field injection (reflexión) ===");
ServicioCampo sf = new ServicioCampo();
inyectarCampo(sf, "repo", repo);
System.out.println(sf.obtener(1));
// Problema: imposible hacer final, el campo queda oculto → dificulta tests unitarios
System.out.println("\n=== Por qué preferir constructor injection ===");
System.out.println("Constructor → final, fail-fast, testable sin contenedor");
System.out.println("Setter → opcional/reconfigurable, pero no fail-fast");
System.out.println("Campo → conciso, pero oculta dependencias y rompe encapsulación");
// Demostración de fail-fast: constructor impide crear un objeto inválido
try {
new ServicioConstructor(null); // se podría añadir Objects.requireNonNull
} catch (Exception e) {
System.out.println("Constructor detectó null: " + e.getMessage());
}
}
}