Create ApplicationContext as Spring bean (by other application context) - java

How i can define one ApplicationContext as prototype spring bean in other application context. Also i need pass current context as parent to new application context.
Details:
I have Bean, that represent one user sessions in rich client application. This class manage lifetime of application context, and few other objects (like database connection). This session beans itself configured by special "start-up application context" .
Now i'm want unit test this session beans, but have trouble because session specific application context created inside session bean, and has many depend to "start-up context";
Example code:
public class UserDBAminSession implements ApplicationContextAware, UserSession {
ApplicationContext startupContext;
ApplicationContext sessionContext;
public void setApplicationContext(ApplicationContext startupContext) {...}
public void start() {
createSessionContext() ;
}
private void createSessionContext() {
sessionContext = new ClassPathXmlApplicationContext("admin-session.xml", startupContext);
}
}
For testing purpose i want relapse createSessionContext function code with something like this:
sessionContext = startupContext.getBean("adminContext", ApplicationContext.class);
Then i can create mock of startupContext, what return some stub. Or even DI "session context" to bean by spring, in some future. But, i don't know how pass parent context parameter to ClassPathXmlApplicationContext constructor. I'm try something like this, but it seems not work:
<bean id="adminContext" class="org.springframework.context.support.ClassPathXmlApplicationContext"
scope="prototype" autowire="constructor">
<constructor-arg type="java.lang.String">
<value>admin-session.xml</value>
</constructor-arg>
</bean>
Also I'm think about create application context on top level and pass it by setter, but:
This just move problem to above level, not solve. In fact it already done (UserSession - are this "top level").
This broke RAII pattern.
This need huge code refactoring.
Or make special "context factory" objects, but it harder already not best code.
What look stupid, I can't IoC objects from IoC framework itself. May be i'm misread some spring documentation?
Any other idea, how unit-test this class?

Use FactoryBean and ApplicationContextAware interfaces.
public class ChildApplicationContextFactoryBean implements FactoryBean, ApplicationContextAware {
protected String[] configLocations;
protected ApplicationContext applicationContext;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
#Override
public Object getObject() throws Exception {
return new ClassPathXmlApplicationContext(configLocations, applicationContext);
}
#Override
public Class getObjectType() {
return ClassPathXmlApplicationContext.class;
}
#Override
public boolean isSingleton() {
return true;
}
public void setConfigLocations(String[] configLocations) {
this.configLocations = configLocations;
}
}
Usage:
<bean class="com.skg.ecg.portal.operation.transit.ChildApplicationContextFactoryBean">
<property name="configLocations">
<list>
<value>applicationContext.xml</value>
</list>
</property>
</bean>

If I understand you correctly, your requirement is for managing a collection of beans within a manually-controlled scope (your RIA session, in this case).
Spring has support for scoped beans. You have the basic singleton and prototype scopes, and webapps get the request and session scopes as well. If your RIA session is actually an HTTP session, then I suggest you use session-scoped beans instead of your manually-nested application context design.
If your sessions are not web-related, then you still have the option of definign your own custom scope. There's more work in this, but it is a defined extension point in the container, so you're on safe ground.
Your original idea of application contexts being themselves beans within a parent context would work, yes, but it's probably unnecessary in this case, and just adds complexity. If you want to investigate it further, however, have a look at the SingletonBeanFactoryLocator, which is a Spring infrastructure class for managing hierarchies of application contexts. It won't do the specific job you want, but it might give you ideas.

Related

Spring's method injection

I'm reading the Spring current version's documentation and have problems with understanding of the chapter 5.4.6 Method Injection. As far as I got we can provide the ability of recreation a bean every time we call the method using the bean. Documentation provides the following code example:
public class CommandManager implements ApplicationContextAware {
//Imports and comments were omitted
private ApplicationContext applicationContext;
public Object process(Map commandState) {
Command command = createCommand();
command.setState(commandState);
return command.execute();
}
protected Command createCommand() {
return this.applicationContext.getBean("command", Command.class);
}
public void setApplicationContext(
ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
where ApplicationContextAware interface looks like:
public interface ApplicationContextAware {
//JavaDocs ommited
void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
}
So, what is the method public Object process(Map commandState)? Is it a callback? If so, where is it going to be called and who performs that call? After all, it's not clear why the bean is goign to be recreated every time it needed.
The Command and CommandManager classes are just example classes, where process() is a part of that example. They have nothing to do with ApplicationContextAware.
Note the comment in the example for CommandManager:
// a class that uses a stateful Command-style class to perform some processing
process() is to be called by the application, somewhere else; it doesn't matter for the sake of the example. If you are not using exactly the same model in your code, you should ignore this method and just call applicationContext.getBean() where applicable.
Finally, yes CommandManager should be registered as a bean in order for spring to call setApplicationContext().
edit
why does spring know that it have to recreate the bean with the name command every time
Given the contents of the example, it doesn't. The example code calls getBean(), which, according to the javadocs:
Return an instance, which may be shared or independent, of the specified bean.
In order to ensure that you always get a new instance, you'll need to use the prototype scope.
<bean id="beanB class="Command" scope="prototype"/>
Injection is happening actually here:
protected Command createCommand() {
// notice the Spring API dependency!
return this.applicationContext.getBean("command", Command.class);
}
We are just getting bean with different lifecycle in this method.
According to Spring's suggestion
A solution is to forego some inversion of control. You can make bean A aware of the container by implementing the ApplicationContextAware interface, and by making a getBean("B") call to the container ask for (a typically new) bean B instance every time bean A needs it.
solution for method get() could look like this:
public class SingletonA implements ApplicationContextAware {
private ApplicationContext applicationContext;
public PrototypeB getPrototypeB() {
return applicationContext.getBean("prototypeB",PrototypeB.class);
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
with beans defenition
<bean id="singletonA" class="di.methodinjection.SingletonA" autowire="byName"/>
<bean id="prototypeB" class="di.methodinjection.PrototypeB" scope="prototype"/>

Spring AOP - pointcut/interceptor not called

I have defined the following interceptor:
#Aspect
public class OpenSessionInRequestInterceptor {
private Log log = LogFactory.getLog(getClass());
#Autowired
private SessionFactory sessionFactory;
public OpenSessionInRequestInterceptor() {
}
#Around("#annotation(com.sc2.master.aop.hibernate.OpenSession)")
public Object processAround(ProceedingJoinPoint pjp) throws Throwable {
log.info("Opening Hibernate Session in method "+pjp.getSignature());
Session session = SessionFactoryUtils.getSession(sessionFactory, true);
TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));
Object ret = pjp.proceed();
session.close();
TransactionSynchronizationManager.unbindResource(sessionFactory);
log.info("Closing Hibernate Session in method "+pjp.getSignature());
return ret;
}
}
When I execute the following piece of code in a spring test
#OpenSession
public void call() {
BusinessCustomer customer = (BusinessCustomer) this.customerDao.loadAll().get(0);
System.out.println(customer.getContacts().size());
}
the aspect method is called. To start the test my test case class looks as follows:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations={"file:WebContent/WEB-INF/applicationContext.xml"})
#Transactional
However, when I have a method annotated with #OpenSession and deploy the application on my Tomcat server, the interceptor method is not called.
The application context definition looks as follows:
<aop:aspectj-autoproxy proxy-target-class="true">
</aop:aspectj-autoproxy>
<bean id="openSessionInRequestInterceptor" class="OpenSessionInRequestInterceptor"></bean>
I can absolutely not figure out, why AOP does not work when deployed on the tomcat. I hope you have some ideas.
Solution I found the solution. I places my aop configuration in the applicationContext.xml, but this will not work. I placed the configuration in the application-servlet.xml and now everything is fine. Has someone an idea why?
I admit I didn't have to make it work using a marker annotation, but I needed the annotation as argument, so this worked:
#Around("#annotation(foo)")
public Object invoke(ProceedingJoinPoint invocation, Foo foo) throws Throwable
But.. note that #Transactional also starts a session if one isn't started, so perhaps you don't really need that.
Update: if your beans are defined in the child context, then the aop configuration of the parent context does not affect them. The parent context does not see the child context, and your x-servlet.xml is a child context.
To answer why you have to put configuration in the servlet XML to get to work:
I assume you are using <context:component-scan ...> tag and this is placed in the servlet XML. That is the reason why you need to have them both in servlet XML, otherwise they don't "see" each other. As a result, the connection is not properly established.

Java Jersey RESTful services

Rather new to REST and Jersey, and I'm trying out some basic examples. I've got one particular question though, which I haven't really found an answer for yet (don't really know how to look for this): how would you go about storing/defining common services so that they are stateful and accessible to all/some resources?
For instance, a logger instance (Log4J or whatever). Do I have to manually initialize this and store it in the HttpSession? Is there a "best practice" way of doing this so that my logger is accessible to all/some resources?
Your best option is to inject the services into the Resource using something like Spring. Adding a setter in your Resource for the particular service makes it available for various scopes. The following tutorial explains in detail how to use Spring and Jersey. Below are examples of the various pieces needed to make this work. Specifically answering your question however the "best practice" is to allow Spring to manage the "stateful"ness or "scope" of a particular service.
Simple Service Bean
public class SimpleBean {
private int counter;
public String sayHello() {
return Integer.toString(counter++);
}
}
Simple JAX-RS Resource
public class HelloWorldResource {
private SimpleBean simpleBean;
public void setSimpleBean(SimpleBean simpleBean) {
this.simpleBean = simpleBean;
}
#GET #Path("/Hello") #Produces(MediaType.APPLICATION_JSON)
public String sayHello() {
return "{\"Hello\": \"" + this.simpleBean.sayHello() + "\"}";
}
}
Simple Spring applicationContext.xml, notice the "scope" for the Bean and Resource. This defines how stateful these objects are.
<bean id="simpleBean" scope="prototype" class="myhealth.spring.SimpleBean"/>
<bean id="helloWorldResource" scope="singleton" class="myhealth.ajax.HelloWorldResource">
<property name="simpleBean">
<ref bean="simpleBean"/>
</property>
</bean>
Speaking strictly about the Logger, are you sure you want it to be stateful? Most projects I've been on just grab one in whatever class needs it
Logger log = Logger.getLogger(this.getClass().getName());
Cut and paste that everywhere and let the logging magic begin!
To your original question regarding keeping state: Are you looking to keep something stateful per user (like a session) or some other state across the application (like a runtime configured logger)? Sessions can be managed with the HttpRequest and some ServletFilters. Grab the request in your JErsey code like so
#Path("/awesome")
public class AwesomeService {
#GET
#Produces("text/awesome")
public String getAwesome(#Context HttpServletRequest req) {
HttpSession session= req.getSession(true);
//brilliance
}
}
Application resources, like an entity manager or a konfabulator can be initialized statically and used as Singletons or with Factories.
class EntityManager {
EntityManager manager;
public static EntityManager getManager() {
if(manager == null) {
manager = initManager();
}
return manager;
}
}

Spring- How to use Spring Dependency Injection to write a Standalone Java Application

I want to write a standalone application with IOC, how do I use springs dependency injection in there? I'm using JIdea. There is spring 2.5 support but I want to use spring 3.0 here is the way I tried!
I experience in using Spring MVC we can inject dependencies there in a WebApplicationContext but how do I inject dependencies in a standalone application
I tried this
ApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"com\\ttg\\xmlfile.xml"});
but I cannot see that the dependencies are injected by the beans defined there (in the XML file)
I put the above code in the main method and two bean definitions for two Objects,in one Java class's constructor I used the other class's object - which was injected to this object - and called a method on that which will print some thing but it didn't worked I thought that the above code creates all the dependencies and injects them but it doesn't seem like that
How do I properly use Springs IOC, dependency injection in my stand alone app which does not contain a WebApplicationContext?
Please mention steps.
suppose you have:
class Bean1 {
Bean2 bean2;
}
class Bean2 {
String data;
}
the context.xml file
<bean id="bean1" class="Bean1">
<property name="bean2" ref="bean2" />
</bean>
<bean id="bean2" class="Bean2" />
then this should be true
ApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"context.xml"});
Bean1 bean1 = (Bean1) context.getBean("bean1");
// bean1.bean2 should not be null here.
you can use autowiring support provided by spring, in order to inject dependencies (and possibly apply post processors) to an object that is not part of the application context.
In your case, this object is your standalone application.
Here is the way to achieve this. In this example, I use #Autowired (for b1), traditional DI (for b2) and initialization hook for b3. The autowiring support with annotations assumes you have defined the appropriate spring post-processor in your application context (e.g. by declaring <context:annotation-config/>).
public class StandaloneApp implements InitializingBean {
#Autowired private Bean1 b1;
private Bean2 b2;
private Bean3 b3;
public void afterPropertiesSet() throws Exception {
this.b3 = new Bean3(b1, b2);
}
public void setB2(Bean2 b2) {
this.b2 = b2;
}
public static void main(String... args) {
String[] locations = // your files relative to the classpath
ApplicationContext ac = new ClasspathXmlApplicationContext(locations);
// or use FileSystemXmlApplicationContext if the files are not in the classpath
StandaloneApp app = new StandaloneApp();
AutowireCapableBeanFactory acbf = ac.getAutowireCapableBeanFactory();
acbf.autowireBeanProperties(app, AUTOWIRE_BY_NAME, false);
acbf.initializeBean(app, "standaloneApp"); // any name will work
}
}
In this example, all b1, b2 and b3 should be non-null (assuming b1 and b2 beans exist in your application context).
I haven't tested it (might not even compile due to some typo), but the idea is in the last 3 lines. See the javadocs for AutowireCapableBeanFactory and mentionned methods to see exactly what happens.
If you prefer (mostly) pure java, you can use java config:
#Configuration
pubic class MyConfig {
#Bean
public Bean1 bean1() { return new Bean1(); }
#Bean
public Bean2 bean2() { return new Bean2(bean1()); }
}
And then to instantiate:
ApplicationContext ctx =
new AnnotationConfigApplicationContext(MyConfig.class);
Keep in mind there's some things that pure annotation driven configuration doesn't yet support (such as things like tx:annotation-driven), which you might need some xml glue code for.
<beans>
<tx:annotation-driven />
<context:annotation-config/>
<bean class="MyConfig"/>
</beans>
And then use a standard xml based way of creating the ApplicationContext (like ClasspathXmlApplicationContext, or the spring web context loader, etc...).
How did you confirm that your beans aren't being wired correctly? One common issue is the xml config file not being in the right place. Can you give us some more information like your project layout and the code you use to obtain the beans from the container?
If you add log4j logging to your app, you should see a cascade of messages coming out that will tell you a lot about what Spring is and is not doing. If you don't have that feedback, you're in the dark. You might be able to ferret out the answer just by getting more information out of Spring from log4j.
Are you calling context.getBean("beanName") to get a reference to the bean or are you doing a new SomeClass()? If you do it through getBean() then the injected properties should be set.
Also, be sure to use the same bean factory (ClassPathXmlApplicationContext instance) for retrieving all your beans. This should most likely be static final and used by the entire application.
you can use the command #Autowired

Getting Spring Application Context

Is there a way to statically/globally request a copy of the ApplicationContext in a Spring application?
Assuming the main class starts up and initializes the application context, does it need to pass that down through the call stack to any classes that need it, or is there a way for a class to ask for the previously created context? (Which I assume has to be a singleton?)
If the object that needs access to the container is a bean in the container, just implement the BeanFactoryAware or ApplicationContextAware interfaces.
If an object outside the container needs access to the container, I've used a standard GoF singleton pattern for the spring container. That way, you only have one singleton in your application, the rest are all singleton beans in the container.
You can implement ApplicationContextAware or just use #Autowired:
public class SpringBean {
#Autowired
private ApplicationContext appContext;
}
SpringBean will have ApplicationContext injected, within which this bean is instantiated. For example if you have web application with a pretty standard contexts hierarchy:
main application context <- (child) MVC context
and SpringBean is declared within main context, it will have main context injected;
otherwise, if it's declared within MVC context, it will have MVC context injected.
Here's a nice way (not mine, the original reference is here:
http://sujitpal.blogspot.com/2007/03/accessing-spring-beans-from-legacy-code.html
I've used this approach and it works fine. Basically it's a simple bean that holds a (static) reference to the application context. By referencing it in the spring config it's initialized.
Take a look at the original ref, it's very clear.
I believe you could use SingletonBeanFactoryLocator. The beanRefFactory.xml file would hold the actual applicationContext, It would go something like this:
<bean id="mainContext" class="org.springframework.context.support.ClassPathXmlApplicationContext">
<constructor-arg>
<list>
<value>../applicationContext.xml</value>
</list>
</constructor-arg>
</bean>
And the code to get a bean from the applicationcontext from whereever would be something like this:
BeanFactoryLocator bfl = SingletonBeanFactoryLocator.getInstance();
BeanFactoryReference bf = bfl.useBeanFactory("mainContext");
SomeService someService = (SomeService) bf.getFactory().getBean("someService");
The Spring team discourage the use of this class and yadayada, but it has suited me well where I have used it.
Before you implement any of the other suggestions, ask yourself these questions...
Why am I trying to get the ApplicationContext?
Am I effectively using the ApplicationContext as a service locator?
Can I avoid accessing the ApplicationContext at all?
The answers to these questions are easier in certain types of applications (Web apps, for example) than they are in others, but are worth asking anyway.
Accessing the ApplicationContext does kind of violate the whole dependency injection principle, but sometimes you've not got much choice.
SpringApplicationContext.java
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
/**
* Wrapper to always return a reference to the Spring Application
Context from
* within non-Spring enabled beans. Unlike Spring MVC's
WebApplicationContextUtils
* we do not need a reference to the Servlet context for this. All we need is
* for this bean to be initialized during application startup.
*/
public class SpringApplicationContext implements
ApplicationContextAware {
private static ApplicationContext CONTEXT;
/**
* This method is called from within the ApplicationContext once it is
* done starting up, it will stick a reference to itself into this bean.
* #param context a reference to the ApplicationContext.
*/
public void setApplicationContext(ApplicationContext context) throws BeansException {
CONTEXT = context;
}
/**
* This is about the same as context.getBean("beanName"), except it has its
* own static handle to the Spring context, so calling this method statically
* will give access to the beans by name in the Spring application context.
* As in the context.getBean("beanName") call, the caller must cast to the
* appropriate target class. If the bean does not exist, then a Runtime error
* will be thrown.
* #param beanName the name of the bean to get.
* #return an Object reference to the named bean.
*/
public static Object getBean(String beanName) {
return CONTEXT.getBean(beanName);
}
}
Source: http://sujitpal.blogspot.de/2007/03/accessing-spring-beans-from-legacy-code.html
If you use a web-app there is also another way to access the application context without using singletons by using a servletfilter and a ThreadLocal. In the filter you can access the application context using WebApplicationContextUtils and store either the application context or the needed beans in the TheadLocal.
Caution: if you forget to unset the ThreadLocal you will get nasty problems when trying to undeploy the application! Thus, you should set it and immediately start a try that unsets the ThreadLocal in the finally-part.
Of course, this still uses a singleton: the ThreadLocal. But the actual beans do not need to be anymore. The can even be request-scoped, and this solution also works if you have multiple WARs in an Application with the libaries in the EAR. Still, you might consider this use of ThreadLocal as bad as the use of plain singletons. ;-)
Perhaps Spring already provides a similar solution? I did not find one, but I don't know for sure.
Take a look at ContextSingletonBeanFactoryLocator. It provides static accessors to get hold of Spring's contexts, assuming they have been registered in certain ways.
It's not pretty, and more complex than perhaps you'd like, but it works.
There are many way to get application context in Spring application. Those are given bellow:
Via ApplicationContextAware:
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public class AppContextProvider implements ApplicationContextAware {
private ApplicationContext applicationContext;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
Here setApplicationContext(ApplicationContext applicationContext) method you will get the applicationContext
ApplicationContextAware:
Interface to be implemented by any object that wishes to be notified
of the ApplicationContext that it runs in. Implementing this interface
makes sense for example when an object requires access to a set of
collaborating beans.
Via Autowired:
#Autowired
private ApplicationContext applicationContext;
Here #Autowired keyword will provide the applicationContext. Autowired has some problem. It will create problem during unit-testing.
Note that by storing any state from the current ApplicationContext, or the ApplicationContext itself in a static variable - for example using the singleton pattern - you will make your tests unstable and unpredictable if you're using Spring-test. This is because Spring-test caches and reuses application contexts in the same JVM. For example:
Test A run and it is annotated with #ContextConfiguration({"classpath:foo.xml"}).
Test B run and it is annotated with #ContextConfiguration({"classpath:foo.xml", "classpath:bar.xml})
Test C run and it is annotated with #ContextConfiguration({"classpath:foo.xml"})
When Test A runs, an ApplicationContext is created, and any beans implemeting ApplicationContextAware or autowiring ApplicationContext might write to the static variable.
When Test B runs the same thing happens, and the static variable now points to Test B's ApplicationContext
When Test C runs, no beans are created as the TestContext (and herein the ApplicationContext) from Test A is resused. Now you got a static variable pointing to another ApplicationContext than the one currently holding the beans for your test.
Not sure how useful this will be, but you can also get the context when you initialize the app. This is the soonest you can get the context, even before an #Autowire.
#SpringBootApplication
public class Application extends SpringBootServletInitializer {
private static ApplicationContext context;
// I believe this only runs during an embedded Tomcat with `mvn spring-boot:run`.
// I don't believe it runs when deploying to Tomcat on AWS.
public static void main(String[] args) {
context = SpringApplication.run(Application.class, args);
DataSource dataSource = context.getBean(javax.sql.DataSource.class);
Logger.getLogger("Application").info("DATASOURCE = " + dataSource);
I use a simple, standardized way to allow external access to any of my own singleton Spring Beans. With this method, I continue to let Spring instantiate the Bean. Here's what I do:
Define a private static variable of the same type as the enclosing class.
Set that variable to this in each of the class's constructors. If the class has no constructors, add a default constructor in which to set the variable.
Define a public static getter method that returns the singleton variable.
Here's an example:
#Component
public class MyBean {
...
private static MyBean singleton = null;
public MyBean() {
...
singleton = this;
}
...
public void someMethod() {
...
}
...
public static MyBean get() {
return singleton;
}
}
I can then call someMethod on the singleton bean, anywhere in my code, via:
MyBean.get().someMethod();
If you are already subclassing your ApplicationContext, you can add this mechanism to it directly. Otherwise, you could either subclass it just to do this, or add this mechanism to any bean that has access to the ApplicationContext, and then use it to gain access to the ApplicationContext from anywhere. The important thing is that it is this mechanism that will let you get into the Spring environment.
Approach 1: You can inject ApplicationContext by implementing ApplicationContextAware interface. Reference link.
#Component
public class ApplicationContextProvider implements ApplicationContextAware {
private ApplicationContext applicationContext;
public ApplicationContext getApplicationContext() {
return applicationContext;
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
Approach 2: Autowire Application context in any of spring managed beans.
#Component
public class SpringBean {
#Autowired
private ApplicationContext appContext;
}
Reference link.
Do autowire in Spring bean as below:
#Autowired
private ApplicationContext appContext;
You will get the ApplicationContext object.
Please note that; the below code will create new application context instead of using the already loaded one.
private static final ApplicationContext context =
new ClassPathXmlApplicationContext("beans.xml");
Also note that beans.xml should be part of src/main/resources means in war it is part of WEB_INF/classes, where as the real application will be loaded through applicationContext.xml mentioned at Web.xml.
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>META-INF/spring/applicationContext.xml</param-value>
</context-param>
It is difficult to mention applicationContext.xml path in ClassPathXmlApplicationContext constructor. ClassPathXmlApplicationContext("META-INF/spring/applicationContext.xml") wont be able to locate the file.
So it is better to use existing applicationContext by using annotations.
#Component
public class OperatorRequestHandlerFactory {
public static ApplicationContext context;
#Autowired
public void setApplicationContext(ApplicationContext applicationContext) {
context = applicationContext;
}
}
I know this question is answered, but I would like to share the Kotlin code I did to retrieve the Spring Context.
I am not a specialist, so I am open to critics, reviews and advices:
https://gist.github.com/edpichler/9e22309a86b97dbd4cb1ffe011aa69dd
package com.company.web.spring
import com.company.jpa.spring.MyBusinessAppConfig
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.ApplicationContext
import org.springframework.context.annotation.AnnotationConfigApplicationContext
import org.springframework.context.annotation.ComponentScan
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Import
import org.springframework.stereotype.Component
import org.springframework.web.context.ContextLoader
import org.springframework.web.context.WebApplicationContext
import org.springframework.web.context.support.WebApplicationContextUtils
import javax.servlet.http.HttpServlet
#Configuration
#Import(value = [MyBusinessAppConfig::class])
#ComponentScan(basePackageClasses = [SpringUtils::class])
open class WebAppConfig {
}
/**
*
* Singleton object to create (only if necessary), return and reuse a Spring Application Context.
*
* When you instantiates a class by yourself, spring context does not autowire its properties, but you can wire by yourself.
* This class helps to find a context or create a new one, so you can wire properties inside objects that are not
* created by Spring (e.g.: Servlets, usually created by the web server).
*
* Sometimes a SpringContext is created inside jUnit tests, or in the application server, or just manually. Independent
* where it was created, I recommend you to configure your spring configuration to scan this SpringUtils package, so the 'springAppContext'
* property will be used and autowired at the SpringUtils object the start of your spring context, and you will have just one instance of spring context public available.
*
*Ps: Even if your spring configuration doesn't include the SpringUtils #Component, it will works tto, but it will create a second Spring Context o your application.
*/
#Component
object SpringUtils {
var springAppContext: ApplicationContext? = null
#Autowired
set(value) {
field = value
}
/**
* Tries to find and reuse the Application Spring Context. If none found, creates one and save for reuse.
* #return returns a Spring Context.
*/
fun ctx(): ApplicationContext {
if (springAppContext!= null) {
println("achou")
return springAppContext as ApplicationContext;
}
//springcontext not autowired. Trying to find on the thread...
val webContext = ContextLoader.getCurrentWebApplicationContext()
if (webContext != null) {
springAppContext = webContext;
println("achou no servidor")
return springAppContext as WebApplicationContext;
}
println("nao achou, vai criar")
//None spring context found. Start creating a new one...
val applicationContext = AnnotationConfigApplicationContext ( WebAppConfig::class.java )
//saving the context for reusing next time
springAppContext = applicationContext
return applicationContext
}
/**
* #return a Spring context of the WebApplication.
* #param createNewWhenNotFound when true, creates a new Spring Context to return, when no one found in the ServletContext.
* #param httpServlet the #WebServlet.
*/
fun ctx(httpServlet: HttpServlet, createNewWhenNotFound: Boolean): ApplicationContext {
try {
val webContext = WebApplicationContextUtils.findWebApplicationContext(httpServlet.servletContext)
if (webContext != null) {
return webContext
}
if (createNewWhenNotFound) {
//creates a new one
return ctx()
} else {
throw NullPointerException("Cannot found a Spring Application Context.");
}
}catch (er: IllegalStateException){
if (createNewWhenNotFound) {
//creates a new one
return ctx()
}
throw er;
}
}
}
Now, a spring context is publicly available, being able to call the same method independent of the context (junit tests, beans, manually instantiated classes) like on this Java Servlet:
#WebServlet(name = "MyWebHook", value = "/WebHook")
public class MyWebServlet extends HttpServlet {
private MyBean byBean
= SpringUtils.INSTANCE.ctx(this, true).getBean(MyBean.class);
public MyWebServlet() {
}
}
Even after adding #Autowire if your class is not a RestController or Configuration Class, the applicationContext object was coming as null. Tried Creating new class with below and it is working fine:
#Component
public class SpringContext implements ApplicationContextAware{
private static ApplicationContext applicationContext;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws
BeansException {
this.applicationContext=applicationContext;
}
}
you can then implement a getter method in the same class as per your need like getting the Implemented class reference by:
applicationContext.getBean(String serviceName,Interface.Class)

Categories