This is an old project using Spring 3, with XML-based beans definitions, and I am new to the company/project.
I am just looking for pointers, not necessarily expecting you to find the root problem for me.
Basically, Spring is creating multiple instances of the same class/bean. From the Spring definition of scope=singleton, it says that they mean a singleton per IoC container, not a singleton in the classic GoF definition https://docs.spring.io/spring-framework/docs/3.0.0.RC1/reference/html/ch03s05.html
As a workaround, my colleague would explicitly add an init-method to each bean definition (that really needed to be a singleton, such as a class containing a cache)
<bean id="myServiceBean" class="com.supercompany.MyCache" init-method="init"/>
And in the init method, a singleton pattern is implemented, so effectively the same instance is re-used.
There's only one contextConfiguration binded to applicationContext.xml, and I tried looking searching for the project "classLoader" and "ApplicationContext" hoping to find multiple implementations, but to no avail.
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
Any other pointers where to look around?
Edit :
My example of MyCache was a bit misleading. After some more investigation, "normal" classes are correctly being instancied as singletons. It's my Service class that is not being created correctly. I am using javax.ws.rs annotation
#Path("/share")
public class ShareService {
...
}
Edit 2: After some more investigation, it's a Javax-ws-rs/JAX-RS "issue" where by design it is a new instance per request. However, even adding javax-ws-rs's Singleton still doesn't work.
Related
I'm trying to get a better understanding of the #Autowired annotations component scanning, but all the examples I found so far use context.getBean(..) at some point to get at least one Bean to start with.
I also read that doing that is considered bad practice , but I can't seem to find any information on how to do it without context.getBean(..)
Could somebody please enlighten me with an example and information on how to do this ?
Define your bean in xml and use
<context:component-scan base-package="com" />
<mvc:annotation-driven />
Bean def
<bean id="processEngine" class="com.processEngine">
<property name="processEngineConfiguration" ref="processEngineConfiguration" />
</bean>
now you can get bean as following
#Autowired
private ProcessEngine processEngine;
how it works
spring scans the bean's recipes either from xml or java configuration. then spring creates a beanDefinitions which are 'loaded' into BeanFactory. BeanFactory triggers a set of BeanPostProcessors (BPP) which are scanning classes for particular annotations like Autowired/Resource/PostProcessor and etc. and do appropriate actions. in case when your class contains #Autowired annotation, AutowiredAnnotationBeanPostProcessor would auto wire required field (dependencies), and when creation of an object is done and all BPP worked out, object is ready to be used by the app, from this point your code can get 'ready' objects from container.
there are some cases when you would need to access this beans from the code which is out of spring's control and not managed by container. in order to do so, you would need to get the ApplicationContext (container) and call #getBean specifying either name or type. using applicationContext directly is not a good practice because there are some problems that you can come to, f.ex. id of a bean might be changed and if you refer to bean by id then NPE would be thrown.
configuration
there are several approaches to configure spring to scan the classes for finding bean recipes. one would be defining component-scan, in this case classes which are located in the path that you've set and having any of valid spring annotations like #Component, #Service, #Repository, #Controller (for web container) would be considered. another way would be specifying each bean separately using <bean> or #Bean.
examples.
if you want to create a web app then you should see DispatcherServlet with ContextLoaderListener classes. this classes would boot your app and load everything according to configuration. f.ex. here,
but if you want to create a desktop app, then you would end up with something like this
From time to time (usually when not using Spring Boot), I use something along the lines of the following code:
public static <T> T autowire(ApplicationContext ctx, T bean) {
ctx.getAutowireCapableBeanFactory().autowireBean(bean);
return bean;
}
In my main, I create an instance of the main application class that contains a few #Autowired annotations for the main services / entry points to my Spring application.
I'm writing a service registry class. This service registry will scan packages for annotated classes and then populate some internal map. What I need then, is to be able to query for services (by String name) using some method (let's say Object get(String name)). This method will then search internal map for a service with such name and returns instance.
What I'm doing right now, is having this ServiceRegistryBean implement ApplicationContextAware and BeanDefinitionRegistryPostProcessor and a list of Strings (package names) given on construct.
Then, as soon as the bean is constructed, registry post processor kicks in (see note) and the registry class adds the service classes as new beans (singleton, lazy loaded) to the spring bean registry. Then, getting the service instance is as simple as requesting a bean from context, returning singleton instance.
My question is: is there a better way in Spring to do this? I've looked into bean factories, but it does not seem to me the same. Support for auto-wiring and DI in service instances is essential, that's why I want Spring to instantiate it. Also, I like the idea of Spring taking care of singletons.
Note: I've found out, that when I inline the bean creation in <constructor-arg> (that is, bean is not named and is just an instance passed as constructor argument of other bean - in my case, I'm passing registry as a parameter to other constructor), BeanDefinitionRegistryPostProcessor interface methods (namely public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)) is not called at all! I'm quite sure, it is some behavior of Spring I don't know about, but I was not able to find proper documentation for the post-processor.
Thank you for any hints or insights!
Scanning for custom annotations it's already supported, you only need to add a include-filter to <context:component-scan> tag, ie
<context:component-scan base-package="org.example">
<context:include-filter type="annotation" expression="some.Annotation"/>
</context:component-scan>
see http://static.springsource.org/spring/docs/current/spring-framework-reference/html/beans.html#beans-scanning-filters
If you turn on default-lazy-init I suppose that the DI Container is ready to use as Service Locator Registry.
About the note, only root bean definitions are taken into account when looking for BeanFactoryPostProcessors, inner beans are ignored.
Usually, scanning and registering beans is done by BeanDefinitionParsers instead because you known when the beans are registered and beans are visible for tools, like STS Spring Bean Explorer, but using a BeanDefinitionRegistryPostProcessor is correct. The interface ensures that beans are defined before other BeanFactoryPostProcessors run.
I have a Spring web application with two contexts: one (applicationContext) built by ContextLoaderListener and a second (webContext) built by DispatcherServlet.
Within the applicationContext is a bean (org.springframework.security.authentication.DefaultAuthenticationEventPublisher) that fires spring context events.
But the receiver for the event is defined in the webContext. And that receiver did not get the event. (If put the receiver for test purpose in the applicationContext then it get the event, but I can not do this, because I need the webContexts for its functionality.)
So my question is, how to bridges the events from the applicationContext to webContext?
I had the same problem, solved mine by moving the beans creating the event to web-context. However you can solve your problem by manually wiring your event listener, something like this (this code is not compiled therefore it is untested):
#Component
public class BeanInWebContext implements ApplicationListener<SomeEvent> {
#Autowired
private ApplicationContext webContext;
#PostConstruct
public void registerAsListener() {
// get parent context
AbstractApplicationContext appContext = (AbstractApplicationContext) webContext.getParent();
// register self as a listener, this method is in AbstractApplicationContext
appContext.addApplicationListener(this);
}
#Override
public void onApplicationEvent(SomeEvent event) {
}
}
I think the actual answer is that you may want to configure your app differently (so that you only have one context)
I think in your web.xml you need to do something like this :
<servlet>
<servlet-name>example</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:/META-INF/applicationSpringConfig.xml
</param-value>
</init-param>
</servlet>
But to answer the deeper question. Someone else points out that you can use includes in your spring file (indeed in the above you can have more than one springconfig specified in your dispatcher servlet).
But when you include other context files you do not share instances of beans, only definitions.
Modularising Spring applications has been the only real downside of spring in comparison with EJB etc. That led spring into using OSGi.
And the answer to your underlying question of how to share spring context, officially you share spring bean instances between contexts using OSGi (spring dm)
Try moving the event publisher to the web context file, where it should have visibility over the whole application context. A similar issue occurs when configuring method security in the parent application context. The parent application context (loaded by ContextLoaderListener) isn't aware of the child (web) context.
You can also use a single application context for the entire application if you don't really need the parent-child relationship between the two. Often it just gets in the way and it is easier if all beans were defined in the same space.
As stated in documentation for the spring framework the simple ApplicationEvent mechanism is only designed to be used within the same application context, I am not aware that it is possible to propagate events to child contexts.
If you need a more advanced solution you might look into using a more enhanced solution like Java Message Service or Spring Integration.
http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#context-functionality-events
We can use the import tag to import/bridge the 2 different contexts created in a way the visibility of the events/beans are available and shared.
<import resource="applicationContext_name.xml"/>
In this import the context xml which is configured to be created from ContextLoaderListener in the context xml of the DispatcherServlet.
I have a bean defined in my spring web application and I am expecting to have only one instance of this bean, here is my bean definition:
<bean id="accessControl" class="my.spring.app.AccessControl" />
In the constructor of AccessControl, I assign an identifier to the object, something like this:
public class AccessControl {
private long id = 0;
public AccessControl() {
id = System.currentTimeMillis();
}
public long getAccessControlId() {
return id;
}
}
In a different class, I try to get hold of the instance of AccessControl, something like this:
ApplicationContext ctx =
new ClassPathXmlApplicationContext("acbean.xml");
AccessControl ac = (AccessControl) ctx.getBean("accessControl");
LOGGER_.info("AccessControl Identifier : " + ac.getAccessControlId());
I am expecting the "id" value to be the same because the value of "id" is set in the constructor and constructor should not get called again and again but that is exactly what is happening. Infact, I added a log statement to the constructor and a new object is created everytime.
I have read: http://www.digizenstudio.com/blog/2006/09/14/a-spring-singleton-is-not-a-singleton/
but I don't think I am dealing with the same class defined twice with two different bean identifiers and the application context is the same.
Can anyone share what is wrong with the way I have defined the bean?
I have also experimented with singleton="true" and scope="singleton" but they do not make any differece.
Thanks.
the singelton-ness in spring is per application context, every time you create a new instance of the application context (like the first line in your second code sample) all the singletons are instantiated.
You need to have a single application context and reuse it around in your application
You are creating a new application context with each call of:
ApplicationContext ctx = new ClassPathXmlApplicationContext("acbean.xml");
So, you end up with a new spring container, which means that your beans are all re-created by the new container.
Also, you mentioned that you had this in a web application. If so, you need to allow the web application to load the spring context and to obtain and use that context as necessary.
Add to web.xml:
<context-param>
<description>Core Spring context.</description>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/classes/applicationContext.xml</param-value>
</context-param>
<listener>
<description>Spring loader.</description>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
or something similar. Obtain the web context via a servletcontext as needed.
One other note: one point of spring is to provide inversion of control, commonly with dependency injection. You should consider allowing spring to inject any dependencies for you rather than obtaining the context and pulling beans yourself.
In a Spring app you should not be explicitly creating an application context of your own.
Ideally the singleton should be injected into your class, or you should be implementing ApplicationContextAware (docs, and some notes). I prefer injection; easier.
How can I inject dependencies into a deserialized bean?
Some of my Spring beans should be serialized during the render-response phase of our JSF application, and then deserialized at the beginning of the next request. Some of those beans have dependencies which are scoped to the request. If I configure the dependencies with the scoped proxy ("<aop:scoped-proxy>"), I can't serialize my dependent beans - the proxy isn't serializable.
So right now we do it by declaring the appropriate member variables of the serialized bean classes as transient, and then calling context.getAutowireCapableBeanFactory().configureBean(bean, name) just after deserializing the beans - but this sucks, because the bean's initializer is called again. (As for other dependencies that are in the same scope, are not transient, and are deserialized, I'm not even sure why they don't get overwritten by configureBean, but I don't think they are.)
What's better? Should I just get the bean definition, loop through it, find the dependencies that are scoped to the request, and then call getBean(name) on the context?
(BTW, I'm not sure it makes a difference, but we are using Spring kind of weirdly. We instantiate a new ClassPathXmlApplicationContext for each non-posted-back HTTP request, rather than a single WebApplicationContext. Upon postback, we deserialize the beans. So when I say "scoped to the request", I'm lying; these beans are actually singleton-scoped. I'd like to use the WebApplicationContext and the more sane scoping with it, but as far as I can tell, that's orthogonal to our problem at the moment.)
It makes all the difference - I've been using spring with JSF for quite a long time and hadn't had any serialization problems. The way to go is simply define the following in your faces-config.xml:
<el-resolver>
org.springframework.web.jsf.el.SpringBeanFacesELResolver
</el-resolver>
This integrates spring with JSF by providing spring beans (using the request and session spring scopes) to JSF pages.
So, I'd advice for changing your approach drastically so that you aren't bug with such problems in the future.