### Install Valhalla JDK via SDK Man (macOS) Source: https://github.com/avaje/avaje-inject/blob/master/how-to-valhalla.md Installs the Valhalla JDK using SDK Man, providing the full path to the JDK home directory. ```bash # the full path to the jdk is: echo $(realpath valhalla-23.jdk/Contents/Home) # install it to sdkman sdk install java 23.ea.valhalla $(realpath valhalla-23.jdk/Contents/Home) # check it ... sdk use java 23.ea.valhalla java -version ``` -------------------------------- ### Example Module Configuration in Java Source: https://github.com/avaje/avaje-inject/blob/master/inject/README.md Configure an Avaje module for dependency injection within a Java module. This setup is required to integrate Avaje Inject into your project's module system. ```java import io.avaje.inject.spi.AvajeModule; module org.example { requires io.avaje.inject; provides io.avaje.inject.spi.AvajeModule with org.example.ExampleModule; } ``` -------------------------------- ### Minimal Avaje Inject Example Source: https://github.com/avaje/avaje-inject/blob/master/docs/LIBRARY.md A basic example demonstrating bean definition and injection with Avaje Inject. ```java @Singleton public class UserService { public String getUser(int id) { return "User: " + id; } } @Singleton public class UserController { private final UserService userService; @Inject public UserController(UserService userService) { this.userService = userService; } public String showUser(int id) { return userService.getUser(id); } } public class Main { public static void main(String[] args) { BeanScope scope = BeanScope.builder().build(); UserController controller = scope.get(UserController.class); System.out.println(controller.showUser(1)); } } ``` -------------------------------- ### Create JDK Directory Source: https://github.com/avaje/avaje-inject/blob/master/how-to-valhalla.md Creates a directory for Valhalla JDK installations and moves the downloaded archive into it. ```bash mkdir -p ~/localjdk mv ~/Downloads/openjdk-* ~/localjdk ``` -------------------------------- ### Constructor Injection Example Source: https://github.com/avaje/avaje-inject/blob/master/docs/guides/dependency-injection.md Inject dependencies via the constructor. This is the recommended approach for dependency injection. ```java import io.avaje.inject.Singleton; @Singleton public class OrderService { private final UserService userService; private final PaymentService paymentService; public OrderService(UserService userService, PaymentService paymentService) { this.userService = userService; this.paymentService = paymentService; } } ``` -------------------------------- ### Example Factory Class Source: https://github.com/avaje/avaje-inject/blob/master/README.md An example of a factory class annotated with @Factory that provides beans. ```java @Factory public class ExampleFactory { @Bean public DependencyClass2 bean() { return new DependencyClass2(); } } ``` -------------------------------- ### Use Mockito Annotations for Test Setup Source: https://github.com/avaje/avaje-inject/blob/master/docs/guides/testing.md Employ `@Mock` and `@Spy` annotations for cleaner test setup. Mocks and spies are automatically wired into the test DI container. ```java @InjectTest class ServiceWithMockitoTest { @Mock UserRepository userRepository; @Spy Logger logger; @Inject UserService userService; @Test void testWithMockito() { when(userRepository.findById(1)).thenReturn(new User(1, "Alex")); User user = userService.findById(1); assertEquals("Alex", user.name); verify(userRepository).findById(1); } } ``` -------------------------------- ### Generated AvajeModule Class Source: https://github.com/avaje/avaje-inject/blob/master/README.md An example of a generated AvajeModule class that defines the dependency wiring order. ```java @Generated("io.avaje.inject.generator") @InjectModule public final class ExampleModule implements AvajeModule { /** * Creates all the beans in order based on constructor dependencies. The beans are registered * into the builder along with callbacks for field/method injection, and lifecycle * support. */ @Override public void build(Builder builder) { this.builder = builder; // create beans in order based on constructor dependencies // i.e. "provides" followed by "dependsOn" build_example_ExampleFactory(builder); build_example_DependencyClass(builder); build_example_DependencyClass2(builder); build_example_Example(builder); } @DependencyMeta(type = "org.example.ExampleFactory") private void build_example_ExampleFactory(Builder builder) { ExampleFactory$DI.build(builder); } @DependencyMeta(type = "org.example.DependencyClass") private void build_example_DependencyClass(Builder builder) { DependencyClass$DI.build(builder); } @DependencyMeta( type = "org.example.DependencyClass2", method = "org.example.ExampleFactory$DI.build_bean", // factory method dependsOn = {"org.example.ExampleFactory"}) //factory beans naturally depend on the factory private void build_example_DependencyClass2(Builder builder) { ExampleFactory$DI.build_bean(builder); } @DependencyMeta( type = "org.example.Example", dependsOn = {"org.example.DependencyClass", "org.example.DependencyClass2"}) private void build_example_Example(Builder builder) { Example$DI.build(builder); } } ``` -------------------------------- ### Verify Valhalla JDK Version Source: https://github.com/avaje/avaje-inject/blob/master/how-to-valhalla.md Displays the Java version to confirm the Valhalla JDK is correctly installed and active. ```bash java -version ``` -------------------------------- ### Basic Component Test: Avaje Inject vs Spring Source: https://github.com/avaje/avaje-inject/blob/master/docs/guides/testing-avaje-inject-vs-spring.md Compares the basic setup for component testing in Avaje Inject and Spring Boot. Avaje Inject uses `@InjectTest` and `@Inject`, while Spring uses `@SpringBootTest` and `@Autowired`. ```java @InjectTest class MyServiceTest { @Inject MyService myService; @Test void testLogic() { /* ... */ } } ``` ```java @SpringBootTest class MyServiceTest { @Autowired MyService myService; @Test void testLogic() { /* ... */ } } ``` -------------------------------- ### Integration Testing with @InjectTest Source: https://github.com/avaje/avaje-inject/blob/master/docs/LIBRARY.md An example of integration testing using Avaje Inject's @InjectTest annotation. ```java @InjectTest public class UserServiceIntegrationTest { @Inject private UserService userService; @Test void testGetUser() { User user = userService.getUser(1); assertNotNull(user); } } ``` -------------------------------- ### Test Configuration for Postgres and Ebean Source: https://github.com/avaje/avaje-inject/blob/master/docs/guides/testing-postgres-ebean.md Define test scope and factory beans for PostgresContainer, Ebean Database, and TestEntityBuilder. The PostgresContainer is automatically started for tests. ```java import io.avaje.inject.Factory; import io.avaje.inject.TestScope; import io.ebean.Database; import io.testcontainers.avaje.PostgresContainer; @TestScope @Factory class TestConfiguration { @Bean PostgresContainer container() { return PostgresContainer.builder("17") .dbName("testdb") .containerName("ut_test_postgres") .port(5557) .build() .start(); } @Bean Database database(PostgresContainer container) { return container.ebean().builder() .name("primary") .ddlRun(true) .build(); } @Bean TestEntityBuilder testEntityBuilder(Database database) { return TestEntityBuilder.builder(database).build(); } } ``` -------------------------------- ### Initialize bean after construction Source: https://github.com/avaje/avaje-inject/blob/master/docs/LIBRARY.md Use the `@PostConstruct` annotation on a method to execute initialization logic after a bean has been constructed and its dependencies have been injected. This is ideal for setup tasks. ```java @PostConstruct void init() {} ``` -------------------------------- ### Named Qualifier Example Source: https://github.com/avaje/avaje-inject/blob/master/docs/guides/qualifiers.md Use the @Named qualifier with a string name to distinguish between beans. This is useful when you have multiple implementations of a service and need to inject a specific one. ```java @Singleton @Named("primary") public class PrimaryService implements Service { } @Singleton @Named("secondary") public class SecondaryService implements Service { } @Singleton public class Client { private final Service primary; public Client(@Named("primary") Service service) { this.primary = service; } } ``` -------------------------------- ### Configure LocalStack and SqsClient for Tests Source: https://github.com/avaje/avaje-inject/blob/master/docs/guides/testing-localstack.md Define test scope configuration to provide beans for LocalStack container and SqsClient. The LocalStack container is automatically started and configured for specified AWS services. Ensure the correct LocalStack version and services are listed. ```java @TestScope @Factory class TestConfig { @Bean LocalstackContainer localstack() { return LocalstackContainer.builder("4.3.0") // .mirror("") // optional: use a local/ECR mirror .awsRegion("ap-southeast-2") .services("sqs") // comma-separated list, e.g. "sqs,s3,dynamodb" .containerName("ut_localstack") .port(4567) .start(); } @Bean SqsClient sqsClient(LocalstackContainer localstack) { return localstack.sdk2().sqsClient(); } } ``` -------------------------------- ### Cleanup Valhalla JDK Tarball Source: https://github.com/avaje/avaje-inject/blob/master/how-to-valhalla.md Removes the downloaded Valhalla JDK tarball after successful extraction and installation. ```bash # remove the tarball rm ~/localjdk/openjdk-23-valhalla+1-90_macos-aarch64_bin.tar.gz ``` -------------------------------- ### Test SQS Operations with Injected SqsClient Source: https://github.com/avaje/avaje-inject/blob/master/docs/guides/testing-localstack.md Write integration tests using Avaje Inject's `@InjectTest` to test SQS functionality. The `SqsClient` bean is automatically injected and configured to communicate with the LocalStack instance. This example demonstrates creating a queue, sending a message, and receiving it. ```java @InjectTest class SqsServiceTest { @Inject SqsClient sqsClient; @Test void testSendAndReceive() { // Create a queue, send a message, receive it, etc. String queueUrl = sqsClient.createQueue(r -> r.queueName("test-queue")).queueUrl(); sqsClient.sendMessage(r -> r.queueUrl(queueUrl).messageBody("hello world")); var messages = sqsClient.receiveMessage(r -> r.queueUrl(queueUrl)).messages(); assertFalse(messages.isEmpty()); assertEquals("hello world", messages.get(0).body()); } } ``` -------------------------------- ### Factory for Multiple Named Databases Source: https://github.com/avaje/avaje-inject/blob/master/docs/guides/testing-postgres-ebean.md Define factory beans for multiple named Ebean databases using custom qualifiers. This setup allows injecting specific database instances into tests. ```java import io.avaje.inject.Factory; import io.avaje.inject.TestScope; import io.ebean.Database; import io.testcontainers.avaje.PostgresContainer; @TestScope @Factory class MultiDbTestFactory { @MainDb @Bean Database mainDb(PostgresContainer container) { return container.ebean().builder().build(); } @ExtraDb @Bean Database extraDb(PostgresContainer container) { return container.ebean().extraDatabaseBuilder() .name("extra") .initSqlFile("init-extra-database.sql") .seedSqlFile("seed-extra-database.sql") .build(); } } ``` -------------------------------- ### Using TestEntityBuilder to Save and Find Entities Source: https://github.com/avaje/avaje-inject/blob/master/docs/guides/testing-postgres-ebean.md Persist test entities with random data using TestEntityBuilder and verify retrieval using Ebean's Database. This reduces boilerplate code for test data setup. ```java import io.avaje.inject.Inject; import io.avaje.inject.InjectTest; import io.ebean.Database; import io.ebean.test.TestEntityBuilder; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; @InjectTest class UserServiceTest { @Inject Database database; @Inject TestEntityBuilder builder; @Test void testFindUser() { // Persist a random user User user = builder.save(User.class); // Or: build, customize, then save // User user = builder.build(User.class).setActive(true); // database.save(user); User found = database.find(User.class, user.getId()); assertEquals(user.getName(), found.getName()); } } ``` -------------------------------- ### Named Qualifiers for Multiple Implementations Source: https://github.com/avaje/avaje-inject/blob/master/docs/LIBRARY.md Demonstrates how to use named qualifiers to inject specific implementations of a service. ```java @Singleton public class PrimaryUserService implements UserService {} @Singleton @Named("backup") public class BackupUserService implements UserService {} @Singleton public class UserController { private final UserService primary; private final UserService backup; @Inject public UserController( UserService primary, @Named("backup") UserService backup) { this.primary = primary; this.backup = backup; } } ``` -------------------------------- ### Build Native Image with Maven Source: https://github.com/avaje/avaje-inject/blob/master/docs/guides/native-image.md Execute the Maven command with the 'native' profile to clean and package your project for native image compilation. ```bash mvn -Pnative clean package ``` -------------------------------- ### Lifecycle Hooks with @PostConstruct and @PreDestroy Source: https://github.com/avaje/avaje-inject/blob/master/docs/LIBRARY.md Shows how to use @PostConstruct for initialization and @PreDestroy for cleanup tasks. ```java @Singleton public class DatabasePool { private Connection pool; @PostConstruct void initialize() { pool = createConnectionPool(); } @PreDestroy void cleanup() { pool.close(); } } ``` -------------------------------- ### Factory Methods for Bean Creation Source: https://github.com/avaje/avaje-inject/blob/master/docs/LIBRARY.md Uses a factory to create beans, demonstrating how to define beans using factory methods. ```java @Factory public class DatabaseFactory { @Bean public DatabaseConnection dbConnection() { return new DatabaseConnection("jdbc:mysql://localhost:3306/mydb"); } @Bean public UserRepository userRepository(DatabaseConnection connection) { return new UserRepository(connection); } } ``` -------------------------------- ### Enable Preview Features for Maven Plugins via jvm.config Source: https://github.com/avaje/avaje-inject/blob/master/how-to-valhalla.md Enables preview features for Maven plugins by creating a `.mvn/jvm.config` file with the `--enable-preview` flag. ```bash --enable-preview ``` -------------------------------- ### Basic Bean Definition with Avaje Inject Source: https://github.com/avaje/avaje-inject/blob/master/docs/LIBRARY.md Defines a repository and a service bean, demonstrating basic dependency injection. ```java @Singleton public class UserRepository { public User findById(int id) { return new User(id, "John"); } } @Singleton public class UserService { private final UserRepository repository; @Inject public UserService(UserRepository repository) { this.repository = repository; } public User getUser(int id) { return repository.findById(id); } } ``` -------------------------------- ### Wire and Retrieve Beans using BeanScope Source: https://github.com/avaje/avaje-inject/blob/master/README.md Use BeanScope to build the dependency injection context and retrieve beans. ```java BeanScope beanScope = BeanScope.builder().build() Example ex = beanScope.get(Example.class); ``` -------------------------------- ### Enable Preview Features in Maven Javadoc Plugin Source: https://github.com/avaje/avaje-inject/blob/master/how-to-valhalla.md Configures the maven-javadoc-plugin to enable preview features for Valhalla. ```xml org.apache.maven.plugins maven-javadoc-plugin --enable-preview ``` -------------------------------- ### Basic Factory Method for Bean Creation Source: https://github.com/avaje/avaje-inject/blob/master/docs/guides/factory-methods.md Use @Factory and @Bean annotations to define methods that create and configure beans. Avaje Inject will discover and manage these beans. ```java import io.avaje.inject.Factory; import io.avaje.inject.Bean; @Factory public class DatabaseFactory { @Bean public DataSource createDataSource() { return new HikariDataSource(...); } @Bean public Database createDatabase(DataSource ds) { return new Database(ds); } } ``` -------------------------------- ### Enable Preview Features in Maven Compiler, Surefire, and Failsafe Plugins Source: https://github.com/avaje/avaje-inject/blob/master/how-to-valhalla.md Configures Maven properties to enable preview features for the compiler, surefire, and failsafe plugins. ```xml --enable-preview 23 true ``` -------------------------------- ### Uninstall Valhalla JDK from SDK Man Source: https://github.com/avaje/avaje-inject/blob/master/how-to-valhalla.md Uninstalls the Valhalla JDK from SDK Man. ```bash # uninstall it from sdkman sdk uninstall java 23.ea.valhalla ``` -------------------------------- ### Native Compilation Command Source: https://github.com/avaje/avaje-inject/blob/master/docs/LIBRARY.md Command to compile a Maven project for GraalVM native image. ```bash mvn clean package -Pnative ``` -------------------------------- ### Create and Use BeanScope Source: https://github.com/avaje/avaje-inject/blob/master/inject/src/main/javadoc/overview.html Create a BeanScope using its builder, obtain beans, and ensure the scope is closed to trigger preDestroy lifecycle methods. This can be done via a shutdown hook, try-with-resources, or explicit calls. ```java // create BeanScope BeanScope scope = BeanScope.builder() .build(); // use it CoffeeMaker coffeeMaker = scope.get(CoffeeMaker.class); coffeeMaker.makeIt(); // close it to fire preDestroy lifecycle methods scope.close(); ``` -------------------------------- ### Post-Construct Bean Initialization Source: https://github.com/avaje/avaje-inject/blob/master/docs/guides/lifecycle-hooks.md Use the @PostConstruct annotation to run code after a bean has been created and dependency injection has been completed. ```java @Singleton public class Service { @PostConstruct public void init() { System.out.println("Service initialized"); } } ``` -------------------------------- ### Create a Singleton Bean Class Source: https://github.com/avaje/avaje-inject/blob/master/README.md Define a bean class annotated with @Singleton. Dependencies must also be annotated with @Singleton or provided by a @Factory. ```java @Singleton public class Example { private DependencyClass d1; private DependencyClass2 d2; // Dependencies must be annotated with singleton, // or else be provided from another class annotated with @Factory public Example(DependencyClass d1, DependencyClass2 d2) { this.d1 = d1; this.d2 = d2; } } ``` -------------------------------- ### Unit Test a Service with Mocks Source: https://github.com/avaje/avaje-inject/blob/master/docs/guides/testing.md Manually construct beans and use mocks for dependencies in unit tests. This approach is fast and isolated, avoiding the DI container. ```java @Test void testService() { UserRepository mockRepo = mock(UserRepository.class); UserService service = new UserService(mockRepo); when(mockRepo.findById(1)).thenReturn(new User(1, "John")); User user = service.findById(1); assertEquals("John", user.name); } ``` -------------------------------- ### Define a Value Class as a Record Source: https://github.com/avaje/avaje-inject/blob/master/how-to-valhalla.md Demonstrates defining a `value` class using Java's record syntax. All fields are final by default. ```java public value class MyRecord(String name, int age) {} ``` -------------------------------- ### Declare Singleton and Factory Beans Source: https://github.com/avaje/avaje-inject/blob/master/inject/src/main/javadoc/overview.html Annotate classes with @Singleton for beans to be wired by avaje-inject. Use @Factory and @Bean for programmatic dependency creation with complex logic. ```java @Singleton public class CoffeeMaker { ... } @Singleton public class Pump { ... } // Use @Factory to programmatically build dependencies @Factory public class MyFactory { @Bean Grinder buildGrinder(Pump pump) { // interesting construction logic ... } } ``` -------------------------------- ### Add Avaje-Inject Test Dependency (Gradle) Source: https://github.com/avaje/avaje-inject/blob/master/README.md Optionally, include the avaje-inject-test library for integration testing with Gradle. ```gradle testImplementation 'io.avaje:avaje-inject-test:${avaje.inject.version}' ``` -------------------------------- ### Add Avaje-Inject Dependency (Gradle) Source: https://github.com/avaje/avaje-inject/blob/master/README.md Include the avaje-inject library as a Gradle dependency in your project. ```gradle implementation 'io.avaje:avaje-inject:${avaje.inject.version}' ``` -------------------------------- ### Gradle Dependencies for Avaje Inject Source: https://github.com/avaje/avaje-inject/blob/master/docs/LIBRARY.md Add these lines to your Gradle build file to include Avaje Inject. ```gradle implementation 'io.avaje:avaje-inject:12.5' annotationProcessor 'io.avaje:avaje-inject-generator:12.5' testImplementation 'io.avaje:avaje-inject-test:12.5' ``` -------------------------------- ### Component/Integration Test with @InjectTest Source: https://github.com/avaje/avaje-inject/blob/master/docs/guides/testing.md Use the DI container to wire real beans for component tests. This is similar to Spring's `@SpringBootTest` and is recommended for service/business logic. ```java @InjectTest class UserServiceTest { @Inject UserService userService; @Test void findsUser() { User user = userService.findById(1); assertNotNull(user); } } ``` -------------------------------- ### Test-Specific Beans: Avaje Inject vs Spring Source: https://github.com/avaje/avaje-inject/blob/master/docs/guides/testing-avaje-inject-vs-spring.md Demonstrates how to define test-specific beans. Avaje Inject uses `@TestScope` and `@Factory` with `@Bean`, while Spring uses `@TestConfiguration` with `@Bean` and often requires `@Primary`. ```java @TestScope @Factory class TestConfig { @Bean MyService myService() { return new MyService(...); } } ``` ```java @TestConfiguration class SpringTestConfig { @Bean @Primary // needed if "main" bean also wired during test (not conditionally wired) MyService myService() { return new MyService(...); } } ``` -------------------------------- ### Conditional Wiring: Spring Profiles Source: https://github.com/avaje/avaje-inject/blob/master/docs/guides/testing-avaje-inject-vs-spring.md Illustrates conditional bean wiring in Spring using profiles. `@Profile("!test")` excludes beans from test environments, while `@Profile("test")` includes test-specific beans. ```java @Profile("!test") @Bean DataSource prodDataSource() { ... } @Profile("test") @Bean DataSource testDataSource() { ... } ``` -------------------------------- ### Add Avaje-Inject Dependency (Maven) Source: https://github.com/avaje/avaje-inject/blob/master/README.md Include the avaje-inject library as a Maven dependency in your project. ```xml io.avaje avaje-inject ${avaje.inject.version} ``` -------------------------------- ### Define a Generic Value Class Source: https://github.com/avaje/avaje-inject/blob/master/how-to-valhalla.md Shows the structure of a generic `value` class in Java, highlighting that all fields must be final. ```java value class MyValueClass { // All fields are final private /*final*/ OtherThing dependency; MyValueClass(OtherThing dependency) { this.dependency = dependency; } ... } ``` -------------------------------- ### Add Native Image Plugin to pom.xml Source: https://github.com/avaje/avaje-inject/blob/master/docs/guides/native-image.md Include the GraalVM native-maven-plugin in your project's pom.xml to enable native image building. ```xml org.graalvm.buildtools native-maven-plugin 1.0.0 ``` -------------------------------- ### Using Strongly Typed Qualifiers Source: https://github.com/avaje/avaje-inject/blob/master/docs/guides/qualifiers.md Apply custom qualifier annotations like @Blue and @Green to beans and injections to ensure compile-time type checking and better IDE autocompletion. ```java @Singleton @Blue public class PrimaryService implements Service { } @Singleton @Green public class SecondaryService implements Service { } @Singleton public class Client { private final Service primary; public Client(@Blue Service service) { this.primary = service; } } ``` -------------------------------- ### Extract Valhalla JDK Tarball Source: https://github.com/avaje/avaje-inject/blob/master/how-to-valhalla.md Extracts the Valhalla JDK tarball to the specified directory. ```bash cd ~/localjdk tar -xvf openjdk-*.tar.gz ``` -------------------------------- ### Add Avaje-Inject Test Dependency (Maven) Source: https://github.com/avaje/avaje-inject/blob/master/README.md Optionally, include the avaje-inject-test library for integration testing with Maven. ```xml io.avaje avaje-inject-test ${avaje.inject.version} test ``` -------------------------------- ### Initialize @Inject Fields with Mocks Source: https://github.com/avaje/avaje-inject/blob/master/docs/guides/testing.md Override real beans by initializing `@Inject` fields with mocks. This allows control over specific dependencies and provides test-specific behavior while using the real DI graph. ```java @InjectTest class ServiceWithMockTest { @Inject UserRepository userRepository = mock(UserRepository.class); @Inject UserService userService; @Test void testServiceWithMock() { when(userRepository.findById(1)).thenReturn(new User(1, "Jane")); User user = userService.findById(1); assertEquals("Jane", user.name); } } ``` -------------------------------- ### Optional Bean Creation with Factory Method Source: https://github.com/avaje/avaje-inject/blob/master/docs/guides/factory-methods.md Factory methods can return Optional. If present, the bean is registered; if empty, it's not. Consumers can inject Optional to handle optional integrations. ```java @Factory class MetricsConfig { @Bean Optional graphiteReporter(Configuration config) { if (!config.enabled("metrics.graphite.enabled", false)) { return Optional.empty(); } return Optional.of(GraphiteReporter.builder().build()); } } @Singleton class MetricsReporter { private final Optional reporter; MetricsReporter(Optional reporter) { this.reporter = reporter; } } ``` -------------------------------- ### Maven Dependencies for Avaje Inject Source: https://github.com/avaje/avaje-inject/blob/master/docs/LIBRARY.md Include these dependencies in your Maven project to use Avaje Inject. ```xml io.avaje avaje-inject 12.5 io.avaje avaje-inject-generator 12.5 provided io.avaje avaje-inject-test 12.5 test ``` -------------------------------- ### Basic Singleton Bean Source: https://github.com/avaje/avaje-inject/blob/master/docs/guides/creating-beans.md Mark a class as a singleton bean using the @Singleton annotation. This ensures one instance of the bean is created for the entire application. ```java import jakarta.inject.Singleton; @Singleton public class UserService { public User findById(long id) { return new User(id, "John"); } } ``` -------------------------------- ### Add Avaje-Inject Generator Dependency (Gradle) Source: https://github.com/avaje/avaje-inject/blob/master/README.md Add the avaje-inject-generator annotation processor as a Gradle dependency with 'annotationProcessor' configuration. ```gradle annotationProcessor 'io.avaje:avaje-inject-generator:${avaje.inject.version}' ``` -------------------------------- ### Mark class as application singleton Source: https://github.com/avaje/avaje-inject/blob/master/docs/LIBRARY.md Use the `@Singleton` annotation to define a class as an application-wide singleton bean. This ensures only one instance of the class is created and managed by the injector. ```java @Singleton public class UserService {} ``` -------------------------------- ### Controlling Bean Creation Order with Dependencies Source: https://github.com/avaje/avaje-inject/blob/master/docs/guides/factory-methods.md Bean creation order is determined by dependencies. Declaring a dependency as a constructor or factory-method parameter ensures it's initialized first. ```java @Factory class DatabaseConfig { @Bean Database database(OpenTelemetry openTelemetry, DataSource dataSource) { return Database.builder() .dataSource(dataSource) .build(); } } ``` -------------------------------- ### Test with Multiple Injected Databases Source: https://github.com/avaje/avaje-inject/blob/master/docs/guides/testing-postgres-ebean.md Inject and utilize multiple named databases within a test class using custom qualifiers. The TestEntityBuilder can be used with a specific database if needed. ```java import io.avaje.inject.Inject; import io.avaje.inject.InjectTest; import io.ebean.Database; import io.ebean.test.TestEntityBuilder; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertNotNull; @InjectTest class MultiDbTest { @Inject @MainDb Database mainDb; @Inject @ReportingDb Database reportingDb; @Inject TestEntityBuilder builder; @Test void testAcrossDatabases() { // Use builder with a specific database if needed User user = builder.save(User.class); // uses default injected db assertNotNull(mainDb.find(User.class, user.getId())); // ... test logic for reportingDb as well } } ``` -------------------------------- ### Bean Implementing an Interface Source: https://github.com/avaje/avaje-inject/blob/master/docs/guides/creating-beans.md Beans can implement interfaces. This is useful for defining contracts and allowing for different implementations. ```java public interface UserService { User findById(long id); } @Singleton public class UserServiceImpl implements UserService { @Override public User findById(long id) { return new User(id, "John"); } } ``` -------------------------------- ### Pre-Destroy Bean Cleanup Source: https://github.com/avaje/avaje-inject/blob/master/docs/guides/lifecycle-hooks.md Use the @PreDestroy annotation to run code just before a bean is destroyed by the container. ```java @Singleton public class Service { @PreDestroy public void shutdown() { System.out.println("Service shutting down"); } } ``` -------------------------------- ### Add Avaje-Inject Generator Dependency (Maven) Source: https://github.com/avaje/avaje-inject/blob/master/README.md Add the avaje-inject-generator annotation processor as a Maven dependency with 'provided' scope. ```xml io.avaje avaje-inject-generator ${avaje.inject.version} provided ``` -------------------------------- ### Inject dependencies into constructor, field, or method Source: https://github.com/avaje/avaje-inject/blob/master/docs/LIBRARY.md Use the `@Inject` annotation to specify dependencies that should be automatically injected by Avaje Inject. This can be applied to constructors, fields, or methods. ```java @Inject UserService service ``` -------------------------------- ### Multiple Implementations with @Named Qualifier Source: https://github.com/avaje/avaje-inject/blob/master/docs/guides/dependency-injection.md Use the @Named qualifier to differentiate between multiple implementations of the same interface. This allows injecting a specific implementation based on its name. ```java import io.avaje.inject.Named; import io.avaje.inject.Singleton; public interface Logger { } @Singleton @Named("file") public class FileLogger implements Logger { } @Singleton @Named("console") public class ConsoleLogger implements Logger { } @Singleton public class Service { private final Logger fileLogger; public Service(@Named("file") Logger logger) { this.fileLogger = logger; } } ``` -------------------------------- ### Controlling Bean Scopes Source: https://github.com/avaje/avaje-inject/blob/master/docs/guides/creating-beans.md Control the lifecycle of beans using scope annotations. @Singleton provides a single instance, while @Prototype provides a new instance each time. ```java @Singleton // One instance (default) public class Single { } @Prototype // New instance each time public class Multi { } ``` -------------------------------- ### Specify implementation to inject by name Source: https://github.com/avaje/avaje-inject/blob/master/docs/LIBRARY.md Use the `@Named` qualifier annotation to specify which implementation of a dependency should be injected when multiple implementations exist. The name provided must match the name defined for the bean. ```java @Named("primary") UserService service ``` -------------------------------- ### Strongly Typed Qualifier Annotations Source: https://github.com/avaje/avaje-inject/blob/master/docs/guides/qualifiers.md Define custom qualifier annotations for improved type safety and IDE support. This approach is recommended over named qualifiers to prevent 'Stringly typed' errors. ```java @Qualifier @Target({FIELD, PARAMETER}) @Retention(RUNTIME) public @interface Blue { } @Qualifier @Target({FIELD, PARAMETER}) @Retention(RUNTIME) public @interface Green { } ``` -------------------------------- ### Rename JDK Directory Source: https://github.com/avaje/avaje-inject/blob/master/how-to-valhalla.md Renames the extracted JDK directory to a more descriptive name for easier management. ```bash mv jdk-23.jdk valhalla-23.jdk ``` -------------------------------- ### Mark bean as primary implementation Source: https://github.com/avaje/avaje-inject/blob/master/docs/LIBRARY.md Use the `@Primary` annotation to designate a bean as the primary implementation for its type. This is useful when multiple implementations exist and you want to specify a default one for injection. ```java @Primary @Singleton public class PrimaryImpl {} ``` -------------------------------- ### Define a Custom Scope Annotation Source: https://github.com/avaje/avaje-inject/blob/master/inject/src/main/javadoc/overview.html Create a custom scope annotation by annotating it with @Scope. Optionally, specify required modules or parent scopes using @InjectModule. ```java @Scope public @interface StoreComponent { } ``` ```java @Scope @InjectModule(requires = {QueueComponent.class, SomeExternalDependency.class}) public @interface StoreComponent { } ``` -------------------------------- ### Clean up bean before destruction Source: https://github.com/avaje/avaje-inject/blob/master/docs/LIBRARY.md Use the `@PreDestroy` annotation on a method to execute cleanup logic before a bean is destroyed. This is useful for releasing resources or performing final tasks. ```java @PreDestroy void shutdown() {} ``` -------------------------------- ### Mark class for prototype scope Source: https://github.com/avaje/avaje-inject/blob/master/docs/LIBRARY.md Use the `@Prototype` annotation to define a class for prototype scope. A new instance of this class will be created each time it is injected. ```java @Prototype public class RequestContext {} ``` -------------------------------- ### Use Custom Scope in BeanScope Builder Source: https://github.com/avaje/avaje-inject/blob/master/inject/src/main/javadoc/overview.html When creating a BeanScope, specify the generated module for the custom scope (e.g., StoreComponentModule) and optionally a parent scope using withParent(). ```java BeanScope parentScope = ... BeanScope scope = BeanScope.builder() .modules(new StoreComponentModule()) .parent(parentScope) .build()); StoreLoader storeLoader = scope.get(StoreLoader.class); storeLoader.load(); ``` -------------------------------- ### Constructor Injection with @Inject Annotation Source: https://github.com/avaje/avaje-inject/blob/master/docs/guides/dependency-injection.md When a bean class has multiple constructors, annotate the desired injection constructor with @Inject. This is useful for distinguishing between DI constructors and test constructors. ```java import io.avaje.inject.Inject; import io.avaje.inject.Singleton; import java.util.Optional; @Singleton class MetricsReporter { @Inject MetricsReporter(Configuration config, Optional reporter) { this(config, reporter.map(MetricsReporter::scheduledTask).orElse(null)); } MetricsReporter(Configuration config, ScheduledTask task) { // test-friendly constructor } } ``` -------------------------------- ### Apply Custom Scope Annotation to Beans Source: https://github.com/avaje/avaje-inject/blob/master/inject/src/main/javadoc/overview.html Apply the custom scope annotation (e.g., @StoreComponent) to beans that should be included in that specific scope. ```java @StoreComponent public class StoreLoader { ... } ``` -------------------------------- ### Mark class containing factory methods Source: https://github.com/avaje/avaje-inject/blob/master/docs/LIBRARY.md Use the `@Factory` annotation to designate a class that contains methods for producing beans. These methods are typically annotated with `@Bean`. ```java @Factory public class BeanFactory {} ``` -------------------------------- ### Remove macOS Quarantine Attribute Source: https://github.com/avaje/avaje-inject/blob/master/how-to-valhalla.md On macOS, removes the quarantine attribute from the expanded JDK directory to allow execution of commands. ```bash xattr -d com.apple.quarantine ./jdk-23.jdk ``` -------------------------------- ### Mark method as bean producer Source: https://github.com/avaje/avaje-inject/blob/master/docs/LIBRARY.md Use the `@Bean` annotation on a method within a `@Factory` class to indicate that this method produces a bean. The return type of the method defines the type of the bean. ```java @Bean public UserRepository userRepo() ```