Using Spring beans and expressions in Flowable models

Flowable service tasks and listeners can call Spring beans through expressions, which decides how much of your application the process model can reach. This page covers the binding options, safe exposure, Flowable 8 changes and what to confirm before you build.

On this page

How Flowable expressions call Spring beans and when teams need them

In a Flowable model, an expression or delegate expression that names a bean is resolved against the beans in the Spring application context, so a BPMN service task can call your own Spring services without a separate Java delegate class. Use this pattern when the logic already lives in a Spring-managed service, when several process definitions need the same call, or when the team prefers to keep orchestration in the model and business logic in tested services. Use a Java delegate class instead when the step has its own lifecycle, needs per-execution state, or must be shared by teams that do not run Spring. The open-source Flowable engine is licensed under Apache 2.0, and its Spring Boot starters are the usual way to get this bean resolution working in a Spring Boot application. The exact resolution rules, and how a bean is looked up, depend on the Flowable version you run, so confirm the behavior against the documentation for your release before you design around it.

The choice between expression and delegateExpression is the first decision. An expression evaluates a value or invokes a method on a bean each time it runs, which suits reading variables or returning a result. A delegateExpression resolves to an object that implements the delegate interface, which suits a reusable step with a defined contract. Both forms depend on which beans the context exposes, so decide early which services are visible to process models and which are not. Expose a narrow set of named beans built for process use, rather than handing models a general-purpose service locator. Review the bean names as part of the process design, because a renamed bean will break every model that calls it, and those failures appear at runtime rather than at deployment for many expression forms.

Which binding to use: expression, delegateExpression or expression functions

This table compares the three ways a BPMN service task, listener or condition can reach Spring code. Read each row as a decision: what the binding executes, where it fits, and what it costs you in testing and per-execution state. The main takeaway is that delegateExpression gives you a delegate lifecycle, while a plain expression and expression functions give you a method call with no delegate instance.

BindingWhat it executes or returnsWhere it fitsMain trade-off
expression calling a bean methodEvaluates a bean method call and returns its result, such as a boolean for a condition or a value for a variableGateway conditions, listener checks, and short calls that set a variable from a bean resultLogic sits in the expression string, so it is harder to unit test and easy to break with a rename
delegateExpression resolving a beanResolves a Spring bean that implements the JavaDelegate or ActivityBehavior interface and calls its execute methodService tasks that run a full unit of work and need a clear entry point for tests and reviewsThe bean is shared across executions, so instance fields can carry state between process instances
Expression functions or shared variablesExposes a helper or function to expressions, returning a value that the model uses in place of inline codeReusable formatting, date or lookup helpers used across many modelsHelpers must be registered and kept stateless, and their availability depends on the engine configuration
Per-execution stateExpression calls read variables passed in on the execution; delegate instances may be reusedAny logic that depends on the current process instance dataKeep per-execution values in variables or method parameters, not in fields of a shared bean
Unit testingExpressions need a running engine or a mocked bean context to evaluate; delegates can be tested as plain Java objectsChoose delegateExpression when the logic is complex and needs direct testsExpression logic is often covered only by process tests, so confirm which layer your test suite targets
Exception and transaction behaviorExceptions thrown from a bean call surface as process errors in the same way for both bindingsError boundary events and retry design for service tasksConfirm how each binding maps exceptions to failed jobs in your Flowable version before relying on retries
Spring bean exposureOnly beans visible to the engine's Spring context can be referenced by nameMulti-module applications where beans are defined in several contextsExpose only the beans a model needs, so a process cannot call arbitrary application services
Java records and lambda supportDepends on the Flowable version; confirm record and lambda handling in the Flowable 8 documentation before using themNewer models where a typed value object or lambda is passed to a bean methodConfirm support for your exact version and pin the pattern in a test before rolling it out

How to expose Spring beans to expressions without widening the attack surface

Expressions and delegate expressions let a BPMN or CMMN model call code in your Spring context, so every bean you expose becomes something a process definition can invoke. The four controls below limit what models can reach, what data reaches those calls, who can change definitions, and where authorization is enforced.

Expose named, purpose-built beans only

Create a small bean for each task a process needs, such as invoiceApprovalService with one method for a single business action, instead of exposing a repository, a general-purpose service or the application context. A model that can call a broad service can reach any of its methods, including ones never meant for a workflow. Review the list of beans referenced by your deployed models and confirm each one is intentional.

Keep untrusted input out of expressions

Process variables often come from API requests, forms or external messages. If an expression builds a method call, property path or condition from those values, a caller can influence what the engine evaluates. Pass variables as arguments to fixed methods and validate them inside the bean. Check every expression for places where a variable name or value changes the expression text itself.

Control who deploys definitions

An expression in a deployed process definition runs with the engine's privileges, so anyone who can deploy a model can add a call to any exposed bean. Restrict deployment to a pipeline or a small operator group, require review of BPMN and CMMN changes that add expressions, and record which definition version is active. Confirm in the Flowable docs for your version how deployment and definition access are controlled.

Enforce authorization inside the bean

The model decides which step runs, but it does not decide whether the current user may perform the action. Candidate groups and assignees guide task routing, not access control. Each exposed bean should check the caller's permission, the state of the business object and any four-eyes rule before it acts. Test the rejection paths, not only the happy path, so a model that skips a check fails in a test.

Review scripting and XML guidance

Script tasks and XML parsing carry their own risks in Flowable, separate from bean calls. Read the Flowable security guidance on scripting and XML risks, then decide whether your models should allow scripts at all. Confirm current recommendations in the docs for your Flowable version before you rely on any setting, since guidance changes between releases.

Setting up Spring beans and expressions in a Flowable Spring Boot project

This sequence wires a Spring bean into a Flowable BPMN service task, deploys the model, and proves with a unit test that the bean runs and the process variables change as intended. Each step ends with a check so that naming and binding errors surface before the process reaches production.

  1. Add the Flowable Spring Boot starter

    The Java developer adds the Flowable Spring Boot starter for the process engine to the build file, matching the Spring Boot baseline of the chosen Flowable release. Confirm the starter artifact name, the group ID and the version against the Flowable Spring Boot documentation for that release, since starter names and compatibility with Spring Boot 3 or 4 differ between major versions. The step is done when the application compiles, starts without errors, and the engine logs its database connection and process engine initialization on startup.

  2. Configure the database and engine properties

    The developer points the application at a supported database and sets the engine properties the project needs, such as schema handling and the deployment location for process resources. Choose the database type from the list in the Flowable configuration documentation and confirm the driver and version with your platform team. The check is that the application starts against a clean schema and that the engine creates its tables without manual intervention.

  3. Register the beans the model will call

    The developer creates Spring components that implement the business logic and gives each one a stable, explicit bean name, for example with the name attribute on the component annotation. Keep the names short, unique and independent of class names so that a refactoring does not silently break the model. Keep each bean stateless or free of per-execution fields, because a single instance serves many process executions. The check is that the application context lists each bean under its chosen name at startup, which you can confirm with a test that retrieves the bean by name from the context.

  4. Reference the bean from the service task

    In the BPMN model, the modeler sets the service task to call the bean through one binding style: delegateExpression pointing to the bean name, or expression invoking a method on it, applied consistently across the project. Pass inputs through process variables rather than through bean fields, and document the variable names the bean reads and writes. The check is that the model opens in the Flowable modeler or an XML validator without errors and that the bean name in the expression matches the name registered in the previous step exactly.

  5. Deploy the process definition and confirm name resolution

    The developer deploys the process definition through the engine's repository service, either automatically from the resources folder or through an explicit deployment call in a test. Confirm that the deployment succeeds and then start a short test instance. If the bean name does not resolve, the failure appears when the service task is reached, not at deployment, so a test must start an instance and reach that task to expose the error. The check is that the instance reaches the service task without an unresolved-name exception and the job or task completes.

  6. Verify the bean and variable changes with a unit test

    The developer writes a JUnit test that deploys the process, starts an instance with known input variables, and asserts that the bean ran and that the expected output variables were set. Use the Flowable test support for your JUnit version, and confirm the setup annotations and extension classes against the Flowable documentation for the release in use. The check is a passing test that fails when the bean name is misspelled or when the bean returns unexpected values, which proves the binding works end to end.

  7. Confirm failure behavior for unknown names

    The developer deliberately renames the bean in a scratch branch or uses a test with a wrong expression, and observes whether the failure occurs at deployment, at instance start, or when the job runs asynchronously. Record which behavior applies to the chosen configuration, since asynchronous service tasks move the error into the job executor and its retry handling. The check is a documented result that the operations team can use to set alerts and to know where to look for failed jobs.

What Flowable 8 changes for bean, record and lambda use

Flowable 8.0.0 changes the platform that your Spring beans run on, so bean wiring and tests need checking before any expression code is reused. According to the Flowable 8.0.0 release notes, the release upgrades to Spring Framework 7 and Spring Boot 4 and has no backward compatibility for Spring Boot 3. Any delegate bean, configuration class or starter setting written for Spring Boot 3 must be reviewed against Spring Boot 4 before the engine starts. Expressions such as ${approvalService.check(execution)} resolve against whatever beans the application context exposes, so a bean that fails to register under the new baseline will surface as a failed expression at runtime, not at compile time. Plan for a context-startup test that resolves every bean name your models reference.

The release notes also make Jackson 3 the default JSON library, with a Jackson 2 compatibility layer. This matters wherever a bean returns or receives structured data through a process variable, because serialization of objects stored in variables and of REST payloads can change. Decide whether your code will use the default Jackson 3 behavior or keep Jackson 2 through the compatibility layer, then test variables that hold custom objects across a wait state. Removal of JUnit 3 and 4 support is the third change: any test that loads a Flowable engine or bean context through those runners will stop running, so migrate those tests to JUnit 5 before upgrading. Your sibling testing guidance covers the JUnit 5 pattern in detail.

Record and lambda support is part of this topic, but the supplied sources do not confirm how records or lambdas bind inside Flowable expressions. Do not depend on either in a model until you have checked the 8.0.0 release notes and the Flowable documentation for the exact behavior, including how record components are read, whether lambdas can be passed as arguments, and which expression contexts support them. Confirm this with a small deployed test process on the version you will run, and record the result in your design notes, because a pattern that works in one expression field may not work in another.

Which Flowable line and Spring baseline to plan for

Flowable 7 moved to Java 17, Spring Boot 3 and Jakarta EE, according to the Flowable 7.0.0 release notes. If your application already runs on that baseline, your expressions resolve Spring beans through the same Spring Boot 3 context your application uses, so the bean names and scopes you rely on today stay the same.

  • Bean wiring: Expressions such as ${myService.run(execution)} resolve against the Spring application context. Keep bean names stable, and avoid depending on prototype-scoped beans in expressions unless you have confirmed how the engine resolves them in your version.
  • Test framework: Your JUnit tests can continue on the framework your build already uses. Confirm that your test dependencies match the Spring Boot 3 line so the engine context loads the same beans in tests as in production.
  • Spring Boot starters: Confirm which Flowable starter and version your build uses, since the starters differ by engine and by REST support.
  • Expression functions and records: Confirm in the Flowable 7 documentation that the expression features you plan to use exist in your exact release before relying on them.
  • First check: Run a small deployed process that calls one Spring bean through an expression, and confirm the bean resolves in both the application and the test context.

Confirm the exact 7.x release you run against the Flowable 7.0.0 release notes before you pin versions.

What to confirm in your environment before relying on expressions

Expression and bean behavior depends on the Flowable release, the Spring Boot line, the database and the JSON library you choose. Each card below is a question to answer with the vendor documentation or a short proof of concept before your team builds expression-heavy models.

Record and lambda argument binding

Confirm whether Java record arguments and lambda expressions bind as expected in the Flowable release you plan to run, including how they are passed from process variables. Test with a record whose components map to process variables and with a lambda supplied through a Spring bean method. Check the release notes for the version you target, since expression language support has changed between major lines, and do not rely on examples written for an earlier release.

Expression evaluation across databases and JSON

Confirm how expression results that depend on stored variables behave on the database and JSON library your organization has approved. Serialized variables, date values and numeric precision can differ between database types, and Flowable 8 moves the default JSON library to Jackson 3 with a Jackson 2 compatibility layer. Run the same expressions against a production-like database in a proof of concept and record any difference in output.

Bean name resolution across contexts

Confirm which bean a name such as orderService resolves to when more than one application context exists, for example a parent context, a test context and a separate REST application. Duplicate bean names or qualifiers can route a service task to the wrong implementation without any error at deployment time. Ask the team to document the bean naming convention and to check resolution with a startup test that fails when a referenced name is missing or ambiguous.

Spring Boot and Flowable line compatibility

Confirm that the Spring Framework and Spring Boot versions in your stack are supported by the Flowable line you select. Flowable 8 requires Spring Framework 7 and Spring Boot 4, and it does not support Spring Boot 3, so a platform still on Spring Boot 3 needs an upgrade plan before adoption. Check the Spring Boot starter documentation for the exact versions and confirm the upgrade order with your platform team.

Testing expressions inside the engine

Confirm how your team will exercise expressions through a running engine rather than only as isolated Java methods. An expression that passes a unit test can still fail when the engine resolves variables, applies type conversion or calls a bean inside a transaction. Plan integration tests that deploy the actual BPMN or CMMN resource, start an instance, and assert on variables and outcomes, and decide who owns those tests in your delivery pipeline.

Who owns expression changes

Confirm who reviews and approves an expression or bean change that alters how a process routes work or sets a variable. Because expressions live inside process definitions, a small edit can change behavior without a code review in the usual place. Agree on a review path that covers both the BPMN resource and the Java bean, and on how a changed definition is deployed alongside the code it calls.

Questions teams ask about Spring beans in Flowable models

Should a service task use delegateExpression or expression for a Spring bean?

Use delegateExpression when the service task should run a bean that implements the Flowable delegate contract, and use expression when you only need to call a method on a bean and optionally store its return value. A delegateExpression resolves to a new delegate instance per execution, which keeps injected state out of shared fields, while an expression is evaluated for a single method call. Most teams standardize on one pattern for service tasks so that reviewers can read a model quickly. The choice affects how much logic sits in the model versus in Java, so decide it with your engineers before the first process is built.

How much effort does a clean Spring bean layer for Flowable take?

Effort depends mainly on the number of services the processes call, how many distinct business operations each one exposes, and how much test coverage you expect around them. A small set of well-named facade beans with explicit inputs and outputs is faster to build and review than many thin wrappers over repositories. Plan for one-time design work on naming, variable contracts and error handling, plus recurring work whenever a process adds a new call. Ask any partner for a fixed-scope quote that lists the services in scope, the tests each one needs and the review steps, because those items set the price and timeline more than the expression syntax does.

What does the internal team need to provide for this work?

Your team needs to name the owners of each Spring bean, confirm which data the processes may read and write, and agree on the process variable names that cross the model boundary. Engineers familiar with Spring Boot and the Flowable engine can review expressions and delegates in the same pull requests as the process models. Security and platform staff should confirm which beans are visible to expressions, since expression evaluation runs with the privileges of the process engine. If these owners are not named early, the work tends to stall at review rather than at build.

Do we need an outside partner to build this, or can we do it ourselves?

A capable Java team can build this in-house, and many do, provided it has time to learn the Flowable expression rules and test process behavior. A partner adds value when the team lacks Spring Boot or Flowable experience, when several process applications must share one bean design, or when the upgrade and test plan needs independent review. Ask whether the partner will hand over runbooks, test suites and a documented bean catalog, because those artifacts remain useful after the engagement. DeployOpen works in the client's own infrastructure and delivers fixed-scope work agreed in writing.

How do upgrades to Flowable 8 affect existing expressions and beans?

Flowable 8.0.0 was released on February 27, 2026, and its release notes describe an upgrade to Spring Framework 7 and Spring Boot 4, with no backward compatibility for Spring Boot 3. Expressions that depend on Spring bean wiring, Jackson serialization or JUnit 4 test setup need review, and the release notes also describe Jackson 3 as the default JSON library with a Jackson 2 compatibility layer. Teams moving from Flowable 7.x, which the 7.0.0 notes describe as built on Java 17, Spring Boot 3 and Jakarta EE, should inventory every expression and delegate before changing versions. Confirm the exact impact against the 8.0.0 release notes and your own test results.

What drives the cost and duration of a Flowable bean and expression review?

The main drivers are the number of process models that call Spring beans, the number of expressions per model, the presence of automated tests, and the Spring Boot baseline you are on. A codebase with no tests and mixed Spring Boot versions takes longer to verify than one with a documented bean catalog and JUnit 5 coverage. The migration source also matters if processes came from another engine. Request a quote that breaks the work into inventory, refactoring, test creation and upgrade verification, and that states which of these are one-time and which recur with each release.

Is it safe to expose Spring beans to Flowable expressions?

It is safe when you expose a narrow set of beans with explicit methods, validate inputs, and avoid giving expressions access to broad infrastructure services. Flowable allows scripting and expression evaluation, so model authors must be treated as a trusted group, and changes to models should go through the same review as code. Document which beans are reachable, restrict who can deploy process definitions, and test failure paths so that an error does not expose internal details. Check the Flowable security guidance and your own threat model before you publish a bean to the expression context.

How DeployOpen scopes and delivers Flowable bean and expression work

DeployOpen plans and builds the Spring bean and expression layer of Flowable models inside your own Java stack, so each service task has a defined contract, a tested bean and a clear owner. Engagements start from a written scope that names the processes, beans and environments involved.

How we scope

  • Inventory every service task, listener and expression in the models, noting whether each uses delegateExpression or expression.
  • Define which Spring beans may be referenced, their owners and the variables each one reads and writes.
  • Agree in writing which Flowable version, Spring Boot baseline and Java version the work targets before build starts.

How we build

  • Expose only narrowly named, stateless Spring beans to models, keeping execution state out of injected fields.
  • Write JUnit tests that exercise the beans and the deployed process definitions together in your build pipeline.
  • Review expression functions and any Java record or lambda usage against the Flowable version you run.

How we hand over

  • Deliver a register of beans, expressions and variable contracts that your engineers can maintain after handover.
  • Document the test suite, how to run it locally and in CI, and the review checklist for new service tasks.
  • Walk your platform and process teams through the code, the models and the open decisions before sign-off.

How we support

  • Offer ongoing support, upgrades and continuous testing as a retainer after the fixed-scope engagement ends.
  • Plan a review of bean and expression changes whenever you move to a new Flowable or Spring Boot release.
  • Confirm with your team the support boundaries, response expectations and who approves changes to shared beans.

Sources and further reading

Talk to our team.

Tell us what you're working on, whether it's a deployment, an audit, a security test or a cyber range. You'll speak with an engineer who can help you scope it.

  • 30-minute call: free, with no obligation.
  • NDA on request: we can sign before you share details.
  • Clear next steps: a scope and plan after the call.