Build1 distinct publisher3 min readUpdated
A dev.to walkthrough pins the BeanFactoryPostProcessor and BeanPostProcessor split to container ordering: blueprints in the first pass, live objects in the second.
The Engineer · Build desk
Compiled by The EngineerSomething wrong?How this is made
A dev.to walkthrough of Spring's container startup argues that BeanFactoryPostProcessor and BeanPostProcessor are the single most confused pair of names in the framework [1]. The useful distinction is not semantic but temporal: the container starts in two separate passes, and each hook attaches to exactly one of them [2].
The first pass reads every source of configuration, the `@Configuration` classes, component scans and any XML, and turns each into a bean definition: a metadata object describing class, scope, constructor arguments and property values, with no object built yet [3]. The second pass walks those definitions, instantiates live objects, wires each one's dependencies and runs its init code [4].
BeanFactoryPostProcessor sits at the seam. It runs after every bean definition is loaded and before a single bean is instantiated [5]. It has one method, `postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)` [6], and the argument is the container holding all the definitions, so you can walk it, read any definition and change it before that bean is ever built [7]. The example given is a component that loops `getBeanDefinitionNames()` and calls `setLazyInit(true)` on each definition [8]; because it runs before instantiation, the edit lands in time for the container to read the modified blueprint when it builds [9].
Most teams already depend on this without noticing. `PropertySourcesPlaceholderConfigurer`, which turns `${...}` placeholders into real values, is a BeanFactoryPostProcessor that Spring registers for you, and it runs in the between-passes window so placeholder resolution is wired up before any bean that needs a value is built [10]. A close relative, BeanDefinitionRegistryPostProcessor, runs a moment earlier and can add whole new definitions rather than tweak existing ones; per the source, that is how `@Configuration` classes become bean definitions in the first place [11]. The rule the piece offers is blunt: a BeanFactoryPostProcessor sees definitions, never instances, and wanting the actual built object means you have reached for the wrong hook [12].
BeanPostProcessor is the other one. It runs inside the second pass, after the container has built a bean and injected its dependencies, around the moment the bean's init code runs [13]. Its two methods, `postProcessBeforeInitialization(Object bean, String beanName)` and `postProcessAfterInitialization(Object bean, String beanName)` [14], bracket that init step, the callback that fires once a bean is fully wired, such as a method marked `@PostConstruct` [15].
That ordering is the whole failure mode. An attempt to change how a bean is constructed from inside a BeanPostProcessor cannot take effect, because the object handed to it has already been instantiated and wired [1]. The reverse mistake is quieter: a BeanFactoryPostProcessor cannot inspect or wrap a live instance, because in its window none exists [2]. Wrapping a service in a transaction proxy is listed among the reasons Spring needs to reach into its own build process at all [16], and both BeanPostProcessor methods are typed to return `Object` rather than `void` [14], which is the mechanical opening for handing back a substitute; the excerpt does not spell that contract out [3].
Worth checking in your own code: whether each custom hook reaches for `getBeanDefinition` or for the built bean, since that single call decides which pass you belong in. The excerpt breaks off mid-sentence while describing `postProcessBeforeInitialization` [17], so what the container does with a returned replacement object is not documented here, and that is the detail to confirm against the reference documentation before relying on it.
Follow any of these and your For You feed starts watching them — no settings page required.
Ranked by verification strength, evidence, and original report placement.
The dev.to article describes BeanFactoryPostProcessor and BeanPostProcessor as Spring's two official extension points and calls their names the single most confused pair in the framework.
When the Spring context starts it does not build beans in one motion; it works in two distinct passes, and each of the two post-processors hooks into exactly one of them, which is the whole distinction.
In the first pass the container reads every source of configuration (@Configuration classes, component scans, any XML) and turns each into a bean definition: a plain metadata object describing the class, scope, constructor arguments and property values. No object exists yet.
In the second pass, once all definitions are collected, the container instantiates: it walks the definitions, builds live objects, wires each one's dependencies and runs its init code.
BeanFactoryPostProcessor runs at the seam between the two passes: after every bean definition is loaded, but before a single bean is instantiated.
The BeanFactoryPostProcessor interface has one method: void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory).
Evidence-backed comparisons of source perspectives and observed adoption signals. Read the methodology
Which Builder, Operator, and Investor concerns the observed source mix emphasized—not a truth score.
Evidence, demonstrated adoption, hype gap, incentives, and confidence are assessed independently, each on its own current evidence. How these are measured.
Single explainer, self-consistent but unanchored
One community tutorial carries every claim. It is internally coherent and shows real interface signatures plus runnable examples, so the mechanics are concretely specified rather than hand-waved, and it correctly names first-party machinery (PropertySourcesPlaceholderConfigurer, BeanDefinitionRegistryPostProcessor). But there is no citation of Spring reference documentation or javadoc, no version stated, no second publisher to corroborate, and the supplied body is a truncated excerpt, so verification depth is low.
No adoption evidence in cluster
The cluster contains no release, deployment, benchmark, usage-disclosure, pricing or licensing observation — only a tutorial describing pre-existing framework interfaces. Nothing in the supplied material measures how widely these hooks are used, and inferring Spring's install base from a tutorial would be speculation.
Mild rhetorical inflation, otherwise sober
Claims stay close to what the code listings show: timing, allowed access, and the return-value contract. The only stretch is promotional framing — 'the single most confused pair in the framework' and 'the confusion disappears for good' — plus a tidy two-pass narrative that omits ordering rules and the operational downside of forcing lazy init everywhere. That is a small positive gap, not overselling of capability.
Low: community explainer, no product on sale
The sole source is an individual author post on a community platform explaining stable, first-party framework interfaces. It promotes no vendor, tool, course, paid product or funding event, and the described hooks belong to the open-source framework rather than to the author. The residual incentive is ordinary audience-building through developer education.
Moderate on mechanics, weak on corroboration
Confidence is moderate: the mechanics are specific, self-consistent and expressed as code, and the claims concern long-stable framework behaviour rather than contested new results. It is held down by single-publisher sourcing, no documentation anchor or version, a truncated excerpt, and the absence of any adoption measurement in the cluster.
build
The proxy in your call path decides whether @Transactional does anything at all1 distinct publisher
build
The @Version field that guarded nothing: JPA counters, bulk UPDATEs and quietly missing clicks1 distinct publisher
build
The stopping problem: an LLM rewrite loop that converged on code javac rejected1 distinct publisher
build
One non-ASCII letter broke four toolchains: path checks belong in CI, not folklore1 distinct publisher
Distinct publishers with included, body-backed reporting in this cluster.
dev.to
1 article · August 16, 2026