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
Related
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.
Using Spring Initializer to create a simple Spring boot. I am only choosing DevTools under the options available.
After creating the project, without making any changes to it, able to run the program fine.
Now when I try to do some Autowiring in the project, it simply doesn't work. I don't get it. Been looking all over previous questions here which has resolutions for it but none works plus there is nothing complex about what I am doing in my case as follows. Please advice what I am missing.
#SpringBootApplication
public class DemoApplication {
// #Autowired
// private static Maker maker; // Stopped using this cos I wanted to check if the autowiring is not working in this class only or anywhere. Turns out it is anywhere.
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
Maker maker = new Maker();
maker.printName(); // Fails cos of ServiceHelper Autowiring
}
}
#Service
public class Maker {
#Autowired
private ServiceHelper serviceHelper;
public void printName(){
System.out.println("This is from the method body itself.");
System.out.println("Auto wiring works cos I got this -> " + serviceHelper.help());
}
}
#Component
public class ServiceHelper {
public String help(){
return "...FROM HELPER...";
}
}
StackTrace
Exception in thread "restartedMain"
java.lang.reflect.InvocationTargetException at
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497) at
org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49)
Caused by: java.lang.NullPointerException at
com.example.demo.services.Maker.printName(Maker.java:15) at
com.example.demo.DemoApplication.main(DemoApplication.java:17) ... 5
more
If you create any bean using new keyword that bean will not added to Spring Application Context, and this is one way to #Autowire static beans
#SpringBootApplication
public class DemoApplication {
#Autowired
private static Maker maker;
#Autowired
private Maker tMaker;
#PostConstruct
public void init() {
DemoApplication.maker = tMaker;
}
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
maker.printName();
}
}
or you can do Autowire using Constructor the instance of Maker is injected as an argument to the constructor when DemoApplication is created
#Autowired
public DemoApplication(Maker maker) {
DemoApplication.maker = maker;
}
or you can use #Autowired on setter method, the setter method is called with the instance of Maker when DemoApplication is created
#Autowired
public void setMaker(Maker maker) {
DemoApplication.maker = maker
}
You create yourself an instance, its ServiceHelper does not get autowired by Spring:
Maker maker = new Maker();
You can access bean via ApplicationContext:
#SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
ApplicationContext cts = SpringApplication.run(DemoApplication.class, args);
Maker maker = ctx.getBean(Maker.class);
maker.printName();
}
}
When you use new Maker() you are not using the bean Spring created for you and as a result the the object you have is uninitialized as dependencies are not injected.
You need to get the bean which the Spring framework created, like I did below as directly you cannot autowire static fields:
#SpringBootApplication
public class DemoApplication {
private static Maker maker;
#Autowired
public void setMaker(Maker maker) {
DemoApplication.maker = maker;
}
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
maker.printName();
}
}
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
In Spring Boot, a main class needs to be specified, which is the entry point to the app. Typically this is a simple class with a standard main method, as follows;
#SpringBootApplication
public class MySpringApplication {
public static void main(String [] args) {
SpringApplication.run(MySpringApplication.class, args);
}
}
This class is then specified as the main entry point when the application runs.
However, I want to run my code using a different main class using config to define this, And without using a different jar!! (I know rebuilding the jar will enable me to specify an alternative main class, but this effectively gives me two apps, not one! So, how can I do this to utilise one jar with two main classes and select the one to use via the Spring application.yml file?
I found an answer - use the CommandLineRunner interface...
So now I have two classes;
public class ApplicationStartupRunner1 implements CommandLineRunner {
#Override
public void run(String... args) throws Exception {
//implement behaviour 1
}
and
public class ApplicationStartupRunner2 implements CommandLineRunner {
#Override
public void run(String... args) throws Exception {
//implement behaviour 2
}
and how to switch between them in config..
#Configuration
public class AppConfig {
#Value("${app.runner}")
private int runner;
#Bean
CommandLineRunner getCommandLineRunner() {
CommandLineRunner clRunner = null;
if (runner == 1) {
clRunner = new ApplicationStartupRunner1();
} (else if runner == 2) {
clRunner = new ApplicationStartupRunner2();
} else {
//handle this case..
}
return clRunner;
}
}
and finally in the application.properties file, use
app.runner=1
I would stick with the original pattern of having just one main class and main method, however within that method you could configure where you want to go. I.e. rather than having 2 main methods and configuring which gets called make these 2 methods just normal methods, and create one main method which uses the config to determine which of your two methods get run.
The answer of jonny.l is fine.
Another very similar, but more manual/DIY solution is to get the ApplicationContext, from which you can get all other things:
#SpringBootApplication
public class App {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(App.class);
ConfigurableEnvironment env = context.getBean(ConfigurableEnvironment.class);
String mainApp = env.getProperty("app.runner");
if (mainApp.equals("Main1")) {
Main1 main1 = context.getBean(Main1.class);
main1.run();
} else if (mainApp.equals("Main2")) {
Main2 main2 = context.getBean(Main2.class);
main2.run();
}
}
}
#Service #Lazy
public class Main1 {
public void run() {}
}
#Service #Lazy
public class Main2 {
public void run() {}
}
#Lazyis used to prevent those beans from loading unnecessarily automatically.
Im fairly new to Java Spring IoC and here's my problem
I have a FactoryConfig class with all beans and annotation #Configuration and #ComponentScan written as below.
import org.springframwork.*
#Configuration
#ComponentScan(basePackages="package.name")
public class FactoryConfig {
public FactoryConfig() {
}
#Bean
public Test test(){
return new Test();
}
//And few more #Bean's
}
My Test class has a simple Print method
public class Test {
public void Print() {
System.out.println("Hello Test");
}
}
Now in my Main Class Ive created an ApplicationContentext of FactoryConfig. (I'm expecting all of my #Beans in Factory config will be initialised. However, it returns null when I access the Test class using #Autowired
My Main Class
public class Main {
#Autowired
protected static Test _autoTest;
public static void main(String[] args) throws InterruptedException {
// TODO Auto-generated method stub
ApplicationContext context =
new AnnotationConfigApplicationContext(FactoryConfig.class);
FactoryConfig config = context.getBean(FactoryConfig.class);
config.test().Print();
// _autoTest.Print(); <--- Im getting NULL Pointer Ex here
}
}
What is the correct way to #Autowire and use objects/beans? any clearer explanation would be much appreciated.
Only beans managed by Spring can have #Autowire annotations. Your main class is not managed by Spring: it's created by you and not declared in a Spring context: Spring doesn't known anything about your class, and doesn't inject this property.
You can just access in your main method the Test bean with :
context.getBean(Test.class).Print();
Usually, you get a "bootstrap" from the context, and call this bootstrap to start your application.
Moreover:
On Java, a method shouldn't start with an uppercase. Your Test class should have a print method, not Print.
If you start with Spring, you should maybe try Spring Boot
Spring does not manage your Main class, that's why you are getting Nullpointer Exception.
Using ApplicationContext to load beans, you can get your beans and access Methods as you are already doing -
ApplicationContext context =
new AnnotationConfigApplicationContext(FactoryConfig.class);
FactoryConfig config = context.getBean(FactoryConfig.class);
config.test().Print();
remove the static argument
protected Test _autoTest;
Your class
public class Test {
public void Print() {
System.out.println("Hello Test");
}
}
is not visible to Spring. Try adding an appropriate annotation to it, like #Component.
The reason is that your Main is not managed by Spring. Add it as bean in your configuration:
import org.springframwork.*
#Configuration
#ComponentScan(basePackages="package.name")
public class FactoryConfig {
public FactoryConfig() {
}
#Bean
public Test test(){
return new Test();
}
#Bean
public Main main(){
return new Main();
}
//And few more #Bean's
}
And then you can edit your main() as follows:
public class Main {
#Autowired
protected Test _autoTest;
public static void main(String[] args) throws InterruptedException {
ApplicationContext context =
new AnnotationConfigApplicationContext(FactoryConfig.class);
Test test = context.getBean(Test.class);
Main main = context.getBean(Main.class);
test.Print();
main._autoTest.Print();
}
}