or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

bean-container.mdbean-invocation.mdbuild-profiles.mdbuild-properties.mdindex.mdinterceptor-integration.mdlogger-injection.mdruntime-lookup.md

runtime-lookup.mddocs/

0

# Runtime Lookup Conditional Beans

1

2

Control which beans are available for programmatic lookup based on runtime properties, enabling dynamic behavior without bean activation overhead. These annotations work with `Instance<T>` for conditional bean resolution at runtime.

3

4

## Capabilities

5

6

### LookupIfProperty Annotation

7

8

Makes beans available for programmatic lookup when runtime properties match specified values.

9

10

```java { .api }

11

/**

12

* Indicates that a bean should only be obtained by programmatic lookup if the property matches the provided value.

13

* This annotation is repeatable. A bean will be included if all the conditions defined by the LookupIfProperty and

14

* LookupUnlessProperty annotations are satisfied.

15

*/

16

@Repeatable(LookupIfProperty.List.class)

17

@Retention(RetentionPolicy.RUNTIME)

18

@Target({ ElementType.METHOD, ElementType.TYPE, ElementType.FIELD })

19

public @interface LookupIfProperty {

20

/**

21

* Name of the runtime property to check

22

*/

23

String name();

24

25

/**

26

* Expected String value of the runtime property (specified by name) if the bean should be looked up at runtime.

27

*/

28

String stringValue();

29

30

/**

31

* Determines if the bean is to be looked up when the property name specified by name has not been specified at all

32

*/

33

boolean lookupIfMissing() default false;

34

35

@Retention(RetentionPolicy.RUNTIME)

36

@Target({ ElementType.METHOD, ElementType.TYPE, ElementType.FIELD })

37

@interface List {

38

LookupIfProperty[] value();

39

}

40

}

41

```

42

43

**Usage Examples:**

44

45

```java

46

import jakarta.enterprise.context.ApplicationScoped;

47

import jakarta.enterprise.inject.Instance;

48

import jakarta.inject.Inject;

49

import io.quarkus.arc.lookup.LookupIfProperty;

50

51

interface Service {

52

String name();

53

}

54

55

// Service available for lookup only when property matches

56

@LookupIfProperty(name = "service.foo.enabled", stringValue = "true")

57

@ApplicationScoped

58

class ServiceFoo implements Service {

59

public String name() {

60

return "foo";

61

}

62

}

63

64

// Always available service

65

@ApplicationScoped

66

class ServiceBar implements Service {

67

public String name() {

68

return "bar";

69

}

70

}

71

72

@ApplicationScoped

73

class Client {

74

75

@Inject

76

Instance<Service> services;

77

78

public void printServiceNames() {

79

// This will include ServiceFoo only if service.foo.enabled=true

80

services.stream()

81

.forEach(service -> System.out.println(service.name()));

82

83

// Direct lookup with fallback handling

84

if (!services.isUnsatisfied()) {

85

Service service = services.get();

86

System.out.println("Selected: " + service.name());

87

}

88

}

89

}

90

```

91

92

### LookupUnlessProperty Annotation

93

94

Makes beans available for programmatic lookup when runtime properties do NOT match specified values.

95

96

```java { .api }

97

/**

98

* Indicates that a bean should only be obtained by programmatic lookup if the property does not match the provided value.

99

* This annotation is repeatable. A bean will be included if all the conditions defined by the LookupUnlessProperty

100

* and LookupIfProperty annotations are satisfied.

101

*/

102

@Repeatable(LookupUnlessProperty.List.class)

103

@Retention(RetentionPolicy.RUNTIME)

104

@Target({ ElementType.METHOD, ElementType.TYPE, ElementType.FIELD })

105

public @interface LookupUnlessProperty {

106

/**

107

* Name of the runtime property to check

108

*/

109

String name();

110

111

/**

112

* Expected String value of the runtime property (specified by name) if the bean should be skipped at runtime.

113

*/

114

String stringValue();

115

116

/**

117

* Determines if the bean should be looked up when the property name specified by name has not been specified at all

118

*/

119

boolean lookupIfMissing() default false;

120

121

@Retention(RetentionPolicy.RUNTIME)

122

@Target({ ElementType.METHOD, ElementType.TYPE, ElementType.FIELD })

123

@interface List {

124

LookupUnlessProperty[] value();

125

}

126

}

127

```

128

129

**Usage Examples:**

130

131

```java

132

import jakarta.enterprise.context.ApplicationScoped;

133

import jakarta.enterprise.inject.Instance;

134

import jakarta.inject.Inject;

135

import io.quarkus.arc.lookup.LookupUnlessProperty;

136

137

interface ProcessingService {

138

void process(String data);

139

}

140

141

// Available for lookup unless disabled

142

@LookupUnlessProperty(name = "service.premium.disabled", stringValue = "true")

143

@ApplicationScoped

144

class PremiumProcessingService implements ProcessingService {

145

public void process(String data) {

146

System.out.println("Premium processing of: " + data);

147

}

148

}

149

150

// Always available fallback

151

@ApplicationScoped

152

class StandardProcessingService implements ProcessingService {

153

public void process(String data) {

154

System.out.println("Standard processing of: " + data);

155

}

156

}

157

158

@ApplicationScoped

159

class ProcessingManager {

160

161

@Inject

162

Instance<ProcessingService> processors;

163

164

public void handleData(String data) {

165

// Select first available processor

166

ProcessingService processor = processors.iterator().next();

167

processor.process(data);

168

}

169

}

170

```

171

172

### Multiple Conditions

173

174

Combine multiple lookup conditions for sophisticated runtime selection logic.

175

176

```java

177

import jakarta.enterprise.context.ApplicationScoped;

178

import jakarta.enterprise.inject.Instance;

179

import jakarta.inject.Inject;

180

import io.quarkus.arc.lookup.LookupIfProperty;

181

import io.quarkus.arc.lookup.LookupUnlessProperty;

182

183

interface NotificationHandler {

184

void handle(String message);

185

}

186

187

// Available only when both conditions are met

188

@ApplicationScoped

189

@LookupIfProperty(name = "notifications.email.enabled", stringValue = "true")

190

@LookupUnlessProperty(name = "notifications.email.maintenance", stringValue = "true")

191

class EmailNotificationHandler implements NotificationHandler {

192

public void handle(String message) {

193

System.out.println("Email: " + message);

194

}

195

}

196

197

// Available when Slack is enabled

198

@ApplicationScoped

199

@LookupIfProperty(name = "notifications.slack.enabled", stringValue = "true")

200

class SlackNotificationHandler implements NotificationHandler {

201

public void handle(String message) {

202

System.out.println("Slack: " + message);

203

}

204

}

205

206

// Fallback handler - always available

207

@ApplicationScoped

208

class LogNotificationHandler implements NotificationHandler {

209

public void handle(String message) {

210

System.out.println("Log: " + message);

211

}

212

}

213

214

@ApplicationScoped

215

class NotificationService {

216

217

@Inject

218

Instance<NotificationHandler> handlers;

219

220

public void broadcast(String message) {

221

// Send via all available handlers

222

handlers.stream()

223

.forEach(handler -> handler.handle(message));

224

}

225

226

public void sendViaPreferred(String message) {

227

// Send via first available handler (based on lookup conditions)

228

if (!handlers.isUnsatisfied()) {

229

handlers.get().handle(message);

230

}

231

}

232

}

233

```

234

235

### Producer Method Usage

236

237

Lookup conditions work with producer methods for dynamic bean creation.

238

239

```java

240

import jakarta.enterprise.context.ApplicationScoped;

241

import jakarta.enterprise.inject.Produces;

242

import jakarta.enterprise.inject.Instance;

243

import jakarta.inject.Inject;

244

import io.quarkus.arc.lookup.LookupIfProperty;

245

246

@ApplicationScoped

247

public class DatabaseConnectionProducer {

248

249

@Produces

250

@LookupIfProperty(name = "database.pool.enabled", stringValue = "true")

251

public DatabaseConnection pooledConnection() {

252

return new PooledDatabaseConnection();

253

}

254

255

@Produces

256

@LookupUnlessProperty(name = "database.pool.enabled", stringValue = "true")

257

public DatabaseConnection simpleConnection() {

258

return new SimpleDatabaseConnection();

259

}

260

}

261

262

@ApplicationScoped

263

class DatabaseService {

264

265

@Inject

266

Instance<DatabaseConnection> connections;

267

268

public void performQuery(String sql) {

269

// Use the appropriate connection based on runtime configuration

270

DatabaseConnection connection = connections.get();

271

connection.execute(sql);

272

}

273

}

274

275

interface DatabaseConnection {

276

void execute(String sql);

277

}

278

279

class PooledDatabaseConnection implements DatabaseConnection {

280

public void execute(String sql) {

281

System.out.println("Executing via pool: " + sql);

282

}

283

}

284

285

class SimpleDatabaseConnection implements DatabaseConnection {

286

public void execute(String sql) {

287

System.out.println("Executing directly: " + sql);

288

}

289

}

290

```

291

292

### Advanced Selection Patterns

293

294

Implement complex runtime selection logic with multiple conditions.

295

296

```java

297

import jakarta.enterprise.context.ApplicationScoped;

298

import jakarta.enterprise.inject.Instance;

299

import jakarta.inject.Inject;

300

import io.quarkus.arc.lookup.LookupIfProperty;

301

import io.quarkus.arc.lookup.LookupUnlessProperty;

302

303

interface PaymentProcessor {

304

String getName();

305

void processPayment(double amount);

306

}

307

308

// Production Stripe processor

309

@ApplicationScoped

310

@LookupIfProperty(name = "payment.provider", stringValue = "stripe")

311

@LookupUnlessProperty(name = "payment.test-mode", stringValue = "true")

312

class StripeProductionProcessor implements PaymentProcessor {

313

public String getName() { return "Stripe Production"; }

314

public void processPayment(double amount) {

315

System.out.println("Processing $" + amount + " via Stripe Production");

316

}

317

}

318

319

// Test Stripe processor

320

@ApplicationScoped

321

@LookupIfProperty(name = "payment.provider", stringValue = "stripe")

322

@LookupIfProperty(name = "payment.test-mode", stringValue = "true")

323

class StripeTestProcessor implements PaymentProcessor {

324

public String getName() { return "Stripe Test"; }

325

public void processPayment(double amount) {

326

System.out.println("Processing $" + amount + " via Stripe Test");

327

}

328

}

329

330

// PayPal processor

331

@ApplicationScoped

332

@LookupIfProperty(name = "payment.provider", stringValue = "paypal")

333

class PayPalProcessor implements PaymentProcessor {

334

public String getName() { return "PayPal"; }

335

public void processPayment(double amount) {

336

System.out.println("Processing $" + amount + " via PayPal");

337

}

338

}

339

340

// Mock processor for development

341

@ApplicationScoped

342

@LookupIfProperty(name = "payment.provider", stringValue = "mock")

343

class MockPaymentProcessor implements PaymentProcessor {

344

public String getName() { return "Mock"; }

345

public void processPayment(double amount) {

346

System.out.println("MOCK: Processing $" + amount);

347

}

348

}

349

350

@ApplicationScoped

351

class PaymentService {

352

353

@Inject

354

Instance<PaymentProcessor> processors;

355

356

public void listAvailableProcessors() {

357

System.out.println("Available payment processors:");

358

processors.stream()

359

.forEach(processor -> System.out.println("- " + processor.getName()));

360

}

361

362

public void processPayment(double amount) {

363

if (processors.isUnsatisfied()) {

364

throw new IllegalStateException("No payment processor available");

365

}

366

367

// Use the first available processor

368

PaymentProcessor processor = processors.get();

369

processor.processPayment(amount);

370

}

371

372

public PaymentProcessor selectProcessor(String preferredName) {

373

return processors.stream()

374

.filter(processor -> processor.getName().contains(preferredName))

375

.findFirst()

376

.orElse(processors.get());

377

}

378

}

379

```

380

381

## Runtime Property Configuration

382

383

Runtime properties are evaluated when beans are looked up, not at build time:

384

385

### Application Properties

386

387

```properties

388

# These are evaluated at runtime

389

service.foo.enabled=true

390

notifications.email.enabled=true

391

notifications.slack.enabled=false

392

payment.provider=stripe

393

payment.test-mode=false

394

```

395

396

### Dynamic Configuration

397

398

```java

399

// Properties can change at runtime through configuration sources

400

@ApplicationScoped

401

class DynamicConfigExample {

402

403

@ConfigProperty(name = "service.foo.enabled")

404

String serviceFooEnabled;

405

406

public void toggleService() {

407

// Changing this property affects future Instance<T> lookups

408

System.setProperty("service.foo.enabled",

409

"true".equals(serviceFooEnabled) ? "false" : "true");

410

}

411

}

412

```

413

414

## Benefits and Use Cases

415

416

### Performance Benefits

417

- Beans are always created at build time, avoiding runtime conditional creation overhead

418

- Only lookup filtering happens at runtime, which is lightweight

419

- Avoids `@ConditionalOnProperty` runtime overhead

420

421

### Dynamic Behavior

422

- Enable/disable features at runtime without application restart

423

- A/B testing and feature toggles

424

- Environment-specific behavior in same deployment

425

- Integration enabling/disabling based on runtime conditions