Docs

Documentation versions (currently viewingVaadin 24)

Custom Instantiator

Creating a custom Instantiator implementation.

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