Repository navigation
Expand file tree
/
Copy pathExpStarterComposition.java
More file actions
129 lines (108 loc) · 5.78 KB
/
Copy pathExpStarterComposition.java
File metadata and controls
129 lines (108 loc) · 5.78 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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
// Simula cómo un starter compone auto-configuraciones.
// spring-boot-starter-web, por ejemplo, encadena Tomcat + Jackson + DispatcherServlet.
// Cada auto-config puede depender de que otra ya haya corrido (@AutoConfigureAfter).
public class ExpStarterComposition {
// ── Entorno de contexto ───────────────────────────────────────────────────
static class AppContext {
private final Map<String, Object> beans = new HashMap<>();
private final Map<String, String> props = new HashMap<>();
void setProperty(String key, String value) { props.put(key, value); }
String getProperty(String key) { return props.getOrDefault(key, ""); }
void registerBean(String name, Object bean) {
beans.put(name, bean);
System.out.println(" bean registrado: " + name);
}
boolean hasBean(String name) { return beans.containsKey(name); }
}
// ── Interfaz de auto-configuración ────────────────────────────────────────
// Equivale a una clase anotada con @AutoConfiguration en Spring Boot
interface AutoConfiguration {
String name();
// Condición de activación — @ConditionalOnProperty, @ConditionalOnClass, etc.
boolean shouldApply(AppContext ctx);
void apply(AppContext ctx);
}
// ── Auto-configuraciones del starter web ─────────────────────────────────
// @AutoConfiguration — registra el servidor Tomcat embebido
static class TomcatAutoConfig implements AutoConfiguration {
@Override public String name() { return "TomcatAutoConfig"; }
@Override public boolean shouldApply(AppContext ctx) { return true; }
@Override public void apply(AppContext ctx) {
ctx.registerBean("embeddedTomcat", "Tomcat:8080");
}
}
// @AutoConfiguration @AutoConfigureAfter(TomcatAutoConfig)
// @ConditionalOnBean("embeddedTomcat")
static class DispatcherServletAutoConfig implements AutoConfiguration {
@Override public String name() { return "DispatcherServletAutoConfig"; }
@Override public boolean shouldApply(AppContext ctx) {
// Solo se aplica si Tomcat ya fue registrado
return ctx.hasBean("embeddedTomcat");
}
@Override public void apply(AppContext ctx) {
ctx.registerBean("dispatcherServlet", "DispatcherServlet → /");
}
}
// @AutoConfiguration @ConditionalOnClass("com.fasterxml.jackson.databind.ObjectMapper")
static class JacksonAutoConfig implements AutoConfiguration {
@Override public String name() { return "JacksonAutoConfig"; }
@Override public boolean shouldApply(AppContext ctx) {
// Simula @ConditionalOnClass — en prod verificaría si Jackson está en el classpath
return "true".equals(ctx.getProperty("jackson.enabled"));
}
@Override public void apply(AppContext ctx) {
if (!ctx.hasBean("objectMapper")) {
// @ConditionalOnMissingBean — no sobreescribe el del usuario
ctx.registerBean("objectMapper", "JacksonObjectMapper (default)");
}
}
}
// ── StarterRegistry — simula spring.factories / AutoConfiguration.imports ─
static class StarterRegistry {
// Mapa: nombre del starter → lista de auto-configs que compone
private final Map<String, List<AutoConfiguration>> starters = new LinkedHashMap<>();
void registerStarter(String name, List<AutoConfiguration> configs) {
starters.put(name, configs);
}
void run(String starterName, AppContext ctx) {
List<AutoConfiguration> configs = starters.getOrDefault(starterName, List.of());
System.out.println(" Cargando " + configs.size() + " auto-config(s) de " + starterName + ":");
for (AutoConfiguration config : configs) {
if (config.shouldApply(ctx)) {
System.out.println(" [APPLY] " + config.name());
config.apply(ctx);
} else {
System.out.println(" [SKIP] " + config.name() + " (condición no cumplida)");
}
}
}
}
public static void main(String[] args) {
StarterRegistry registry = new StarterRegistry();
// spring-boot-starter-web compone estas auto-configs en orden
registry.registerStarter("spring-boot-starter-web", List.of(
new TomcatAutoConfig(),
new JacksonAutoConfig(), // Jackson puede correr antes o después — no depende de Tomcat
new DispatcherServletAutoConfig()
));
System.out.println("=== Sin Jackson en classpath ===");
AppContext ctx1 = new AppContext();
// jackson.enabled no está → JacksonAutoConfig omitido
registry.run("spring-boot-starter-web", ctx1);
System.out.println("\n=== Con Jackson disponible ===");
AppContext ctx2 = new AppContext();
ctx2.setProperty("jackson.enabled", "true");
registry.run("spring-boot-starter-web", ctx2);
System.out.println("\n=== Con bean objectMapper del usuario (no se sobreescribe) ===");
AppContext ctx3 = new AppContext();
ctx3.setProperty("jackson.enabled", "true");
ctx3.registerBean("objectMapper", "MyCustomObjectMapper"); // registrado antes del starter
registry.run("spring-boot-starter-web", ctx3);
}
}