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.
Related
I'm trying to clear up some Spring concepts here. I have two contextConfigureLocation xml files as defined in web.xml here
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/spring-datasource.xml
/WEB-INF/security.xml
</param-value>
</context-param>
So, in the first configuraton file "spring-datasource.xml", I did a component-scan like so
<context:annotation-config />
<context:component-scan base-package="com.mycompany.root" />
my source code structure is like so
com
--->mycompany
--------------->root
--------------------->ui
--------------------->server
--------------------->etc
The issue is, my controllers decorated with #controllers under "com.mycompany.root.ui" never got picked up.
in the spring-servlet.xml, I had to do another componet-scan like so
<context:annotation-config />
<context:component-scan base-package="com.mycompany.root.ui" />
<mvc:resources mapping="/images/**" location="/images/" />
<mvc:annotation-driven />
<mvc:default-servlet-handler/>
for my controllers to get picked up.
Why is that? I thought whatever higher up in the parent configuration files should automatically be avaialbe to the children configuraton file? Or is that not the case as evident here.
[EDit] - After some reading, I think I'm more curious with controllers instantiated in the root application context, what happened to them when the spring-dispatch servlet got to them? The child applicaton context just ignored them? It will be nice if anyonne can show me some source code of dispatch servlet that did the ignore.
Thanks
In a typical Spring MVC project there two "containers": One created by ContextLoaderListener and the other created by DispatchServlet.
I want to know, are these really two IoC container instance?( I see two bean config files, one is root-context.xml the other is servlet-context.xml)
If there are 2 containers, then what's the relationship?
Can the beans declared in one container be used in the other?
From the Spring Official Website:
The interface org.springframework.context.ApplicationContext
represents the Spring IoC container and is responsible for
instantiating, configuring, and assembling the aforementioned beans.
The container gets its instructions on what objects to instantiate,
configure, and assemble by reading configuration metadata. The
configuration metadata is represented in XML, Java annotations, or
Java code.
Again from official Doc:
In the Web MVC framework, each DispatcherServlet has its own
WebApplicationContext, which inherits all the beans already defined in
the root WebApplicationContext. These inherited beans can be
overridden in the servlet-specific scope, and you can define new
scope-specific beans local to a given Servlet instance.
Now coming to your Question, as is stated here:
In Spring Web Applications, there are two types of container, each of
which is configured and initialized differently. One is the
“Application Context” and the other is the “Web Application Context”.
Lets first talk about the “Application Context”. Application Context
is the container initialized by a ContextLoaderListener or
ContextLoaderServlet defined in the web.xml and the configuration
would look something like this:
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:*-context.xml</param-value>
</context-param>
In the above configuration, I am asking spring to load all files from
the classpath that match *-context.xml and create an Application
Context from it. This context might, for instance, contain components
such as middle-tier transactional services, data access objects, or
other objects that you might want to use (and re-use) across the
application. There will be one application context per application.
The other context is the “WebApplicationContext” which is the child
context of the application context. Each DispatcherServlet defined in
a Spring web application will have an associated
WebApplicationContext. The initialization of the WebApplicationContext
happens like this:
<servlet>
<servlet-name>platform-services</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:platform-services-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
You provide the name of the spring configuration file as a servlet
initialization parameter. What is important to remember here is that
the name of the XML must be of the form -servlet. xml.
In this example, the name of the servlet is platform-services
therefore the name of our XML must be platform-service-servlet.xml.
Whatever beans are available in the ApplicationContext can be referred
to from each WebApplicationContext. It is a best practice to keep a
clear separation between middle-tier services such as business logic
components and data access classes (that are typically defined in the
ApplicationContext) and web- related components such as controllers
and view resolvers (that are defined in the WebApplicationContext per
Dispatcher Servlet).
Check these links
Difference between applicationContext.xml and spring-servlet.xml in Spring Framework
http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/beans.html#beans-basics
There aren't two separate containers created. Typically, you want spring to instantiate the object declared in the servlet-context.xml when the object is required. So, you map the servlet-context.xml configuration file to the Dispatcher Servlet i.e. you want to initialize the object when a request hits the dispatcher servlet.
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
Where as, if you want to initialize the object and perform action when the context is being loaded you would declare the configuration file with in the context-param tags of your deployment descriptor.
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/root-context.xml</param-value>
</context-param>
You could test this out by writing by declaring separate beans in the servlet-context.xml and root-context.xml and then, autowiring them in a custom Context Loader Listener class. You would find only the root-context instances are initialized and servlet-context beans are null.
ApplicationContext a registry of components (beans).
ApplicationContext defines the beans that are shared among all the servlets i.e. root context configuration for every web application.
spring*-servlet.xml defines the beans that are related WebApplicationContexts here DispatcherServlet.
Spring container can have either single or multiple WebApplicationContexts.
Spring MVC have atleast 2 container -
Application Context declared by
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/root-context.xml</param-value>
</context-param>
Servlet context declared by -
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>servlet-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
And a web application can define any number of DispatcherServlet's. Each servlet will operate in its own namespace, loading its own application context with mappings, handlers, etc. Only the root application context as loaded by ContextLoaderListener, if any, will be shared. Thus can have any number of child containers.
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)
I have a problem with my Spring application where my #Service classes are being created twice when the application starts. I know this is a problem with my configuration, as I've experienced it before, but what exactly am I doing wrong?
Is there anything fundamentally wrong with the way I've laid out my config, below? (I have omitted everything I deem to be irrelevant)
web.xml:
<servlet>
<servlet-name>myapp</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>myapp</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/myapp-config.xml
/WEB-INF/myapp-security.xml
/WEB-INF/myapp-mvc.xml
</param-value>
</context-param>
<listener>
<listener-class>com.myapp.servlet.MyAppContextListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
myapp-servlet.xml
<context:component-scan base-package="com.myapp" annotation-config="true" />
<mvc:annotation-driven />
myapp-config.xml
<context:component-scan base-package="com.myapp" annotation-config="true" />
<context:annotation-config />
In addition to #GaryF's answer, there is a following beautiful solution for this problem (used in projects generated by Spring Roo):
myapp-config.xml
<!-- Load everything except #Controllers -->
<context:component-scan base-package="com.myapp">
<context:exclude-filter expression="org.springframework.stereotype.Controller"
type="annotation"/>
</context:component-scan>
myapp-servlet.xml
<!-- Load #Controllers only -->
<context:component-scan base-package="com.myapp" use-default-filters="false">
<context:include-filter expression="org.springframework.stereotype.Controller"
type="annotation"/>
</context:component-scan>
EDIT:
Removing <context:component-scan> from myapp-config.xml means, that all your autodiscovered annotated beans are registered in DispatcherServlet's context (that is, the context loaded from myapp-servlet.xml).
However the recommended approach is to use servlet's context for presentation-specific things (such as controllers), and use the root context (myapp-config.xml) for the core services of your application. The solution above make this approach easy.
Regarding the practical considerations, when your core services are placed in servlet's application context, they can't be accessed from outside the scope of that servlet, for example, from another servlets (you may need to use another servlets to implement another access technologies) or filters (such as Spring Security filters). That's the reason for having core services in the root application context.
As an addition to the answer #axtavt gave, I’d like to give the matching Spring JavaConfig here.
In RootConfig.java:
#ComponentScan(basePackages = { "com.myapp" },
excludeFilters = #Filter({Controller.class, Configuration.class}))
In WebMvcConfig.java:
#ComponentScan(basePackages = { "com.myapp" },
useDefaultFilters = false, includeFilters = #Filter(Controller.class))
You're doing two separate component-scans over the same base-package. Remove one of them.
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" />