Pages

Saturday, April 25, 2020

Building Calculator Application in Spring Boot - @Bean Example



1. Introduction


In this tutorial, We'll learn how to build a simple calculator application using spring boot framework. This is mainly demonstrating to understand @Bean and @Componet annotations usage and how the dependencies are getting injected by the application context.

First, We will see the creation of the calculator interface and its implementation classes.

Next, Creating the Bean with @Bean annotation using the method injection technique.

Finally, Understanding the execution and dependency injections.

Further, You can read examples of bean creation using @Component and @Bean annotations.




2. Creating a Calculator Application


By default, Spring boot does autoconfiguration and detects all @Componet components.
Additionally, you can use @Bean annotation with the help of factory methods. @Bean gives us the flexibility of construction of the bean.

But, here you have to remember is the always method name is considered as bean name so we have to keep the bean name as method name as below.

The below is just an example and not part of the calculator program. Do not get confused.

[@Bean
public Student student(Address a){
return new Student(a);
}]

2.1 Creating Operation Base Interface


[package com.javaprogramto.caculator.calculatordemo;
public interface Operation {
long apply(long a, long b);
boolean valid(char operator);
}]

2.2 Creating Operation Implementation classes


Now, Adding three operations for Addition, multiplication, and division operations. But, this still can be extensible for subtraction and other operations log, root operations as well.

2.2.1 Addition Class


[import org.springframework.stereotype.Component;
@Component
public class Addition implements Operation {
@Override
public long apply(long a, long b) {
return a + b;
}
@Override
public boolean valid(char operator) {
return '+' == operator;
}
}]


2.2.2 Division Class


[import org.springframework.stereotype.Component;
@Component
public class Division implements Operation {
@Override
public long apply(long a, long b) {
return a / b;
}
@Override
public boolean valid(char operator) {
return '/' == operator;
}
}]


2.2.3 Multiplication Class


[import org.springframework.stereotype.Component;
@Component
public class Multiplication implements Operation {
@Override
public long apply(long a, long b) {
return a * b;
}
@Override
public boolean valid(char operator) {
return '*' == operator;
}
}]


2.3 Creating Calculator class


This class does the basic operation for the given values.

[package com.javaprogramto.caculator.calculatordemo;
public class Calculator {
Logger logger = LoggerFactory.getLogger(getClass());
private List<Operation> operations;
public Calculator(List<Operation> operations) {
super();
this.operations = operations;
}
public void calculate(long a, long b, char operator) {
for (Operation o : operations) {
if (o.valid(operator)) {
long output = o.apply(a, b);
logger.info(a + " " + operator + " " + b + " = " + output);
return;
}
throw new IllegalArgumentException("Illegal operation for operator : "+operator);
}
}
}]


This class is dependent on the List<Operation> so we should tell to spring to inject all of these.

Fist of all, it checks if the given operation is valid then valid() method returns true and further calls apply() method that does actual core logic for calculation.

If in case given operator is not matching to the list of available Operation's then it will throw IllegalArgumentException exception.


2.4 Main SpringBootApplication


In this application, Created a bean with @Bean annotation with the method

[package com.javaprogramto.caculator.calculatordemo;
import java.util.List;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class CalculatorDemoApplication {
public static void main(String[] args) {
SpringApplication.run(CalculatorDemoApplication.class, args);
}
@Bean
public Calculator calculator(List<Operation> operations) {
return new Calculator(operations);
}
@Bean
public ApplicationRunner applicationRunner(Calculator calculator) {
return args -> {
calculator.calculate(10, 20, '+');
calculator.calculate(100, 20, '/');
calculator.calculate(10, 3, '*');
};
}
}]

Output:

[2020-04-25 12:46:45.751  INFO 50913 --- [           main] c.j.c.c.CalculatorDemoApplication        : Starting CalculatorDemoApplication on -Pro-2.local with PID 50913 (/Users/venkateshn/Documents/VenkY/blog/workspace/CaclulatorExample/target/classes started by venkateshn in /Users/venkateshn/Documents/VenkY/blog/workspace/CaclulatorExample)
2020-04-25 12:46:45.754  INFO 50913 --- [           main] c.j.c.c.CalculatorDemoApplication        : No active profile set, falling back to default profiles: default
2020-04-25 12:46:46.380  INFO 50913 --- [           main] c.j.c.c.CalculatorDemoApplication        : Started CalculatorDemoApplication in 0.914 seconds (JVM running for 1.291)
2020-04-25 12:46:46.382  INFO 50913 --- [           main] c.j.caculator.calculatordemo.Calculator  : 10 + 20 = 30
2020-04-25 12:46:46.382  INFO 50913 --- [           main] c.j.caculator.calculatordemo.Calculator  : 100 / 20 = 5
2020-04-25 12:46:46.382  INFO 50913 --- [           main] c.j.caculator.calculatordemo.Calculator  : 10 * 3 = 30]

Here, Spring Boot will automatically detect all classes of Operation implementations with @Bean annotation.

Note: Instead of using @Bean, you can @Component on the Calculator class and @Autowired on the right constructors if there are multiple constructors.

If you pass any other operations than valid then it will throw runtime exception as below because it does not match with the Operation implementors classes.

[calculator.calculate(10, 3, '-');]

Output:


[java.lang.IllegalStateException: Failed to execute ApplicationRunner
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:778) [spring-boot-2.2.6.RELEASE.jar:2.2.6.RELEASE]
at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:765) [spring-boot-2.2.6.RELEASE.jar:2.2.6.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:322) [spring-boot-2.2.6.RELEASE.jar:2.2.6.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1226) [spring-boot-2.2.6.RELEASE.jar:2.2.6.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1215) [spring-boot-2.2.6.RELEASE.jar:2.2.6.RELEASE]
at com.javaprogramto.caculator.calculatordemo.CalculatorDemoApplication.main(CalculatorDemoApplication.java:15) [classes/:na]
Caused by: java.lang.IllegalArgumentException: Illegal operation for operator : -
at com.javaprogramto.caculator.calculatordemo.Calculator.calculate(Calculator.java:31) ~[classes/:na]
at com.javaprogramto.caculator.calculatordemo.CalculatorDemoApplication.lambda$0(CalculatorDemoApplication.java:33) [classes/:na]
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:775) [spring-boot-2.2.6.RELEASE.jar:2.2.6.RELEASE]
... 5 common frames omitted]


If you define the @Componet and @Bean method these two and run the application will produce the below error.


[2020-04-25 13:06:35.353  WARN 51621 --- [           main] s.c.a.AnnotationConfigApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.support.BeanDefinitionOverrideException: Invalid bean definition with name 'calculator' defined in com.javaprogramto.caculator.calculatordemo.CalculatorDemoApplication: Cannot register bean definition [Root bean: class [null]; scope=; abstract=false; lazyInit=null; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=calculatorDemoApplication; factoryMethodName=calculator; initMethodName=null; destroyMethodName=(inferred); defined in com.javaprogramto.caculator.calculatordemo.CalculatorDemoApplication] for bean 'calculator': There is already [Generic bean: class [com.javaprogramto.caculator.calculatordemo.Calculator]; scope=singleton; abstract=false; lazyInit=null; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in file [/Users/venkateshn/Documents/VenkY/blog/workspace/CaclulatorExample/target/classes/com/javaprogramto/caculator/calculatordemo/Calculator.class]] bound.
2020-04-25 13:06:35.371  INFO 51621 --- [           main] ConditionEvaluationReportLoggingListener :
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2020-04-25 13:06:35.372 ERROR 51621 --- [           main] o.s.b.d.LoggingFailureAnalysisReporter   :
***************************
APPLICATION FAILED TO START
***************************
Description:
The bean 'calculator', defined in com.javaprogramto.caculator.calculatordemo.CalculatorDemoApplication, could not be registered. A bean with that name has already been defined in file [/Users/venkateshn/Documents/VenkY/blog/workspace/CaclulatorExample/target/classes/com/javaprogramto/caculator/calculatordemo/Calculator.class] and overriding is disabled.
Action:
Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true]

This error is produced because of calculator bean is defined twice and can not be used the same name for another bean. So, either we need to provide a different name for the bean as below or need to provide overriding property to true.

application.properties

[spring.main.allow-bean-definition-overriding=true]


[@Bean("cal")
public Calculator calculator(List<Operation> operations) {
return new Calculator(operations);
}]


4. Conclusion


In conclusion, We've seen how to perform simple calculator operations in spring boot with the power of @Bean and @Componet annotations. And also we can extend the functionalities for other operators just providing the implementations for Operation interface.

As usual, the complete code is over GitHub and you can download from below links.

[lock]

[View on GitHub ##eye##]

[Download ##file-download##]

[/lock]

Spring Boot Calculator Application


  • [accordion]
    • Operation.java
      • package com.javaprogramto.caculator.calculatordemo;
        
        public interface Operation {
        
         long apply(long a, long b);
        
         boolean valid(char operator);
        
        }
    • Addition.java
      • package com.javaprogramto.caculator.calculatordemo;
        
        import org.springframework.stereotype.Component;
        
        @Component
        public class Addition implements Operation {
        
         @Override
         public long apply(long a, long b) {
        
          return a + b;
         }
        
         @Override
         public boolean valid(char operator) {
        
          return '+' == operator;
         }
        
        }
    • Division.java
      • package com.javaprogramto.caculator.calculatordemo;
        
        import org.springframework.stereotype.Component;
        
        @Component
        public class Division implements Operation {
        
         @Override
         public long apply(long a, long b) {
        
          return a / b;
         }
        
         @Override
         public boolean valid(char operator) {
        
          return '/' == operator;
         }
        }
    • Multiplication.java
      • package com.javaprogramto.caculator.calculatordemo;
        
        import org.springframework.stereotype.Component;
        
        @Component
        public class Multiplication implements Operation {
         @Override
         public long apply(long a, long b) {
        
          return a * b;
         }
        
         @Override
         public boolean valid(char operator) {
        
          return '*' == operator;
         }
        }
    • Calculator.java
      • package com.javaprogramto.caculator.calculatordemo;
        
        import java.util.List;
        
        import org.slf4j.Logger;
        import org.slf4j.LoggerFactory;
        import org.springframework.stereotype.Component;
        
        //@Component
        public class Calculator {
        
         Logger logger = LoggerFactory.getLogger(getClass());
        
         private List<Operation> operations;
        
         public Calculator(List<Operation> operations) {
          super();
          this.operations = operations;
         }
        
         public void calculate(long a, long b, char operator) {
        
          for (Operation o : operations) {
           if (o.valid(operator)) {
        
            long output = o.apply(a, b);
            logger.info(a + " " + operator + " " + b + " = " + output);
            return;
           }
          }
          throw new IllegalArgumentException("Illegal operation for operator : " + operator);
         }
        
        }
    • CalculatorDemoApplication.java
      • package com.javaprogramto.caculator.calculatordemo;
        
        import java.util.List;
        
        import org.springframework.boot.ApplicationRunner;
        import org.springframework.boot.SpringApplication;
        import org.springframework.boot.autoconfigure.SpringBootApplication;
        import org.springframework.context.annotation.Bean;
        
        @SpringBootApplication
        public class CalculatorDemoApplication {
        
         public static void main(String[] args) {
          SpringApplication.run(CalculatorDemoApplication.class, args);
        
         }
        
         //@Bean("cal")
         @Bean
         public Calculator calculator(List operations) {
        
          return new Calculator(operations);
         }
        
         @Bean
         public ApplicationRunner applicationRunner(Calculator calculator) {
        
          return args -> {
        
           calculator.calculate(10, 20, '+');
           calculator.calculate(100, 20, '/');
           calculator.calculate(10, 3, '*');
           //calculator.calculate(10, 3, '-');
          };
        
         }
        
        }
        

No comments:

Post a Comment

Please do not add any spam links in the comments section.