I want to inject field values to Camunda delegate. Because it's more convenient to have separate inputs for every listener (here - java classes), but not for them all, working within this activity. (it is even more important for such technical and through task like logging – which we want not to be visible, but doing its work)
My delegate is a Spring bean, cause i want to autowire loggingRestService
to it.
As Spring bean is a Singleton by default, for renewing injected fields' values (so, we can say, the delegate become statefull because of these inputs from a BPMN process) i use SCOPE_PROTOTYPE and it works as i wish:
@Service
@Scope(SCOPE_PROTOTYPE)
class BusinessLogDelegate : JavaDelegate {
private val loggingRestService: LoggingRestService
override fun execute(execution: DelegateExecution) {...}
}
But in the Camunda docs there is no info about such solution, only restriction not to use Spring/CDI beans:
For the same reasons as mentioned above, field injection should not be (usually) used with Spring beans, which are singletons by default. Otherwise, you may run into inconsistencies due to concurrent modification of the bean fields.
As written in Is Spring's @Autowired a huge performance issue? the most expensive operation is creating of a bean (not an injection), but without being bean, java class instance will be created every time when it called by Camunda bpm. So there is no addition lack of performance.
Are there any other problems in this solution?
CodePudding user response:
As you correctly observed, a Java class would also be instantiated every time it is required. There is no issue using a Spring bean, which autowires services.
The warning is only relevant when using Camunda's field injection feature to parameterize the delegate. The injected value would survive to the next bean usage if the same delegate expression is used repeatedly. That does not have to be an issue. If the desired value is always injected first, then one could reuse the bean. There is a certain room for unforeseen behavior, if the developer is not aware of Spring scope. As long as you consider the lifecycle, and set the scope to prototype as required, it is ok. Unless you are executing thousands of process instances per second, the performance impact of the instantiation is likely negligible.
In this example I played with different alernavites to inject parameters into delegates. Input data create a data value in the DB. Extension properties requite more code in the delegate. https://github.com/rob2universe/flexible-delegate
CodePudding user response:
To achieve this, either use the @Autowire annotation or implement the ApplicationContextAware interface:
public class SingletonAppContextBean implements ApplicationContextAware {
private ApplicationContext applicationContext;
public PrototypeBean getPrototypeBean() {
return applicationContext.getBean(PrototypeBean.class);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.applicationContext = applicationContext;
}
}
Every time the getPrototypeBean() method is called, a new instance of PrototypeBean will be returned from the ApplicationContext.
However, this approach has serious disadvantages. It contradicts the principle of inversion of control, as we request the dependencies from the container directly.
Also, we fetch the prototype bean from the applicationContext within the SingletonAppcontextBean class. This means coupling the code to the Spring Framework.