After we talk about generating the Spring context using @Bean and Stereotype annotations in Part 1, now we talk about the third way which is used Programmatically.

This programmatic approach offers flexibility to add the bean into the context, this way will be proper when you want to have a custom way of adding beans into the context, and the preceding ways of @Bean and Stereotype annotations are not sufficient for your case. Therefore, use the programmatic ways if you want to register the bean depending on certain conditions into the context of your application, following the next snippet code:

 if (condition is true) {
    context.registerBean(beanTypeA);
 } else {
    context.registerBean(beanTypeB);
 }

Then, this time we will discuss a basic knowledge to using programmatically to add the bean in the context.

  1. Using Programmatically

    • Define the Animal class for our testing process, In this simple example will use inheritance (Birds, Mammals, and Reptiles) classes concept from Animal (take advantage of the power of OOP).

      @Data
      public class Animal {
          private String description;
      }
      

      Inheritance Animal classes, snippet code:

      • Birds

        public class Birds extends Animal {
            private static final String ANIMAL_INFO = "Birds are warm-blooded vertebrates (vertebrates have backbones) and are the only animals with feathers. Although all birds have wings, a few species can't fly.";
               
            public Birds() {
                super.setDescription(ANIMAL_INFO);
            }
        }
        
      • Mammals

        public class Mammals extends Animal {
            private static final String ANIMAL_INFO = "Mammals are warm-blooded vertebrates (vertebrates have backbones) with hair. They feed their young with milk and have a more well-developed brain than other types of animals.  ";
               
            public Mammals() {
                super.setDescription(ANIMAL_INFO);
            }
        }
        
      • Reptiles

        public class Reptiles extends Animal {
            private static final String ANIMAL_INFO = "Reptiles are cold-blooded vertebrates. (Vertebrates have backbones.) They have dry skin covered with scales or bony plates and usually lay soft-shelled eggs.";
               
            public Reptiles() {
                super.setDescription(ANIMAL_INFO);
            }
        }
        

      Note: This approach is not required using @Bean and Stereotype annotations in our configuration or POJO classes.

    • Define registerBean method of the ApplicationContext instance. This method will register bean class depending on the condition of our requisite application.

      private static AnnotationConfigApplicationContext registerBeans(AnimalType type) {
         var context = new AnnotationConfigApplicationContext(AppConfig.class);
         Animal animal = getAnimalInstance(type);
         context.registerBean(Animal.class, () -> animal);
         return context;
      }
      

      Note: this configuration depends on the specific condition as the snippet code

      private static Animal getAnimalInstance(AnimalType type) {
         switch (type) {
         case BIRDS: return new Birds();
         case MAMMALS: return new Mammals();
         case REPTILES: return new Reptiles();
         }
         throw new IllegalArgumentException("Invalid of Animal Type.");
      }
      
    • Verify the register bean context.

      registerBeans(context, AnimalType.BIRDS);
      Animal animal = context.getBean(Animal.class);
      System.out.println("Animal description : " + animal.getInfo());
           
      registerBeans(context, AnimalType.MAMMALS);
      animal = context.getBean(Animal.class);
      System.out.println("Animal description : " + animal.getInfo());
           
      registerBeans(context, AnimalType.REPTILES);
      animal = context.getBean(Animal.class);
      System.out.println("Animal description : " + animal.getInfo());
      
    • Output

      13:23:54.344 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'animal'
      Animal description : Birds are warm-blooded vertebrates (vertebrates have backbones) and are the only animals with feathers. Although all birds have wings, a few species can't fly.
      13:23:54.346 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'animal'
      Animal description : Mammals are warm-blooded vertebrates (vertebrates have backbones) with hair. They feed their young with milk and have a more well-developed brain than other types of animals.  
      13:23:54.347 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'animal'
      Animal description : Reptiles are cold-blooded vertebrates. (Vertebrates have backbones.) They have dry skin covered with scales or bony plates and usually lay soft-shelled eggs.
           
      Process finished with exit code 0
      

      Note: Referring to the output, we can see that the Animal beans have different behavior depending on the condition of configuration that we expect, and this configuration cannot be reached by using the preceding way (i.e. @Beans and Stereotype annotation).

    After we understand the programmatic works, we will try in our configuration class, so then the bean can be called throughout our application. We will use @Autowired annotation to inject the AnnotationConfigApplicationContext class, the detail in such following next steps:

    • Define property key for the Animal configuration setting.

      animal.type=BIRDS
      
    • Define the specific configuration class and create a method to register the bean with programmatic based on the property key.

      @Configuration
      public class AppAnimalConfig {
           
          @Value("${animal.type}")
          private String animalType;
           
          @Autowired
          public void register(AnnotationConfigApplicationContext context) {
              Animal animal = getAnimalInstance();
              context.registerBean("animal", Animal.class, () -> animal);
          }
           
          private Animal getAnimalInstance() {
              if (AnimalType.BIRDS.name().equals(animalType)) {
                  return new Birds();
              } else if (AnimalType.MAMMALS.name().equals(animalType)) {
                  return new Mammals();
              } else if (AnimalType.REPTILES.name().equals(animalType)) {
                  return new Reptiles();
              }
              throw new IllegalArgumentException("Invalid of Animal Type.");
          }
      }
      
    • Verify the bean in the context as following snippet code.

      @SpringBootApplication
      public class SpringContextApplication implements ApplicationRunner {
           
          ....
           
          @Autowired
          private AnnotationConfigApplicationContext context;
           
          public static void main(String[] args) {
              SpringApplication.run(SpringContextApplication.class, args);
          }
           
          @Override
          public void run(ApplicationArguments args) throws Exception {
              ...
              Animal animal = context.getBean("animal", Animal.class);
              System.out.println("Animal info : " + animal.getDescription());
          }
      }
      
    • Output

      2022-03-16 09:48:47.956  INFO 17580 --- [           main] i.v.b.s.SpringContextApplication         : Starting SpringContextApplication using Java 11.0.8 on Taufiks-MBP with PID 17580 (/Users/mohdtaufik/Workspaces/blogs/sample-code-vimona/spring/spring-context/target/classes started by mohdtaufik in /Users/mohdtaufik/Workspaces/blogs/sample-code-vimona/spring/spring-context)
      2022-03-16 09:48:47.961  INFO 17580 --- [           main] i.v.b.s.SpringContextApplication         : No active profile set, falling back to 1 default profile: "default"
      2022-03-16 09:48:51.843  INFO 17580 --- [           main] i.v.b.s.SpringContextApplication         : Started SpringContextApplication in 4.701 seconds (JVM running for 5.476)
      Car brand : Honda
      Pet name : Woody
      Animal info : Birds are warm-blooded vertebrates (vertebrates have backbones) and are the only animals with feathers. Although all birds have wings, a few species can't fly.
      Disconnected from the target VM, address: '127.0.0.1:58667', transport: 'socket'
           
      Process finished with exit code 0
      

      Note: Referring to the output, there will be print out the Animal info as per the property key which is Birds bean.

That’s it. Hopefully, now we have a better understanding of the basic Spring concept on how to add the bean into the Spring context.

The source code can refer to here.

Reference: