Use spring application as library in non-spring application - spring

I implemented spring-boot application and now I want to use it as a lib for non-spring application.
How can I initialize lib classes so autowired dependencies work as expected?Obviously if I create class instance with 'new', all autowired dependencies will be null.

The theory is that you need to instantiate an application context for your Spring Boot dependency to live in, then extract a bean from there and make use of it.
In practice, in your Spring Boot dependency you should have an Application.java class or similar, in which a main method starts the application. Start by adding there a method like this:
public static ApplicationContext initializeContext(final String[] args) {
return SpringApplication.run(Application.class, args);
}
Next step, in you main application, when you see fit (I'd say during startup but might as well be the first time you need to use your dependency) you need to run this code:
final String[] args = new String[0]; // configure the Spring Boot app as needed
final ApplicationContext context = Application.initializeContext(args); // createSpring application context
final YourBean yourBean = (YourBean)context.getBean("yourBean"); // get a reference of your bean from the application context
From here you can use your beans as you see fit.

I'm not sure how you will handle to wait until the context is fully loaded before you try to access some beans from your Constructor etc. But if you just want to access context without creating components manually try one of those:
1) Simply Inject ApplicationContext
#Inject
private ApplicationContext context;
or
2) Implement ApplicationContextAware
public class ApplicationContext implements ApplicationContextAware {
private ApplicationContext context;
#Override
public void setApplicationContext(ApplicationContext context) {
this.context = context;
}
// just quick example, better to set it to your custom singleton class
public static ApplicationContext getContext() {
return context;
}
}
Then use context.getBean(SomeServiceFromLibrary.class);

Related

In a Spring Boot application is a Bean Factory/Application context ever explicitly used?

I am fairly new to Spring Boot and it is my sense from looking at sample applications that if a Bean Factory is ever used, it is used "under the covers" by Spring Boot. Or are there cases when using Spring Boot that you would in fact want to explicitly obtain a bean using the Bean Factory?
Every once in a while, I access Spring ApplicationContext from a bean that I initialize with new (basically a non-Spring managed bean) as follows:
#Component
public class ApplicationContextProvider implements ApplicationContextAware {
private static ApplicationContext context;
public static ApplicationContext getApplicationContext() {
return context;
}
#Override
public void setApplicationContext(ApplicationContext ac)
throws BeansException {
context = ac;
}
}
and in wherever I need it:
SomeBean someBean = ApplicationContextProvider.getApplicationContext().getBean("testBean", TestBean.class);
This is because:
I have to access a singleton (say, a #Service or a #Repository) from a bean I initialize (new) and I cannot make my bean Spring managed for that case. (so no #Autowired).
I don't want to introduce #Configurable to the project because it brings in AspectJ, which might be overkill to introduce for this simple case.

ClassBridge with DAO class injected

I have a Hibernate Search ClassBridge where I want to use #Inject to inject a Spring 4.1 managed DAO/Service class. I have annotated the ClassBridge with #Configurable. I noticed that Spring 4.2 adds some additional lifecycle methods that might do the trick, but I'm on Spring 4.1
The goal of this is to store a custom field into the index document based on a query result.
However, since the DAO, depends on the SessionFactory getting initialized, it doesn't get injected because it doesn't exist yet when the #Configurable bean gets processed.
Any suggestions on how to achieve this?
You might try to create a custom field bridge provider, which could get hold of the Spring application context through some static method. When provideFieldBridge() is called you may return a Spring-ified instance of that from the application context, assuming the timing is better and the DAO bean is available by then.
Not sure whether it'd fly, but it may be worth trying.
Hibernate Search 5.8.0 includes support for bean injection. You can see the issue https://hibernate.atlassian.net/browse/HSEARCH-1316.
However I couldn't make it work in my application and I had implemented a workaround.
I have created an application context provider to obtain the Spring application context.
public class ApplicationContextProvider implements ApplicationContextAware {
private static ApplicationContext context;
public static ApplicationContext getApplicationContext() {
return context;
}
#Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
ApplicationContextProvider.context = context;
}
}
I have added it to the configuration class.
#Configuration
public class RootConfig {
#Bean
public ApplicationContextProvider applicationContextProvider() {
return new ApplicationContextProvider();
}
}
Finally I have used it in a bridge to retrieve the spring beans.
public class AttachmentTikaBridge extends TikaBridge {
#Override
public void set(String name, Object value, Document document, LuceneOptions luceneOptions) {
// get service bean from the application context provider (to be replaced when HS bridges support beans injection)
ApplicationContext applicationContext = ApplicationContextProvider.getApplicationContext();
ExampleService exampleService = applicationContext.getBean(ExampleService .class);
// use exampleService ...
super.set(name, content, document, luceneOptions);
}
}
I think this workaround it's quite simple in comparision with other solutions and it doesn't have any big side effect except the bean injection happens in runtime.

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.

how to get beans from application context loaded by contextloaderlistener?

I'm new to Spring/Spring Mvc and here's my problem. In my webapp, besides the spring-servlet.xml , I have a jdbc.xml which define beans like datasource, dao ... Before using contextloaderlistener , I load my jdbc.xml inside the Controller's constructor like this ApplicationContext context = new ClassPathXmlApplicationContext("jdbcbeans.xml") then get the beans from that. But since I'm using contextloaderlistener to load the file, how can I get the reference to the context ? I was able to set up everything using those #Autowired things but I just want to know is there any way to do that ?
You can use WebApplicationContextUtils.
ApplicationContext context;
context = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);
See here for details.
You can do the following to get an instance of Application Context
in case of a container managed bean use ApplicationContextAware interface
public class MyBean implements ApplicationContextAware {
private static ApplicationContext context;
public void setApplicationContext(ApplicationContext acontext) throws BeansException {
context = context;
}
public static ApplicationContext getApplicationContext() {
return context;
}
}
Or you can write the following
#Autowired
private ApplicationContext Context;
An instance of the Application Context will be autowired.

Spring bean injection in a main method class

I have a web application with spring 3.0. I need to run a class with main method from a cron that uses beans defined in appcontext xml(using component scan annocations). I have my main class in same src directory.
How can I inject beans from web context into main method. I tried to do it using
ApplicationContext context = new ClassPathXmlApplicationContext("appservlet.xml");
I tried to use AutoWired and it returns a null bean. So I used Application ctx and this is creating a new context (as expected) when I run main method. But is it possible that I can use existing beans from container.
#Autowired
static DAO dao;
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("xman- servlet.xml");
TableClient client = context.getBean(TableClient.class);
client.start(context);
}
You can not inject a Spring bean into any object that was not created by spring. Another way to say that is: Spring can only inject into objects that it manages.
Since you are creating the context, you will need to call getBean for your DAO object.
Check out Spring Batch it may be useful to you.
You may use a spring context for your main application, and reuse the same beans as the webapp. You could even reuse some Spring XML configuration files, provided they don't define beans which only make sense in a webapp context (request-scope, web controllers, etc.).
But you'll get different instances, since you'll have two JVMs running. If you really want to reuse the same bean instances, then your main class should remotely call some method of a bean in your webapp, using a web service, or HttpInvoker.
Try with this Main:
public class Main {
public static void main(String[] args) {
Main p = new Main();
p.start(args);
}
#Autowired
private MyBean myBean;
private void start(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("classpath*:/META-INF/spring/applicationContext*.xml");
System.out.println("The method of my Bean: " + myBean.getStr());
}
}
And this Bean:
#Service
public class MyBean {
public String getStr() {
return "mybean!";
}
}
In order to address this issue, I have created https://jira.springsource.org/browse/SPR-9044. If you like the proposed approach, please vote for it.
Spring boot provides an official solution for this. Download a skeleton from
https://start.spring.io/
and make sure packaging in the pom.xml is set to jar. As long as you don't include any web dependency the application will remain a console app.

Resources