Spring bean injection in a main method class - spring

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.

Related

Trying to use JSR330ScopeMetadataResolver with Spring boot without success

I am trying to retrofit Spring DI into an existing application using Spring Boot. I want to use JSR-330 annotations instead of Spring's. I would like to make prototype scope the default so I don't have to use non-JSR-330 scope #Scope("prototype") everywhere. I first tried to install the JSR330ScopeMetadataResolver using:
SpringApplication application = new SpringApplication(OurApplication.class);
ConfigurableApplicationContext context = application.createContext();
context..setScopeMetadataResolver(new Jsr330ScopeMetadataResolver());
application.run(args);
I still get a singleton when injecting a class with no annotation. I then tried using an ApplicationContextInitializer to do the same thing. Either way, I get a singleton injected. In the debugger, I verified with the initializer that Jsr330ScopeMetadataResolver is getting set in the ApplicationContext. In the Jsr330ScopeMetadataResolver instance, I see a map is getting populated in the default constructor with key /value as singleton. I obviously are missing something. Does anyone know what that is?
We finally were able to figure this out. The solution was:
private static final String CONTEXT_PACKAGES_TO_SCAN =
"my.company.package.*";
public static void main(String[] args) {
SpringApplication application = new SpringApplication(
MainApplication.class);
application.addInitializers(new Jsr330Initializer());
application.run(args);
}
public static class Jsr330Initializer implements
ApplicationContextInitializer<AnnotationConfigApplicationContext>
{
#Override
public void initialize(AnnotationConfigApplicationContext context) {
context.setScopeMetadataResolver(new Jsr330ScopeMetadataResolver());
context.scan(CONTEXT_PACKAGES_TO_SCAN);
}
}
The key is to perform context.scan() in the initializer. Packages to scan must end in ".*", or the main application gets added twice and Spring fails to start.

Adding legacy singleton to Spring ApplicationContext for Injection

I am trying to create a lightweight web service around a legacy java library to expose it as a web service using Spring Boot. I am new to Spring, while I have a lot of java experiance writing libraries all my web service experiance is in ASP.NET.
I can instantiate an instance of my library object but I can't figure out how to then have that object be injected into my controllers via #Autowired when the application is spun up.
This is my main application:
#SpringBootApplication
public class ResolverWebServiceApplication {
private static ArgumentParser newArgumentParser() {
ArgumentParser parser = ArgumentParsers.newFor("Resolver").build();
// configuring the parser
return parser;
}
public static void main(String[] args) throws ArgumentParserException {
ArgumentParser parser = newArgumentParser();
Namespace ns = parser.parseArgs(args);
ResolverOptions options = new ResolverOptions.Builder(ns)
.build();
ResolverContext context = new ResolverContext(options);
// ^^^ I need to get this injected into my controllers ^^^
SpringApplication.run(ResolverWebServiceApplication.class, args);
}
}
And then a simple controller which needs the class injected:
#RestController
public class VersionController {
#Autowired
private ResolverContext context; // And here the instance needs to be injected.
#GetMapping(path = "/version", produces = MediaType.APPLICATION_JSON_VALUE)
public long version() {
return context.getResolver().getVersionAsLong();
}
}
I could make the context a singleton which the controllers just refer to but I want to be able to test my controllers by mocking the context. There is also obviously a lot of validation and error handeling that needs to be added.
I can't have it be a Bean since I only want to instantiate one for my entire application.
The closest question I have found is this one: Registering an instance as 'singleton' bean at application startup. But I can't put the options in the configuration files. The application might be spun up in a container or on a users machine and requires the ability to accept arguments to initialize the library class. It would be a real usability degradation if someone had to manually edit the application config for these options.
You need to tell spring to consider the required classes from your lib when initializing the application context i.e Configure and let spring know how to create a bean and then let spring handle dependency injection for you.
First of all, add required jar that you have in your build file, say pom.xml for maven, in your current project. Idea is to have it on your classpath when you build the project.
As you said it is legacy lib and I am assuming it is not a spring bean, then
In your configuration class, return it as a bean, using #Bean annotaion.
#Configuration
public class YourConfigurationClass {
#Bean
SomeBean returnSomeBeanFromLegacyLib() {
return new SomeClassFromLib();
}
Once you return this bean from your config, it should be available to Spring Context for dependency injection whereever you #Autowire the required dependency.

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.

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.

Use spring application as library in non-spring application

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);

Resources