In the last couple of weeks, I have been interrogating Matt on why and how
of the 3.x plugin infra. This inevitably led to some code archaeology. I
will try to share my take out of this exercise.
1.x required users to type the fully-qualified class names of components
(appenders, layouts, etc.) in either configuration files or properties:
log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
Plugins were first introduced in 2.x to address this concern: providing a
mechanism to alias components. Later on it also got used to glue the rest
of the configuration components together. Consequently maintainers started
exercising this throughout the entire code base, whenever something needed
to be indirectly injected somewhere.
That was a great epiphany and I am in love with the plugins! It feels like
Log4j is composed of simple beans beautifully practicing
separation-of-concerns while running on a marvellous Spring-like
fully-fledged dependency injection (DI) framework... almost... sort of...
The reality is sadly a little bit different than that. In particular, I see
two major issues:
1. Missing `@ConditionalOn*` support
2. Static access to DI
I guess Matt is already working on issue #1. He is trying to make sure
`@Required` et al. annotations are executed the same way at every injection
site.
What do I mean with the static access to DI? In a `@Bean`-annotated method
of a Spring application, do you create your own `ApplicationContext`
instance and access other beans from that? Do you statically access
`ApplicationContext.getBean("foo")`? Certainly not! Though these two
examples are what we exactly do in Log4j. We create single-use
`PluginManager` instances and use it to collect plugins. We call
`DI.createInjector().getInstance("foo")`. What we should be rather doing is
to inject the `ApplicationContext` and/or the beans we are interested in,
that is, in Log4j terms, inject the `Injector` and/or the plugins we are
interested in.
Thoughts?