I have a class marked @Startup
and @Singleton
and the constructor is being called twice.
Why is it being called twice?
Here is the class:
import java.util.concurrent.atomic.AtomicInteger;
import javax.ejb.Singleton;
import javax.ejb.Startup;
@Startup
@Singleton
public class CacheStartupListener {
static AtomicInteger count= new AtomicInteger(0);
public CacheStartupListener() {
System.err.println("Singleton invoked " + count.incrementAndGet() + " " + getClass().getClassLoader().toString());
}
}
I can see from the output the the constructor is being called from the same classloader both times.
The stacktrace from within the constructor both lead through wlfullclient-12.1.1jar
, but the stacktraces are otherwise different.
Here is the stack trace from the first instantiation:
Daemon Thread [[ACTIVE] ExecuteThread: '0' for queue: 'weblogic.kernel.Default (self-tuning)'] (Suspended (breakpoint at line 30 in CacheStartupListener)) (out of synch)
CacheStartupListener_m3hhum_NoIntfViewImpl(CacheStartupListener).<init>() line: 30 (out of synch)
CacheStartupListener_m3hhum_NoIntfViewImpl.<init>(SingletonLocalObject) line: not available
NativeConstructorAccessorImpl.newInstance0(Constructor, Object[]) line: not available [native method]
NativeConstructorAccessorImpl.newInstance(Object[]) line: 57
DelegatingConstructorAccessorImpl.newInstance(Object[]) line: 45
Constructor<T>.newInstance(Object...) line: 525
SingletonEJBLocalHomeImpl.allocateBI(Class<T>) line: 45
SingletonEJBLocalHomeImpl.prepare() line: 30
SingletonSessionBeanInfoImpl(SessionBeanInfoImpl).prepare() line: 458
SingletonSessionBeanInfoImpl.prepare() line: 115
EJBDeployer.setupBeanInfos() line: 767
EJBDeployer.prepare(VirtualJarFile, EjbDescriptorBean) line: 920
EJBModule.prepare() line: 419
ScopedModuleDriver.prepare() line: 188
ExtensibleModuleWrapper.prepare() line: 83
ModuleListenerInvoker.prepare() line: 100
ModuleStateDriver$1.next(Module) line: 172
ModuleStateDriver$1.next(Object) line: 167
StateMachineDriver<StateMachine>.nextState(StateChange<StateMachine>, StateMachine[]) line: 35
ModuleStateDriver.prepare(Module[]) line: 38
DeploymentCallbackFlow.prepare(Module[]) line: 139
DeploymentCallbackFlow.prepare() line: 55
BaseDeployment$1.next(Object) line: 706
StateMachineDriver<StateMachine>.nextState(StateChange<StateMachine>, StateMachine[]) line: 35
EarDeployment(BaseDeployment).prepare(DeploymentContext) line: 237
EarDeployment.prepare(DeploymentContext) line: 61
DeploymentStateChecker.prepare(DeploymentContext) line: 158
AppContainerInvoker.prepare(DeploymentContext) line: 60
RedeployOperation.createAndPrepareContainer() line: 104
RedeployOperation.doPrepare() line: 138
RedeployOperation(AbstractOperation).prepare() line: 229
DeploymentManager.handleDeploymentPrepare(Deployment, DeploymentManager$DeploymentRequestInfo) line: 747
DeploymentManager.prepareDeploymentList(ArrayList, DeploymentContext) line: 1216
DeploymentManager.handlePrepare(DeploymentContext) line: 250
DeploymentServiceDispatcher.prepare(DeploymentContext) line: 159
DeploymentReceiverCallbackDeliverer.doPrepareCallback(DeploymentContext) line: 171
DeploymentReceiverCallbackDeliverer.access$000(DeploymentReceiverCallbackDeliverer, DeploymentContext) line: 13
DeploymentReceiverCallbackDeliverer$1.run() line: 46
SelfTuningWorkManagerImpl$WorkAdapterImpl.run() line: 545
ExecuteThread.execute(Runnable) line: 256
ExecuteThread.run() line: 221
And here is the stack trace for the second instantiation:
Daemon Thread [[ACTIVE] ExecuteThread: '0' for queue: 'weblogic.kernel.Default (self-tuning)'] (Suspended (breakpoint at line 30 in CacheStartupListener)) (out of synch)
CacheStartupListener_m3hhum_Impl(CacheStartupListener).<init>() line: 30 (out of synch)
CacheStartupListener_m3hhum_Impl.<init>() line: not available
NativeConstructorAccessorImpl.newInstance0(Constructor, Object[]) line: not available [native method]
NativeConstructorAccessorImpl.newInstance(Object[]) line: 57
DelegatingConstructorAccessorImpl.newInstance(Object[]) line: 45
Constructor<T>.newInstance(Object...) line: 525
WeldConstructorImpl<T>.newInstance(Object...) line: 204
ConstructorInjectionPoint<T>.newInstance(BeanManagerImpl, CreationalContext<?>) line: 117
ExtendedSessionBean(SessionBean<T>).createInstance(CreationalContext<T>) line: 212
ExtendedSessionBean.derivedCreateInstance(CreationalContext) line: 35
WeldEjbBeanManager$ExtendedInjectionTarget.produce(CreationalContext) line: 131
WeldEjbBeanManager.newBeanInstance(String) line: 78
InjectionBasedEjbComponentCreator.getBean(String, Class, boolean) line: 75
SingletonSessionManager(BaseEJBManager).createNewBeanInstance() line: 209
SingletonSessionManager.constructAndInitBean() line: 353
SingletonSessionManager.access$300(SingletonSessionManager) line: 63
SingletonSessionManager$SingletonLifecycleManager.doActualInit() line: 798
SingletonSessionManager$SingletonLifecycleManager.initInternal(boolean) line: 744
SingletonSessionManager$SingletonLifecycleManager.init() line: 631
SingletonSessionManager.init() line: 280
SingletonSessionManager.perhapsInit() line: 276
EJBDeployer.initializeBeans() line: 1287
EJBDeployer.start() line: 1174
EJBModule.start() line: 590
ModuleStateDriver$3.next(Module) line: 213
ModuleStateDriver$3.next(Object) line: 208
StateMachineDriver<StateMachine>.nextState(StateChange<StateMachine>, StateMachine[]) line: 35
ModuleStateDriver.start(Module[]) line: 70
ScopedModuleDriver.start() line: 212
ExtensibleModuleWrapper.start() line: 111
ModuleListenerInvoker.start() line: 124
ModuleStateDriver$3.next(Module) line: 213
ModuleStateDriver$3.next(Object) line: 208
StateMachineDriver<StateMachine>.nextState(StateChange<StateMachine>, StateMachine[]) line: 35
ModuleStateDriver.start(Module[]) line: 70
StartModulesFlow.activate() line: 24
BaseDeployment$2.next(Object) line: 729
StateMachineDriver<StateMachine>.nextState(StateChange<StateMachine>, StateMachine[]) line: 35
EarDeployment(BaseDeployment).activate(DeploymentContext) line: 258
EarDeployment.activate(DeploymentContext) line: 61
DeploymentStateChecker.activate(DeploymentContext) line: 165
AppContainerInvoker.activate(DeploymentContext) line: 79
RedeployOperation(AbstractOperation).activate(Deployment) line: 582
RedeployOperation(ActivateOperation).activateDeployment() line: 148
RedeployOperation(ActivateOperation).doCommit() line: 114
RedeployOperation(AbstractOperation).commit() line: 335
DeploymentManager.handleDeploymentCommit(Deployment, AbstractOperation) line: 844
DeploymentManager.activateDeploymentList(ArrayList, DeploymentManager$DeploymentRequestInfo) line: 1253
DeploymentManager.handleCommit(DeploymentContext) line: 440
DeploymentServiceDispatcher.commit(DeploymentContext) line: 163
DeploymentReceiverCallbackDeliverer.doCommitCallback(DeploymentContext) line: 195
DeploymentReceiverCallbackDeliverer.access$100(DeploymentReceiverCallbackDeliverer, DeploymentContext) line: 13
DeploymentReceiverCallbackDeliverer$2.run() line: 68
SelfTuningWorkManagerImpl$WorkAdapterImpl.run() line: 545
ExecuteThread.execute(Runnable) line: 256
ExecuteThread.run() line: 221
The first stack is not actually creating a bean instance but a proxy instance. Your @Singleton
does not declare any business interfaces, so by default it uses the no-interface view (which can be explicitly configured using @LocalBean
). When using the no-interface view, the container generates a proxy class that is a subclass of your class (in this case, the proxy is named CacheStartupListener_m3hhum_NoIntfViewImpl
). When the container creates an instance of this proxy subclass, the constructor of your EJB class will be invoked. You can try printing getClass() in your constructor to prove this point.
I strongly recommend removing the Java constructor altogether and moving all initialization logic to @PostConstruct
. Java constructors do not mix well with no-interface view.