Custom Instantiator
- Creating a Custom Instantiator
- Loading the custom Instantiator using Java ServiceLoader
- Loading the custom Instantiator though VaadinServletService
An Instantiator
is used for discovering, creating and managing object instances that Flow uses. Usually, a custom instantiator is needed for dependency injection frameworks so that they can provide managed instances of objects according to the specific conventions of that DI framework.
Creating a Custom Instantiator
To create a custom instantiator, implement the Instantiator
interface and implement the following methods:
-
Stream<VaadinServiceInitListener> getServiceInitListeners()
; -
T getOrCreate(Class<T> type)
; and -
T createComponent(Class<T> componentClass)
It’s usually good to extend the DefaultInstantiator
and only implement getOrCreate
and createComponent
with dependency injection bean handling, with a fallback to the default functionality.
getOrCreate(Class<T>)
should always return an initialized object of type T, where the instance returned is an initialized one from the current scope, or generate a new bean instance, or if not a bean handle field injection. createComponent(Class<T>)
should always create a new component with field injection.
As an example, the simplified instantiator for Context and Dependency Injection (CDI) could be as follows:
import com.vaadin.flow.component.Component;
import com.vaadin.flow.di.DefaultInstantiator;
import jakarta.enterprise.inject.spi.BeanManager;
import jakarta.enterprise.inject.spi.Unmanaged;
import jakarta.inject.Inject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class CustomInstantiator extends DefaultInstantiator {
@Inject
private BeanManager beanManager;
public CustomInstantiator(BeanManager beanManager, VaadinService service) {
super(service);
this.beanManager = beanManager;
}
/**
* Hands over an existing bean or tries to instantiate one.
*/
@Override
public<T> T getOrCreate(Class<T> type) {
Set<Bean<T>> beans = beanManager.getBeans(type);
if(beans == null || beans.isEmpty()) {
getLogger().debug("'{}' is not a CDI bean.",
type.getName());
return createComponent(type);
}
Bean<?> bean = beanManager.resolve(beans);
final CreationalContext<?> ctx = beanManager.createCreationalContext(bean);
//noinspection unchecked
return (T) beanManager.getReference(bean, type, ctx);
}
/**
* Tries to instantiate new bean.
*/
@Override
public<T extends Component> T createComponent(Class<T> componentClass) {
Unmanaged<T> unmanagedClass = new Unmanaged<T>(componentClass);
Unmanaged.UnmanagedInstance<T> instance = unmanagedClass.newInstance();
instance.produce().inject().postConstruct();
return instance.get();
}
private static Logger getLogger() {
return LoggerFactory.getLogger(CdiInstantiator.class);
}
}
Loading the custom Instantiator using Java ServiceLoader
To load the instantiator, you should implement the InstantiatorFactory
interface and load it through the Java ServiceLoader.
package org.example;
import com.vaadin.flow.di.Instantiator;
import com.vaadin.flow.di.InstantiatorFactory;
import com.vaadin.flow.server.VaadinService;
import jakarta.enterprise.inject.spi.BeanManager;
import org.apache.deltaspike.core.api.provider.BeanManagerProvider;
public class CustomInstantiatorFactory implements InstantiatorFactory {
@Override
public Instantiator createInstantiator(VaadinService vaadinService) {
BeanManager beanManager = BeanManagerProvider.getInstance().getBeanManager();
return new CustomInstantiator(beanManager, service);
}
}
To load the factory, add to src/main/resources/META-INF/services/
the file com.vaadin.flow.di.InstantiatorFactory
with the content being the fully qualified name of the factory class. For this example, the content would be org.example.CustomInstantiatorFactory
.
Loading the custom Instantiator though VaadinServletService
Implement VaadinServletService
and VaadinServlet
.
For VaadinServletService
, override the createInstantiator()
.
import com.vaadin.flow.di.Instantiator;
import com.vaadin.flow.server.VaadinServletService;
import jakarta.enterprise.inject.spi.BeanManager;
public class CustomVaadinServletService extends VaadinServletService {
private BeanManager beanManager;
public CustomVaadinServletService(CustomServlet servlet,
DeploymentConfiguration configuration,
BeanManager beanManager) {
super(servlet, configuration);
this.beanManager = beanManager;
}
protected Instantiator createInstantiator() throws ServiceException {
Set<Bean<CdiInstantiator>> beans = beanManager.getBeans(CustomInstantiator.class);
if(beans == null || beans.isEmpty()) {
getLogger().debug("'{}' is not a CDI bean.", type.getName());
Unmanaged<T> unmanagedClass = new Unmanaged<T>(CustomInstantiator.class);
Unmanaged.UnmanagedInstance<T> instance = unmanagedClass.newInstance();
instance.produce().inject().postConstruct();
return instance.get();
}
Bean<?> bean = beanManager.resolve(beans);
final CreationalContext<?> ctx = beanManager.createCreationalContext(bean);
//noinspection unchecked
return (CustomInstantiator) beanManager.getReference(bean, CustomInstantiator.class, ctx);
}
}
For VaadinServlet
, override the createServerService(DeploymentConfiguration deploymentConfiguration)
to return the custom VaadinServletService.
import com.vaadin.flow.function.DeploymentConfiguration;
import com.vaadin.flow.server.VaadinServlet;
import com.vaadin.flow.server.VaadinServletService;
import jakarta.enterprise.inject.spi.BeanManager;
import org.apache.deltaspike.core.api.provider.BeanManagerProvider;
@WebServlet(urlPatterns = "/*", asyncSupported = true)
public class CustomServlet extends VaadinServlet {
@Override
protected VaadinServletService createServletService(DeploymentConfiguration deploymentConfiguration) throws ServiceException {
BeanManager beanManager = BeanManagerProvider.getInstance().getBeanManager();
CustomVaadinServletService service = new CustomVaadinServletService(this, deploymentConfiguration, beanManager);
service.init();
return service;
}
}
e1b2822e-926b-4801-9cb0-2c45a9af64e7