How to convert plain java main method program on docker - spring-boot

I wanted to migrate/run old java code to docker using Jenkins.
It is structured to run using normal main method of java (Jar file having main method is executed through some script).
Its making use of spring.xml(applicationContext.xml) files with spring-context-2.5.xsd
Uses properties file for all configurations.
Questions as I am looking for recommendations now on:
Does this project needs to be migrated to spring-boot application for migrating to/creating docker image?
If yes, please have a look at current code block
Do I need to replace properties files by yml files?
Current code of main class can be framed as :
public class SIIRunner {
public static void main(String[] args){
String envStr = null;
if (args != null && args.length > 0) {
envStr = args[0];
}
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
SIIExecutor siiExecutor= (SIIExecutor) ctx.getBean("SIIExecutor");
siiExecutor.pollAndOperate();
}
}

#SpringBootApplication
public class Application {
public static void main(String[] args) throws Exception {
ApplicationContext app = SpringApplication.run(Application.class,
args);//init the context
SIIExecutor siiExecutor = (SIIExecutor)
app.getBean(SIIExecutor.class);//get the bean by type
}
#Bean // this method is the equivalent of the <bean/> tag in xml
public SIIExecutor getBean(){
return new SIIExecutor();
}
}
As long as you are starting with a base #Configuration class to begin with, which it maybe sounds like you are with #SpringBootApplication, you can use the #ImportResource annotation to include an XML configuration file as well.
#SpringBootApplication
#ImportResource("classpath:beanFileName.xml")
public class SpringConfiguration {
//
}
Spring boot ideal concept is avoid xml file. but if you want to keep xml bean, you can just add #ImportResource("classPath:beanFileName.xml")
I would recommend remove the beanFileName.xml file. and, convert this file to spring annotation based bean. So, whatever class has been created as bean. Just write #Service or #Component annotation before class name. for example:
XML based:
<bean ID="id name" class="com.example.MyBean">
Annotation based:
#Service or #Component
class MyBean {
}
And, add #ComponentScan("Give the package name").
This is the best approach. Hope this helps.

Related

#Autowired notation is not working as expected

#SpringBootApplication
public class MainApplication {
#Autowired
static BibliographyIndexer bi;
public static void main(String[] args) {
SpringApplication.run(MainApplication.class, args);
bi.reindex();
}
}
#Repository
public class BibliographyIndexer {
...
}
Whenever I access the properties of bi I get a NullPointerException. I know the #Autowired notation didn't work. But why?
Note: both classes are under the same package.
Additional question: Since I want to run a method upon the start of the spring application. Is this the best approach since #pepevalbe's answer already gave me the workaround I needed. Is there another way to run a method upon the start of the spring application?
Because you can't #Autorwire an static class. It doesn't get initialized so you get a NPE when trying to use it.
There are workarounds to wire a bean into a static class, but it is strongly discouraged.
EDIT:
If you need to execute code after initilization you could add an event listener:
#SpringBootApplication
public class MainApplication {
#Autowired
BibliographyIndexer bi;
public static void main(String[] args) {
SpringApplication.run(MainApplication.class, args);
}
#EventListener(ApplicationReadyEvent.class)
public void doAfterStartUp() {
bi.reindex();
}
}
There are several reasons #Autowired might not work.
When a new instance is created not by Spring but by for example manually calling a constructor, the instance of the class will not be registered in the Spring context and thus not available for dependency injection. Also when you use #Autowired in the class of which you created a new instance, the Spring context will not be known to it and thus most likely this will also fail.
Another reason can be that the class you want to use #Autowired in, is not picked up by the ComponentScan. This can basically be because of two reasons.
The package is outside the ComponentScan search path. Move the package to a scanned location or configure the ComponentScan to fix this.
The class in which you want to use #Autowired does not have a Spring annotation. Add one of the following annotatons to the class: #Component, #Repository, #Service, #Controller, #Configuration. They have different behaviors so choose carefully........
Your problem is that you cannot use bi in main because main is static.
Making bi static doesn't help because static fields will not be #Autowired (It is possible but does not make sense in the concepts of Dependency Injection).
Remove static and move bi.reindex() to a new method annotated with #PostConstruct. It will be executed after the MainApplication-bean is fully initialized and here you can use your injected bi.
In main method you can refer to context and from it get access to bean BibliographyIndexer. In static main spring can not creates and injects bean so this is how you can get it from context.
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(MainApplication.class, args);
BibliographyIndexer bibliographyIndexer = context.getBean(BibliographyIndexer.class);
bibliographyIndexer.reindex();
}
You can also do as in answer from pepevalbe and execute this code after initialization.

Spring boot #ConfigurationProperties not working

I'm using #ConfigurationProperties for auto configuration of properties. My code is working in IDE. But when I run the jar in command line, it is not working.
Configuration class:
#Configuration
#ConfigurationProperties(prefix="location")
public class Location {
private String base;
public String getBase() {
return base;
}
public void setBase(String base) {
this.base = base;
}
}
Main class:
#SpringBootApplication
#EnableConfigurationProperties(Location.class)
#EnableAutoConfiguration
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
application.yml:
location:
base: c:\test
If I autowire Location class, I see an instance created. But there is not property set. The code is not entering setBase() method.
The application prints this in the console.
AutowiredAnnotationBeanPostProcessor : JSR-330 'javax.inject.Inject'
annotation found and supported for autowiring
Make sure that application.yml file is in the root of your classpath, usually it's put in the resources folder.
The fact that the setBase() method is not called suggests that your application.yml file is not being found. Spring looks in the root of your classpath for the application.yml file.
The comment from M. Deinum is correct saying that your duplicated annotations will result in 2 spring beans for Location class. However, as you say you managed to autowire the bean without getting an error it suggests that your Location class isn't in a package that is found by spring when it's scanning for beans. If there were 2 beans then you'd get an error when you autowired it. By default spring will scan use the package where the #SpringBootApplication is as the base. It will then look in this package and all sub packages.
If your package structure is like this...
myapp.main
Application.java
myapp.config
Location.java
Then you need to add scanBasePackages="myapp" to the #SpringBootApplication annotation.
Also change your main class and remove the #Enable.. annotations. i.e.:
#SpringBootApplication(scanBasePackages="myapp")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
As nothing worked with yaml, I had to change to property file and use
#PropertySource({"classpath:application.properties"})
for the spring to identify the properties file

Receiving Objects from the IoC Container - Spring

have have these two apps which actually do the same (if I am correct)
#SpringBootApplication
public class DemoApplication {
#Autowired
HelloWorld helloWorld;
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
#Bean
public CommandLineRunner run() {
helloWorld.setMessage("wow");
return (load) -> {
helloWorld.getMessage();
};
}
}
and
public class MainApp {
public static void main(String[] args) {
ApplicationContext context = new
ClassPathXmlApplicationContext("Beans.xml");
HelloWorld obj = (HelloWorld) context.getBean("helloWorld");
obj.getMessage();
}
}
both uses
#Component
public class HelloWorld {
private String message;
public void setMessage(String message) {
this.message = message;
}
public void getMessage() {
System.out.println("Your Message : " + message);
}
}
The only difference at the helloWord obj is, that if I use the MainApp-class in my program, then the helloWorld class doesn't need the #Component annotation.
My Question:
If I am correct the SpringBoot annotation makes it unnecessary to define a ClassPathXMLApplicationContext. #Autowire does that for me.
I am now interested if I AutoWire lets say 100 objects at the beginning, all these objects are now in the IoC container correct?
If so: Is not possible to just hand out that container in a CTOR of another class and have access to all saved objects there like:
(HelloWorld) context.getBean("helloWorld"); or
(someRandomClass) context.getBean("someRandomClass")
public CTOR(IOCContainer container) {
this.container = container;
}
Instead of that implementation
public CTOR(HelloWorld helloWorld, SomeRandomClass someRandomClass) {
this.helloWorld = helloWorld;
this.someRandomClass = someRandomClass;
}
And if that is possible, how can I do that?
(There is no use case/task behind my question, i am just interested if that is possible)
The XML'ish way of configuration where you define your bean and wiring via
<bean ... etc. pp.
can be completely replaced by either using
#Component
public class MyClass ....
or by
#Bean
public MyClass myClass() {return new MyClass();}
definition in a configuration class. Both ways place the entity in the IoC container of Spring.
The #Autowire just informs the IoC container of Spring that you would like to have a bean fulfilling the contract of the entity marked with #Autowire injected into this place.
In order to get access to the container you just need to inject the ApplicationContext where you would like to have it.
There are two ways of creating beans in Spring. One is through XML config and the other is through annotation config. Annotation config is the preferred approach as it has lot of advantages over xml config.
Spring boot doesnt have any thing to do with annotation or xml config. Its just a easy way to boot spring application. #Component creates the object of the annotated bean in the application context. #Import or #ImportResource are the annotations used to load the configs from Annotations or through XML configs in Spring boot. With Spring boot u need not create ClassPathXMlCOntext or AnnotationContext objects, but its created internally by spring boot.
#Autowired is a way of getting the beans into any object by injecting rather than tight coupling to the code. Spring container(Application context) do this job of injecting. Just autowiring any class wont create the objects in Spring context. Its just an indication for the Spring context to set the object in the Application context here. You need to create them explicitly inside a xml config/ or annotations like #Component #Service others.
There is no need of hand out of container anywhere. U can just #Autowire ApplicationContext context; in any other spring bean object. With which you can call getBean(YourBean.class) to get that bean.

Manually loading application context to write getBean() in spring boot application

In a spring application, we write like this to get a bean through manually loading spring application context.
ApplicationContext context = new ClassPathXmlApplicationContext("path/to/applicationContext.xml");
JobLauncher launcher=(JobLauncher)context.getBean("launcher");
How to do the similar thing in spring boot ?
Being a newbie...need help
#SpringBootApplication
public class Application {
public static void main(String[] args) throws Exception {
ApplicationContext app = SpringApplication.run(Application .class, args);//init the context
SomeClass myBean = app.getBean(SomeClass.class);//get the bean by type
}
#Bean // this method is the equivalent of the <bean/> tag in xml
public SomeClass getBean(){
return new SomeClass();
}
#Bean
public MyUtilClass myUtil(SomeClass sc){
MyUtilClass uc = new MyUtilClass();
uc.setSomeClassProp(sc);
return uc;
}
}
You can also your xml file to declare the beans instead of the java config, just use #ImportResource({"classpath*:applicationContext.xml"})
Edit: To answer the comment: Make the util class a spring bean(using #Component annotation and component scan or the same as SomeClass shown above) and then you can #Autowire the bean you like. Then when you want to use the Util class just get it from the context.

What is the advantage of autowiring in spring

What exactly is the advantages of autowiring is Spring?
An example of autowiring in spring would be like
public class TestClass {
testMethod() {
// .....
};
}
public class MainClass {
public static void main(String[] args) {
ApplicationContext ctx = new ClasspathXmlApplicationContext("test.xml");
TestMethod obj = (TestClass) ctx.getBean("test");
obj.testMethod();
}
}
test.xml
<bean id="test" class="TestClass">
same in a normal operation could be done using:
public class MainClass {
public static void main(String[] args) {
TestClass obj = new TestClass();
obj.testMethod();
}
}
What is the advantage of Spring, I mean I have heard about terms Inversion of control and Dependency Injection.
In both the examples a reference of TestClass is used once through Spring XML again through new oerator. So can someone in simple terms explain what is the advantage.
Spring is taking care of creating of the objects. Let's say in spring boot you are creating a service:
#Service
public class CreditService { ....
with this you are saying to spring boot that he needs to create an object from type CreditService and whenever you want to use it you don't need to create it you can just say:
#Autowired
private CreditService creditService;
With that you are getting an reference: creditService , that will point to the object that spring boot created for you and call the methods (services). So basically spring is taking care of creation of the object and you are just calling it, not to worry about creating new object anywhere.

Resources