Can Spring Webflow define beans within flow.xml definitions? - spring

I'm defining a lot of flows and each of my flows has a lot of actions within its states.
The namespace seems to be getting fairly crowded now, so I'm wondering if it's possible to define the spring beans for flow actions from within the flow.xml
or some other way such that it's visible to the flow, but not to other flows, but still has access to the greater spring context (for things such as service injections)

You have 1 spring context and therefore you can't have beans invisible for each other. That said, you can put different beans wit different ids in different xmls, using either:
in web.xml:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/flow1.xml,/WEB-INF/flow2.xml</param-value>
</context-param>
or in applicationContext.xml (your flowX.xml should be under /WEB-INF/classes - i.e. the root of the classpath):
<import resource="classpath*:/flow1.xml" />
<import resource="classpath*:/flow2.xml" />

Related

Spring: link between WebApplicationContext and ApplicationContext?

I am working on a Spring application. I started from creating a small java app using spring. Later, it became necessary to add a web interface. I decided to use Spring MVC. Now I am confused. In my web.xml I have
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/mvc-dispatcher-servlet.xml,
/WEB-INF/applicationContext.xml
</param-value>
</context-param>
where mvc-dispacher-servlet.xml contains beans necessary for web logic while applicationContext.xml contains beans performing some specific operations. My question is: Are beans in these files going to be aware of each other? Is it going to be a one big container which includes beans from both config files? or these containers are separate?
Yes it will be in one context which will be loaded from the web application context. Its the same as you would do when using the application context and passing in multiple files to it.

spring servlet-context in sts

Using STS in eclipse to create an mvc project I notice that the servlet-context.xml seems to be written to be used in both the root context and the dispatcherservlet Context. I say this because I notice that the context:component-scan is in it, which is often loaded into the root context, but it is loaded into the dispatcherservlet context. I also noticed a sample spring mvc/jpa project - http://duckranger.com/2012/04/spring-mvc-3-x-with-sts-tutorial-part-iii-add-some-jpa/ - that specifically loads the servlet-context.xml into both contexts. I thought the idea was to keep a clean separation between the contexts. Can someone explain this to me?
The following configuration is plain wrong
<!-- The definition of the Root Spring Container shared by all Servlets and Filters -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:META-INF\root-context.xml
classpath:META-INF\servlet-context.xml
classpath:META-INF\datasource.xml
</param-value>
</context-param>
<!-- Processes application requests -->
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:META-INF\servlet-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
Your root and servlet context should NEVER import the same files, as the beans from the root context will already be available in the servlet context because of the context hierarchy in Spring. There is no point to create copies of them in the different contexts(especially because the beans in the root context will be shadowed by the ones in the servlet context, for example if you declare <tx:annotation-driven> only in the root context it will not affect the behavior of the servlet context beans, which will force you to tangle configuration even more) .
It's very illogical to put <jpa:repositories> into the servlet context, because it's VERY likely that you will use the repositories from the service layer.
Typically you should not put anything but the MVC configuration to the servlet context. It's the root web app context where the services should live. Servlet context provides separation of the controllers from the services, so when you test your services with Spring Test Context framework you don't have to create the controllers(if you want to test the mappings you should use Spring MVC Test framework) and test the application services directly.
To be clear, if we examine the figure from the Hexagonal Architecture article
the the servlet context should contain only user-side API related things but not the application. It is arguable whether you should divide the configuration of the root web app context and put data-side-api into separate configuration file but the question was about servlet/root contexts.
Just to be less abstract here's some informal diagram of what I typically keep in mind(in terms of Spring contexts and bean configuration files) when configure a Spring application(of course it's all subjective, it's not a super solution and actually is a bit over complicated - it's unlikely that I will need so many servlets and configuration files)

Using multiple applicationContexts with duplicate bean ids from different jar files in a webApplication

I have bean1 and bean2 defined in applicationContext1 in jar1 and have bean1 (of course a different class) and bean3 defined in applicationContext2 in jar2
I need to use both jar1 and jar2 in my webapplication, which also has an applicationContext3.
I use the below entries in web.xml
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>applicationContext1.xml
<param-value>applicationContext2.xml
<param-value>/WEB-INF/properties/application/applicationContext3.xml
</param-value>
</context-param>
Is there a way to prefix the applicationContext IDs so that they dont step over each other;
for eg:
in applicationContext3 can I have
<bean id="myBean" >
<property name="bean1" ref="ac1:bean1">
<property name="bean2" ref="ac2:bean1">
</bean>
Thanks in advance
Maybe you could arrange it somehow (Spring is very flexible and powerful) but I think you should clean up a little your contexts.
If you are in total control of both libraries and the main app, I would recommend creating only a main applicationContext and copying the relevant beans from the JARs.
Other good solution is to separate every applicationContext in context modules, like repository-context.xml, business-context.xml, etc and reference only the ones you need, so you have more control over what is getting loaded.
Also, take a look at this:
http://www.gridshore.nl/2008/05/13/spring-application-context-loading-tricks/

context depended scan-component filter

My SpringMVC based webapp uses typically 2 contexts: the webapplication context for the MVC dispatcher servlet and the parent/root application context.
<!-- the context for the dispatcher servlet -->
<servlet>
<servlet-name>webApp</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:servlet-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
....
<!-- the context for the root/parent application context -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:root-context.xml</param-value>
</context-param>
Within these contexts, I use component scanning for loading all beans.
My packages are named according their usecase (e.g. com.abc.registration, com.abc.login etc.) rather then based on the technological tier (e.g. com.abc.dao, com.abc.services etc.)
Now my question: in order to avoid duplicate scanning of some classes, is it a good practice, to filter the candidate component classes for both contexts, e.g. include only the MVC Controller for web context scan and include all other components (services, dao/repositorie) in the root application context ?
<!-- servlet-context.xml -->
<context:component-scan base-package="com.abc.myapp" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!-- root-context.xml -->
<context:component-scan base-package="de.efinia.webapp">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
Or is it neither important nor necessary to avoid such duplication for the component scan ?
I like your solution in two areas:
You divide classes based on uses cases rather than layers. If you would have a single web package with all controllers then you wouldn't have problems. But still I found such an approach much better.
Yes, you should filter classes. Obviously it's not a problem with increased memory footprint, as this is marginal (but increased startup time might be significant).
However having duplicated beans (both controllers and service beans) might introduce subtle bugs and inconsistencies. Some connection pool has been initialized two times, some startup hook runs two times causing unexpected behavior. If you use singleton scope, keep that it way. Maybe you won't hit some problems immediately, but it's nice to obey the contracts.
BTW note that there is an <mvc:annotation-driven/> tag as well.
It is a good practice, indeed. The parent application context should not have controllers in it.
I can't add more arguments to justify the practice, but it certainly is cleaner that way.

How to declare a parent application context

I find myself using two identical beans in my applicationContext.xml and my applicationContext-test.xml. I'd like my test context to be able to inherit from my app context, to avoid repeating myself.
I've seen plenty of material indicating that you can declare a parent application context and reference beans from that context, but I can't find a useful example. Can anyone help?
Update
As some background info, my normal application context is being loaded in web.xml:
<context-param>
<description>Application Contexts for Spring</description>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/classes/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
My test application context is loaded in my unit tests:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = "/applicationContext-test.xml")
So let's say I have a bean in my regular context:
<bean name="someBean" class="com.foo.MyClass" />
Then, in my test application context, I'd like to refer to this bean. How do I do it?
Update
Per skaffman's suggestion, I've moved the bean into a SharedBeans.xml file and imported it into my applicationContext.xml. However, this causes a SAXParser exception:
org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Failed to import bean definitions from URL location [classpath:SharedBeans.xml]
Offending resource: ServletContext resource [/WEB-INF/classes/applicationContext.xml]; nested exception is org.springframework.beans.factory.xml.XmlBeanDefinitionStoreException: Line 1 in XML document from class path resource [SharedBeans.xml] is invalid; nested exception is org.xml.sax.SAXParseException: cvc-elt.1: Cannot find the declaration of element 'bean'.
at org.springframework.beans.factory.parsing.FailFastProblemReporter.error(FailFastProblemReporter.java:68)
I can't be sure what I'm doing wrong. The bean was working fine in my context file, and all I did was cut and paste into the new file. Here are the contents of SharedBeans.xml in its entirety:
<bean name="properties" class="com.foo.Properties">
<constructor-arg><value>${module.name}</value></constructor-arg>
<constructor-arg><value>${businessUnit}</value></constructor-arg>
<constructor-arg><value>${product}</value></constructor-arg>
<constructor-arg><value>${env}</value></constructor-arg>
<constructor-arg><value>${machineName}</value></constructor-arg>
<constructor-arg><value>${collectionSet.company}</value></constructor-arg>
<constructor-arg><value>${route.tag}</value></constructor-arg>
<constructor-arg><value>${timeout}</value></constructor-arg>
</bean>
This doesn't strike me as a particularly good use-case for a parent context, which is useful mainly to set up a hierarchy (e.g. one root webapp context, multiple child servlet contexts).
For your situation, it's going to be simpler and easier to understand if you just extract the common bean definitions into a separate file, and then <import> them into each context file that needs it. You could do this with parent-child contexts, but it's going to be harder to understand, unnecessarily so.
OK, so an example, put your shared bean definition into a file called shared-beans.xml, and put it (for now) at the top-level of your classpath, containing:
<bean name="someBean" class="com.foo.MyClass" />
Then, inside applicationContext-test.xml and /WEB-INF/classes/applicationContext.xml, add the following:
<import resource="classpath:/shared-beans.xml" />
All of the bean definitions in shared-beans.xml will now be imported into each app context. You don't get a third app-context by doing this, you just import the bean definitions from another file.
You can move your common declaration to the separate XML file and place it in classpath (for easy access from test). Then you can do the following:
<context-param>
<description>Application Contexts for Spring</description>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/classes/applicationContext.xml
classpath:/common-beans.xml
</param-value>
</context-param>
.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(
locations = {"/applicationContext-test.xml", "common-beans.xml"})
You can also include common-beans.xml from both contexts using <import>, as suggested by skaffman.

Resources