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.
Related
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.
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 two Spring contexts declared in my application - one for Spring-MVC requests, and another for Flex/BlazeDS messagebroker requests, mapped to different url-patterns:
<servlet-mapping>
<servlet-name>spring-mvc</servlet-name>
<url-pattern>/app/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>flex</servlet-name>
<url-pattern>/messagebroker/*</url-pattern>
</servlet-mapping>
There's a common context configuration declared, (/WEB-INF/applicationContext.xml) and then each of the two contexts have their own configurations declared in spring-mvc-servlet.xml and flex-servlet.xml respectively.
Inside flex-servlet.xml I have beans declared which are specific to the flex context. However, when a call comes in to http://localhost/messagebroker/* I'm getting errors that those beans aren't available.
The code in question is inside a custom Spring component, so directly references the WebApplicationContext in order to access the declared beans:
public ISerializer getSerializer(Object source,boolean useAggressiveSerialization)
{
ServletContext ctx = FlexContext.getServletContext();
WebApplicationContext springContext = WebApplicationContextUtils.getRequiredWebApplicationContext(ctx);
String serializerBeanName = springContext.getBeanNamesForType(ISerializer.class);
}
This approach works when I'm running with a single context. However it needs to also support where there are multiple contexts running.
Setting a breakpoint, I see that the value of springContext is the root context, with a single configLocation - /WEB-INF/applicationContext.xml
I'm asssuming that this is the problem - as the ISerializer that the above code requires is declared in flex-servlet.xml.
How do I modify the above code to support both scenarios? (Single context, and multiple contexts)?
EDIT:
The code shown above sits inside a ManageableComponentFactoryBean, which appears to operate as a custom bean factory. It seems that the ApplicationContextAware interface is not honoured on generated classes. Eg:
<bean id="dpHibernateRemotingAdapterComponentFactory"
class="org.springframework.flex.core.ManageableComponentFactoryBean">
<constructor-arg
value="org.dphibernate.adapters.RemotingAdapter" />
<property name="properties">
<value>
{"dpHibernate" :
{
"serializerFactory" : "org.dphibernate.serialization.SpringContextSerializerFactory"
}
}
</value>
</property>
</bean>
The code quoted above sits inside the org.dphibernate.serialization.SpringContextSerializerFactory. Making this SpringContextSerializerFactory implement ApplicationContextAware has no impact.
If flex is a DispatcherServlet, and for some reason you can't follow Tomás Narros's suggestion, you can obtain a context associated with the current DispatcherServlet using RequestContextUtils.getWebApplicationContext(request).
There is also a convenience method RequestContextUtils.getWebApplicationContext(request, ctx), which returns the root context if DispatcherServlet's one is not available.
Declare your custom componente as Spring Context aware:
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public MyCustomBean implements ApplicationContextAware {
private ApplicationContext springContext;
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
springContext = applicationContext;
}
public ISerializer getSerializer(Object source,boolean useAggressiveSerialization)
{
String serializerBeanName = springContext.getBeanNamesForType(ISerializer.class);
}
}
At the bean initialization, Spring will access the setApplicationContext method of your bean, passing as an argument the context in wich it's being created. There, you can keep it an use it whenever you need.
Hrmmmm.....I have nearly that exact sort of declaration in my Spring/Flex app, using Spring/Flex integration and there is only one application context. Could that be the problem? You have beans declared in the Flex context file that aren't in the MVC context file, and they aren't really getting loaded?
As I understand it, Spring MVC application has two distinct contexts, the application context and the web context, which are controlled by applicationContext.xml and dispatcher-servlet.xml, respectively.
Inside my controllers, how do I go about loading a bean into either of these contexts?
Note that I am aware of Getting Spring Application Context. That would answer my question for a stand alone application. Where I would use a factory to load the application context from the xml file, but this seems like the wrong way to go about loading beans in Spring MVC.
Matt is absolutely correct. You should not need with any kind of bean-loading/instantiating code in your MVC application, otherwise you're doing something wrong. You define your beans inside the according spring XML configuration files.
<bean id="pinboardServiceTarget" class="com.lifepin.services.PinboardService">
<property name="pinboardEntryDao" ref="pinboardEntryDAO"/>
</bean>
...
<bean id="pinboardEntryDAO" class="com.lifepin.daos.PinboardEntryDAO">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
Your PinboardService class (this is just from an application I wrote recently) will have a property IPinboardEntryDAO like
public class PinboardService implements IPinboardService{
private IPinboardEntryDAO pinboardEntryDao;
...
public void setPinboardEntryDAO(IPinboardEntryDAO dao){
this.pinboardEntryDao = dao;
}
public IPinboardEntryDAO getPinboardEntryDAO(){
...
}
...
}
public class PinboardEntryDAO implements IPinboardEntryDAO{
...
}
Note that inside the the PinboardService class I'm using the DAO interface, not the implementation itself, while in the configuration I'm then injecting the real implementation PinboardEntryDAO. This is a very good practice for separating the different layers (presentation, service and data layer).
Although a Spring MVC application has two distinct contexts, the web context has access to all the beans loaded in the application context. The application context however cannot access beans in the web context. This is used to enforce separation of concerns, e.g. business rules class does not need to know about the HTTP session. So if you have a bean you need access to from both contexts it will have to be declared within the application context.
Any dependencies that your Controller has (such as on service-layer classes, DAOs, etc) should be expressed as normal - through injection, either constructor injection or setter injection.
The context where the controller is mapped just wires it up with any dependencies it needs as normal. The Controller code never needs to work with Spring directly to get any beans, it is wired up with them.
You should use dependency injection and your config files to load beans into your controllers, but if you do need to access the application context directly, any Controller that extends AbstractController (or any of its descendents) has access to the getApplicationContext() method.
In stand alone application we can user context.Refresh() it will reloading/re-instantiating the new requested beans the old beans will have the old instance only.
In web applications we need to overwrite the ContextLoaderListener and call the contextInitialized()
You need to import the file containing the bean definitions of the service layer(say, service-context.xml) into the new project. It can be done as:
<import resource="classpath:service-context.xml"/>
I'm an end-user of one of my company's products. It is not very suitable for integration into Spring, however I am able to get a handle on the context and retrieve the required bean by name. However, I would still like to know if it was possible to inject a bean into this class, even though the class is not managed by Spring itself.
Clarification: The same application which is managing the lifecycle of some class MyClass, is also managing the lifecycle of the Spring context. Spring does not have any knowledge of the instance of MyClass, and I would like to some how provide the instance to the context, but cannot create the instance in the context itself.
You can do this:
ApplicationContext ctx = ...
YourClass someBeanNotCreatedBySpring = ...
ctx.getAutowireCapableBeanFactory().autowireBeanProperties(
someBeanNotCreatedBySpring,
AutowireCapableBeanFactory.AUTOWIRE_AUTODETECT, true);
You can use #Autowired and so on within YourClass to specify fields to be injected etc.
One way to bring a bean into Spring despite its manufacture being external is to use a helper class marked as a #Configuration bean that has a method (marked with #Bean) that actually makes the instance and hands it back through Spring (which does its property injection and proxy generation at that point).
I'm not quite sure what scope you need; with prototype, you'll get a fresh bean in each place.
#Configuration
public class FooBarMaker {
#Bean(autowire = Autowire.BY_TYPE)
#Scope("prototype")
public FooBar makeAFooBar() {
// You probably need to do some more work in here, I imagine
return new FooBar();
}
}
You can inject properties required for manufacture into the #Configuration bean. (I use this to create instances of an interface where the name of the class to instantiate is defined at runtime.)
suppose that u have the following dependency chain:
A --> B --> C --> x --> y -- > Z
A, B, C are spring managed beans (constructed and manged by spring framework)
x, y are really simple POJOs that constructed by your application, without spring assistance
now if you want that y will get a reference to Z using spring that you need to have a 'handle' to the spring ApplicationContext
one way to do it is to implement ApplicationContextAware interface . In this case I would suggest that either A, B or C will implement this interface and will store the applicationContext reference in a static member.
so lets take Class C for example:
class C implmenets ApplicationContextAware{
public static ApplicationContex ac;
void setApplicationContext(ApplicationContext applicationContext) {
ac = applicationContext;
}
.............
}
now, in class y you should have:
(Z)(C.ac.getBean("classZ")).doSomething()
HTH -- Yonatan
Another way to do this is to us use AspectJ. This is the recommended way of injection Spring beans into non-managed objects that are created with the new operator. See this for details:
http://www.javacodegeeks.com/2011/02/domain-driven-design-spring-aspectj.html
Searching endless combos of autowire inject spring bean into pojo applicationcontextaware beanaware etc circled me back here but this didnt provide a complete enough solution for me.
This is a much better implementation/tutorial of this IMO:
I hope it helps everyone like it finally helped me.
Accessing Spring Beans from outside Spring Context
Be careful that in oldest version of Spring, there is thread-safe problem with bean factory http://jira.springframework.org/browse/SPR-4672
If you want to create an object outside the Spring context, and make that object available for injection into other beans that are in the Spring context, you can follow the steps in this article.
Basically, you create a parent application context and push your external object into this parent context as a singleton. Then you create you main application context (for example, from xml files), with the parent application context as its parent.
Object externalObject = ...
GenericApplicationContext parent = new StaticApplicationContext();
parent.getBeanFactory().registerSingleton( "externalObject", externalObject );
parent.refresh();
ApplicationContext appContext = new ClassPathXmlApplicationContext( ... , parent);
From a Spring configuration class, set a static field on the non-Spring class that needs the beans.
I have an example in my answer to a Liquibase question: https://stackoverflow.com/a/71191546/5499391