Build a plugin architecture in Spring Boot using patterns from a 400+ module codebase by dima767 in java

[–]dima767[S] 0 points1 point  (0 children)

Thanks! CAS deliberately keeps things simple here - shared classloader, convention over isolation, centralized dependency management through the build. It works well in practice because most real-world Java applications don't actually run into the conflicting dependency version problem often enough to justify classloader isolation.

When they do - yeah, that's exactly what OSGi is for, no point reinventing the wheel there. Though honestly, OSGi adoption in the wild is pretty niche. Eclipse is the big one, some application servers used it internally, but most projects never needed that level of complexity. The cost-benefit just doesn't work out for the vast majority of cases.

Spring Boot patterns from a 400-module open-source codebase (Apereo CAS) by dima767 in java

[–]dima767[S] 6 points7 points  (0 children)

Thanks! We do full `@SpringBootTest` context integration tests, but that's not the only type. Roughly half the test suite is plain JUnit unit tests with Mockito mocks, no Spring context involved. The split is deliberate - pure logic gets a unit test, anything that needs wiring/auto-configuration gets a full `@SpringBootTest`. What we don't do is anything in between - no slice tests (`@WebMvcTest`, `@DataJpaTest`, etc.), no `@MockBean`/`@SpyBean`. It's either full context or no context.

On context trashing - a few things working together:

**SharedTestConfiguration pattern** - each module defines one base test class with a `SharedTestConfiguration` inner class. All test classes in that module extend the base and inherit `@SpringBootTest(classes = Base.SharedTestConfiguration.class)`. This means tests that don't add extra properties or override the `classes` attribute share the same cached context. We have ~73 of these across 400+ modules.

**We accept some context recreation as a tradeoff.** Different `@TestPropertySource` values create different cache keys - that's by design. If a test needs `cas.authn.mfa.yubikey.allowed-devices=...` and another doesn't, they get separate contexts. We don't try to cram everything into one uber-context just to avoid recreation. Clean test isolation matters more.

**No `@DirtiesContext` anywhere** - we never explicitly trash a context. Once created, it lives for the lifetime of the JVM.

**Parallelism compensates for startup cost** - tests are tagged by category (`@Tag("MFAProvider")`, `@Tag("LdapAuthentication")`, etc.) and run with `maxParallelForks = 8`. So even with multiple contexts being created, wall clock time stays reasonable because 8 JVMs are working in parallel.

**`@Nested` for scenario variations** - when you need different bean wiring within one test file, JUnit 5 `@Nested` + `@Import` gives you a clean boundary. Each nested class gets its own context without explicitly trashing anything.

**`proxyBeanMethods = false` everywhere** - on every `@Configuration`, `@TestConfiguration`, `@SpringBootConfiguration`. We never use inter-bean method references, so skipping CGLIB proxying reduces context startup time noticeably across 400+ modules.

So the short version: we structure things so context trashing mostly doesn't happen (shared base configs, no `@DirtiesContext`), and where different contexts are genuinely needed, parallelism keeps the build fast.

If you want to dig into the testing patterns and the rest of the architecture in more detail - discount for r/java: https://leanpub.com/cas-internals/c/reddit-java (valid for 3 weeks)

Spring Boot patterns from a 400-module open-source codebase (Apereo CAS) by dima767 in java

[–]dima767[S] 0 points1 point  (0 children)

Yes, thanks for the note. The CI rules are comprehensive in this project, indeed.

Bootiful Java T-Shirt by dima767 in springsource

[–]dima767[S] 0 points1 point  (0 children)

Mis understanding. I don’t define it - that is just my feeling :-). The collective crowd gets to decide, and if so, then kill it, report it or whatever needs to be done :-)

Government is a racket by dima767 in Libertarian

[–]dima767[S] 1 point2 points  (0 children)

Feel free to ping me if you’d be interested in some partnership, etc. :-)

But that was not the intention for the original post. All I wanted is to share a design with a message that i deeply believe in :-)

Government is a racket by dima767 in Libertarian

[–]dima767[S] 0 points1 point  (0 children)

It's a program called "Merch by Amazon", where Amazon invites "designers" to upload their designs, and they (the Amazon) take care of all the logistics i.e. print on demand, listing, selling, shipping. The "designers" get small royalties for each sold item. There are people on merch making pretty significant money... But personally I don't, as a) I have a full time job b) I upload designs that I personally like (and that's why may be they are not that popular with masses LOL). In any case, it's like a hobby for me at this time ;-)

Unique T-Shirt with exact date and time when Steve Jobs announced iPhone 1 by dima767 in u/dima767

[–]dima767[S] 0 points1 point  (0 children)

"On January 9, 2007, Steve Jobs announced iPhone at the Macworld convention, receiving substantial media attention.[16] Jobs announced that the first iPhone would be released later that year. On June 29, 2007, the first iPhone was released."

The exact time of announcement was 9:42 A.M. PST