I have an interface like so:
public interface Animal {
void setName(String animal);
String getName();
}
and I have a Class that implements the interface:
#Component
public class Dog implements Animal {
private String name;
public void setName(String name) {
this.name= name;
}
public String getName() {
return this.name;
}
}
In another class (ProcessAnimal), I AutoWire the interface:
public class ProcessAnimal {
#Autowired
public Animal animal;
public void processAnimals() {
animal.setName("Fido");
}
}
I only have one class that implements Animal so this should work, however, I get a NullPointerException when it hits the animal.setName("Fido"); line. IntelliJ is complaining that Autowired members must be defined in valid Spring bean (#Component|#Service...) which I have... I don't understand what I'm doing wrong. I've tried to add a #Qualifier, but still it didn't work and it shouldn't be necessary since I only have one implementation.
-java
-com.example.com.AnimalProcessing
-Animal
-Animal.java
-Dog.java
-ProcessAnimal.java
-AnimalProcessingApplication.java
AnimalProcessingApplication.java
#SpringBootApplication
public class AnimalProcessingApplication {
public static void main(String[] args) {
SpringApplication.run(AnimalProcessingApplication.class, args);
run();
}
public static void run() {
ProcessAnimal processAnimal = new ProcessAnimal();
processAnimal.processAnimals();
}
}
AnimalProcessingApplication class should be one level above all other classes.
Also you are using new for creation of object instead of using Dependency Injection (autowiring).
Replace below -
ProcessAnimal processAnimal = new ProcessAnimal();
with
#Autowired
ProcessAnimal processAnimal;
Also make sure that ProcessAnimal is a bean and Animal is injected in this class using autowiring.
Animal Processing Application.java must be on root folder of all classes.
Then all components in child folders are recognized automatically.
Update:
Create a config class with #Bean method to create an instance with a Dog. Also then you can get rid of the #Component annotation of the class.
The problem here is the constructor String name which cannot be injected.
Update 2:
Don't create the instances by yourself. Let spring container create them. Remove the run method.
Following are to be done to make this program work.
1.ProcessAnimal should be made a component . Annotating the class with #Component will mark the class to be autodetected during component scan.
#Component
public class ProcessAnimal {
#Autowired
public Animal animal;
public void processAnimals() {
animal.setName("Fido");
}
}
Obtain the ProcessAnimal class from the application context. The spring will prepare the ProcessAnimal bean with all its dependencies set.
You may do this in multiple ways and following is one of those
#Component
public class CheckRedRunner implements ApplicationRunner {
#Autowired
ProcessAnimal process;
#Override
public void run(ApplicationArguments args) throws Exception {
process.processAnimals();
}
}
A bean implementing ApplicationRunner will be run when the application starts.
or else
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(AnimalProcessingApplication.class, args);
ProcessAnimal process = ctx.getBean(ProcessAnimal.class);
process.processAnimals();
}
Couple of observations
the package names by convention uses lower case letters
example : com.example.process.entity
Please go through the official documentation to learn the expected way of writing Spring boot application.
Related
I have this scenario
team A is implementing an interface Vehicle as ClassAVehicle
team B is implementing a dashboard service in which it uses vehicle implementation
Now team A have new implementation of Vehicle as ClassBVehicle. And team B wants to use it. One way I know is that use of #Qualifier annotation. But for this I require to change team B's code.
So do I have tight coupling here? Can I have some XML based configuration so that team B's code resolves new ClassBVehicle instance automatically?
interface Vehicle{
int getNoTyre();
}
class ClassAVehicle{
int getNoTyre(){
return 1;
}
}
class ClassBVehicle{
int getNoTyre(){
return 2;
}
}
class Dashboard{
// Here everything is fine until classBVehicle is not there
// Now I want to use new classBVehicle.
// One way I see is that using #Qualifier but will it not be tight coupling?
#Autowired
Vehicle oldAInstance;
}
If you use xml to define bean, your way is good to decouple. Another way is that you can use ApplicationContext to get bean dynamically in annotation program. There are two way to getBean with beanName or beanClass. The below is sample:
#Service
public class BService {
private Vehicle vo;
#Autowired
ApplicationContext context;
public void getVehicle(String beanName){
this.vo = (Vehicle) context.getBean(beanName);
}
public void getVehicle(Class beanClz){
this.vo = (Vehicle) context.getBean(beanClz);
}
public void print(){
System.out.println("---class is "+vo.getClass());
}
}
public interface Vehicle {
}
#Component
public class OneVehicle implements Vehicle{
}
#Component
public class TwoVehicle implements Vehicle{
}
#SpringBootApplication
public class SpringDependenciesExampleApplication implements ApplicationRunner {
#Autowired
BService bService;
public static void main(String[] args) {
SpringApplication.run(SpringDependenciesExampleApplication.class, args);
}
#Override
public void run(ApplicationArguments applicationArguments) throws Exception {
bService.getVehicle("oneVehicle");
bService.print();
}
}
// output is ---class is class OneVehicle
Is there a way to inject a particular interface implementation based on a command line argument in Spring Boot?
I have a data loading app and based on the command line argument I need to load a specific type of data.
Here is my main class and CommandLineRunner:
#SpringBootApplication
public class DataLoadersApplication implements CommandLineRunner {
private Type1LoadProcess type1LoadProcess;
private Type2LoadProcess type2LoadProcess;
public DataLoadersApplication(Type1LoadProcess type1LoadProcess,
Type2LoadProcess type2LoadProcess) {
this.type1LoadProcess = type1LoadProcess;
this.type2LoadProcess = type2LoadProcess;
}
public static void main(String[] args) {
SpringApplication.run(DataLoadersApplication.class, args);
}
#Override
public void run(String... args) {
if (args[0].equalsIgnoreCase("load-type1")) {
type1LoadProcess.process();
} else if (args[0].equalsIgnoreCase("load-type2")) {
type2LoadProcess.process();
}
}
}
Is there a way where I create a DataLoadeProcess interface with two implementations Type1DataLoadProcess and Type2DataLoadProcess and inject the implementaion in main class based on the commandline arg?
You can use Spring profiles to achieve your goal:
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-profiles.html
Create the interface DataLoadProcess
Then the classes:
#Component
#Profile("type1")
public class Type1LoadProcess implements DataLoadProcess {
}
#Component
#Profile("type2")
public class Type2LoadProcess implements DataLoadProcess {
}
Then you can inject the interface type like:
#Autowired
DataLoadProcess dataLoadProcessor;
And now you can start your application with one of the profiles for example with a system property set:
-Dspring.profiles.active=type1
A complete example for this is
#SpringBootApplication
public class DataLoadersApplication implements CommandLineRunner {
public interface LoadProcess {
void doLoad();
}
#Component // default that exists unconditionally in any profile
static class Type1LoadProcess implements LoadProcess {
#Override public void doLoad() { System.out.println("Load1"); }
}
#Profile("type2") // this only exists in the type2 profile
#Primary // if it exists it gets picked over others
#Component
static class Type2LoadProcess implements LoadProcess {
#Override public void doLoad() { System.out.println("Load2"); }
}
// need a 3rd? #Profile("type3") #Primary #Component
#Autowired // need one of them here
private LoadProcess loadProcess;
#Override
public void run(String... args) {
loadProcess.doLoad();
}
public static void main(String[] args) {
SpringApplication.run(DataLoadersApplication.class, args);
}
}
This makes use of profiles and uses the primary bean mechanism to allow for a default implementation when no profile is specified.
You can then select which profile is used via any of the options listed at https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html e.g. by setting an environemnt variable
SPRING_PROFILES_ACTIVE=type2 java -jar myApp.jar
using a property
java -Dspring.profiles.active=type2 java -jar myApp.jar
or even a parameter
java -jar myApp.jar --spring.profiles.active=type2
when you want the type2 implementation. You can still put "type1" as active profile even though it is nowhere defined. It will still do the right thing and use the type1 code since that's the default.
I would use Spring profiles for this. Just turn your implementations into Spring Beans and then load the desired Bean based on an active profile.
When you then specify the active profile(s) as command line parameters when starting the app the respective Bean should get used.
I want to execute a very simple example which explains the IoC-concept in Spring-Boot.
For that I have create a Bean which gets #Autowired to a main-class, which has a method which does something with the bean.
The bean:
The main:
#Component
public class MyMain {
#Autowired
private MyBean bean1;
public void usingTheBean()
{
bean1.setName("Thats my first bean!");
bean1.setAttribute("And thats just an attribute");
System.out.println(bean1);
}
public static void main(String[] args) {
//MyMain main = new MyMain();
//main.usingTheBean();
}
}
My SpringBootApplication:
#SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
MyMain main = new MyMain();
main.usingTheBean();
}
}
How can I start the Main-class? with out getting the
java.lang.NullPointerException
for the #Autowired Bean "MyBean" in the main?
I know that the reason for the NullPointer-Exception is that I created the Main-class with the "new" keyword.
But the question focuses more on the question "How can I start a main-class with spring-boot"
Usually, you do not want to use the context directly to create a bean yourself. You should just let the context initialize and then just use the autowired beans. Most likely, the way you approach this problem is very different from the Spring-way of achieving it.
You should have a look at the following examples:
using the CommandLineRunner interface (see here) or
using the InitializingBean interface (see here)
Alternatively, you can solve this via configuration:
#Configuration
public class MyConfig {
#Bean
public MyBean myBean() {
MyBean bean = new MyBean();
bean.setName("...");
bean.setAttribute("...");
return bean;
}
}
You can then simply use
#Autowired
MyBean myBean;
to autowire it.
Yet another alternative would be to inject the values from a config file (e.g. application.properties) if this is possible in your case:
#Component
public class MyBean {
#Value("${my.config.value}")
private String name;
#Value("${my.config.attribute}")
private String attribute;
public MyBean(){
}
...
Having the following entries in your application.properties:
my.config.value = Some value content
my.config.attribute = Some attribute content
I need to autowire a bean in a standalone main program as i need to set Up some data . Main program has a dependency on "MyDependencyClass" to set up some services.
I am unclear as to how to get the ApplicationContext as the "MyDependencyClass" is not declared in any spring xml,nor the class is annotated. Please help.
My main program :
public class Main {
#Autowired
private MyDependencyClass myDepClass
public static void main(String[] args) {
Main main1 = new Main();
main1.callDep();
}
private void callDep(){
myDepClass.setUp();
}
}
MyDependencyClass:
public class MyDependencyClass {
public void setUp() {
Sysout("Setting up");
}
}
IF you don't define beans in .xml you need to use #Component or #Repository annotation based on your class type. annotations
I have the below bean defined as part a A.jar
package abc;
#Component
public class ParentInterceptor implements ClientInterceptor {
}
I have created another bean in a different project under a different package by extending ParentInerceptor
package xyz;
#Component
public class ChildInterceptor extends ParentInterceptor {
}
In my SpringBoot app , I have a bean defined similar to below
#ComponentScan({"abc","xyz"})
public class Application {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(Application.class, args);
}
#Bean
public Data dataRepo(ParentInterceptor p){
}
}
When I run the main method , I am expecting to see NoUniqueBeanDefinitionException as 2 beans are of same type. However I see both the beans are loaded and ParentInterceptor is being used. Is there a reason why the error is not being thrown?
EDIT
However when i did the below , I was able to see the error being thrown. However I am still unable to understand why an error wasn't thrown in the case listed above.
package xyz;
#Component
public class ChildInterceptor1 extends ChildInterceptor {
}
Application Class:
#ComponentScan({"abc","xyz"})
public class Application {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(Application.class, args);
}
#Bean
public Data dataRepo(ChildInterceptor p){
}
}
EDIT 2
I also tried to check if the child bean indeed extending the parent using the code below -
ctx.getBeansOfType(ParentInterceptor.class)
This returns ParentInterceptor & ChildInterceptor. So I am not really sure why Spring is not returning the error!
In your first example (Parent and Child), i have the correct behaviour, which is the NoUniqueBeanDefinitionException thrown.
If you want to specify which bean to be injected, you can use the #Qualifier annotation:
#Bean
public Data dataRepo(#Qualifier("parentInterceptor") ParentInterceptor p) {
}
Optionally, you can give a name to a component:
#Component("foo")
and change the #Qualifier accordingly.