Add methods to BeanConfigurator for applying decorators
- Dominant language
- Java
- Stars
- 240
- Forks
- 83
- PR merge metrics
- No merged PRs in 30d
Description
Currently when adding a `Bean` using `jakarta.enterprise.inject.spi.AfterBeanDiscovery.addBean()` or `jakarta.enterprise.inject.spi.AfterBeanDiscovery.addBean(Bean)` any decorators declared or programatically added for the types of the `Bean` are not automatically applied.
Not only is this perhaps surprising, there actually is no portable way to achieve this behaviour. Someone adding a bean must resort to SPIs and implementation specific code. For instance Soteria uses the following code:
```java
afterBeanDiscovery.addBean(
decorator.decorateBean(authenticationMechanismBean, HttpAuthenticationMechanism.class, beanManager));
```
[Source](https://github.com/eclipse-ee4j/soteria/blob/master/impl/src/main/java/org/glassfish/soteria/cdi/CdiExtension.java#L246)
Where `decorator.decorateBean` is an SPI, that for Weld uses code such as the following:
```java
public class DecorableWeldBeanWrapper extends RIBean implements Bean, PassivationCapable {
private final Bean bean;
private final CurrentInjectionPoint currentInjectionPoint;
private final boolean isProxyable;
private Class type;
private List> decorators;
private Class proxyClass;
private boolean proxyRequired;
private boolean isPassivationCapableBean;
private boolean isPassivationCapableDependency;
public DecorableWeldBeanWrapper(Bean bean, Class type, BeanManagerImpl beanManager) {
super(
bean,
new StringBeanIdentifier(BeanIdentifiers.forBuiltInBean(beanManager, type, null)),
beanManager);
this.bean = bean;
this.type = type;
this.currentInjectionPoint = beanManager.getServices().get(CurrentInjectionPoint.class);
this.isProxyable = Proxies.isTypesProxyable(getTypes(), beanManager.getServices());
}
@Override
public void initializeAfterBeanDiscovery() {
decorators = beanManager.resolveDecorators(getTypes(), getQualifiers());
if (!decorators.isEmpty()) {
proxyClass = new ProxyFactory(getBeanManager().getContextId(), getType(), getTypes(), this).getProxyClass();
}
}
@Override
protected void internalInitialize(BeanDeployerEnvironment environment) {
proxyRequired = getScope() != null && isNormalScoped();
isPassivationCapableBean = Serializable.class.isAssignableFrom(type);
isPassivationCapableDependency = isNormalScoped() || (isDependent() && isPassivationCapableBean());
}
@Override
public T create(CreationalContext creationalContext) {
T instance = bean.create(creationalContext);
if (decorators.isEmpty()) {
return instance;
}
return getOuterDelegate(this, instance, creationalContext, proxyClass, currentInjectionPoint.peek(), getBeanManager(), decorators);
}
```
[Source](https://github.com/eclipse-ee4j/soteria/blob/master/spi/bean-decorator/weld/src/main/java/org/glassfish/soteria/spi/bean/decorator/weld/DecorableWeldBeanWrapper.java#L40)
This code is difficult to come up with, difficult to maintain and error prone. The user must also make sure the right companion SPI implementation is available on the class path.
To simplify this, I'd like to propose a method for `BeanConfigurator` to signal to the runtime that enabled decorators should be applied to the instance returned from the `Bean.create` (`Contextual.create') method, just like happens for beans which implement the bean types directly and are discovered by CDI.
For instance:
```java
public void afterBean(final @Observes AfterBeanDiscovery afterBeanDiscovery) {
afterBeanDiscovery
.addBean()
.scope(ApplicationScoped.class)
.types(MyBean.class)
.id("Created by " + CdiExtension.class)
.enabledDecorators()
.createWith(e -> new MyBeanImpl("Hi!"));
}
```
Several variants could be considered, like e.g.:
* addEnabledDecorators()
* decorators(Decorator... decorators)
* addDecorators(Decorator... decorators)
Contributor guide
Assessment
This issue has not been assessed yet.