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.
Related
I have a web application and SOAP Web service in same project. I have defined two servlet xml
mvc-servlet.xml
<import resource="ws-applicationContext.xml"/>
soap-servlet.xml
<import resource="soap-applicationContext.xml"/>
Bot these applicationContext import a common file
Contents of soap-applicationContext.xml
<import resource="my-common-applicationContext.xml"/>
<context:annotation-config/>
<context:component-scan base-package="com.xyz.myproject1"/>
Contents of ws-applicationContext.xml
<import resource="my-common-applicationContext.xml"/>
<context:annotation-config/>
<context:component-scan base-package="com.xyz.myproject2"/>
my-common-applicationContext.xml has common packages which are needed by both like integration,data etc.
At start of application , since my-common-applicationContext.xml is included in both the application context, it gets loaded twice. This leads to DataConfigurations being loaded twice.
What is the best way to solve this problem also ensuring both the context have access to relevant beans ?
Figured out the right way to do it .
All common files go in common but are loaded with ContextLoaderListener. This ensures all classes are available.
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:my-common-applicationContext.xml
</param-value>
</context-param>
Following on from How do I inject a Spring bean into Apache Wink?
I'm now using wink-spring-support and I thought I had things set up correctly.
web.xml includes:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:META-INF/wink/wink-core-context.xml
classpath:applicationContext.xml
</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>restServlet</servlet-name>
<servlet-class>org.apache.wink.server.internal.servlet.RestServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>restServlet</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
META-INF/wink/wink-core-context.xml contains:
<bean class="org.apache.wink.spring.Registrar">
<property name="instances">
<set>
<ref bean="myservice" />
</set>
</property>
</bean>
<bean id="myservice" class="mystuff.ServiceImpl"/>
There's a #Autowired annotation in mystuff.ServiceImpl that injects other Spring stuff, and mystuff.ServiceImpl implements a JAX-RS annotated interface and itself includes a JAX-RS #Path("/services") annotation.
I can see Spring loading up this stuff just fine, including the myservice bean. However when I request my resources, I get a 404 not found. As Wink starts, I can see a couple of log entries that might indicate the problem:
applicationConfigLocation property was not defined
Using application classes null named in init-param applicationConfigLocation
Have I missed something somewhere? Any advice?
The problem was my misunderstanding the docs.
There is a Spring configuration META-INF/server/wink-core-context.xml provided with wink-spring-support. This registers the BeanPostProcessors that actually do the setup and must be referenced from contextConfigLocation.
I thought that I put my configuration in there, which explains why the application didn't get registered with Wink on startup.
I am using Spring MVC 3.0.6 and Spring security 3.0.7. I cannot #Autowire the RoleDao class to my user class when in the security context.
my web.xml file:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/spring/root-context.xml
/WEB-INF/security-app-context.xml
</param-value>
</context-param>
<!-- Creates the Spring Container shared by all Servlets and Filters -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 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>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
The security-app-context.xml:
<beans:bean id="chineseCheckersEntryPoint" class="com.nike.golf.security.ChineseCheckersAuthenticationEntryPoint" />
<beans:bean id="chineseCheckersFilter" class="com.nike.golf.security.ChineseCheckersAuthenticationFilter">
<beans:property name="authenticationManager" ref="authenticationManager"/>
</beans:bean>
<http use-expressions="true" auto-config="false" entry-point-ref="chineseCheckersEntryPoint">
<intercept-url pattern="/secure/extreme/**" access="hasRole('supervisor')" />
<intercept-url pattern="/user/**" access="permitAll" />
<intercept-url pattern="/profile/**" access="isAuthenticated()" />
<intercept-url pattern="/secure/**" access="isAuthenticated()" />
<custom-filter position="PRE_AUTH_FILTER" ref="chineseCheckersFilter" />
</http>
<authentication-manager alias="authenticationManager">
<authentication-provider ref="chineseCheckersAuthenticationProvider" />
</authentication-manager>
<beans:bean id="chineseCheckersAuthenticationProvider" class="com.nike.golf.security.ChineseCheckersAuthenticationProvider" />
In my user object where it uses the roleDao, it's null. It has not been autowired. From all the research I have done online it seems to be related to the different contexts, and not being able to autowire between them.
Can someone help me understand these contexts and how I can get them into the same context?
Thank you everyone for your help. I managed to figure this out.
This question was similar to mine and got me moving in the right direction:
Declaring Spring Bean in Parent Context vs Child Context
This forum post really simplified the idea for me.
In your web.xml file you define the servlet context and the application context.
The application context is configured through these pieces of XML:
<!-- The definition of the Root Spring Container shared by all Servlets and Filters -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/spring/root-context.xml
/WEB-INF/security-app-context.xml
</param-value>
</context-param>
<!-- Creates the Spring Container shared by all Servlets and Filters -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
any number of *context.xml files you pass into the context-param > contextConfigLocation are in the application context. This is the parent context.
The servlet context gets created in the web.xml file by this bit of xml:
<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>
The servlet context as the child context has access to the application context (parent).
This was the key thing for me to understand. So I moved all configuration I had in the servlet context, up to the application context.
Like the answer in the other question I linked to above says #Autowired still does not work. Anyone know the reason for that?
So to get around this I defined the beans and the properties in the xml all the way from the property I was concerned with down to the sessionFactory.
The thing is now I could wire up the beans I needed in xml all the way up the hierarchy to sessionFactory because it was in the same context, since I moved it up to the application context from the servlet context where it was before.
In my question I didn't even post the servlet-context.xml file because I didn't think it needed to be touched, but in fact I needed to move the configuration up to the app context if I wanted to wire things up to my security beans.
I hope that makes sense.
You can imagine a context as a set of Spring beans.
Contexts can be nested, so that the outer context can access the beans from the inner one, but not the other way around. An example for this are typical web application, the have two contexts: the inner one specified with the contextConfigLocation and loaded by ContextLoaderListener, and the outer one configured with the DispatcherServlet.
One way to merge two xml files to one context, is introducing a third apllication configuration xml file, that only include then other xml files via bean:include. And then you have only to specify this third xml files for the loader. But I am not sure if you really have 2 application contexts configured for ContextLoaderListener. -- Anyway you can try the trick with the 3. xml file.
It doesn't look like #Secured on methods in my #Controller are being read. When security filtering based on sec:intercept-url is being used, this seems to be working just fine. The following code results in Spring Security giving me this log entry:
DEBUG: org.springframework.security.web.access.intercept.FilterSecurityInterceptor - Public object - authentication not attempted
web.xml
contextConfigLocation
/WEB-INF/spring/root-context.xml
<!-- Creates the Spring Container shared by all Servlets and Filters -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 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>
/WEB-INF/spring/appServlet/servlet-context.xml
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- Filter security -->
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
servlet-context.xml holds the configuration of the viewResolvers and all the marshalling. This configuration is annotation-driven.
root-context.xml
<sec:global-method-security secured-annotations="enabled" />
<sec:http auto-config="true">
<sec:http-basic/>
</sec:http>
<!-- Declare an authentication-manager to use a custom userDetailsService -->
<sec:authentication-manager>
<sec:authentication-provider
user-service-ref="userDetailsService">
<sec:password-encoder ref="passwordEncoder" />
</sec:authentication-provider>
</sec:authentication-manager>
<bean
class="org.springframework.security.authentication.encoding.PlaintextPasswordEncoder"
id="passwordEncoder" />
<sec:user-service id="userDetailsService">
<sec:user name="john" password="john" authorities="ROLE_USER, ROLE_ADMIN" />
<sec:user name="jane" password="jane" authorities="ROLE_USER" />
</sec:user-service>
PingController.java
#Controller
public class PingController {
#Secured("ROLE_ADMIN")
#RequestMapping(value = "/ping", method = RequestMethod.GET)
public void ping() {
}
}
This doesn't seem to have any relation to which authentication method I'm using, so the basic-http-tag can be overlooked.
I have this idea that the #Secured doesn't work because of it being used in another context than the root-context.xml in which the security is being configured. I've tried to move this configuration to the servlet-context.xml, but it doesn't seem to reach the springSecurityFilterChain. Any thoughts on the problem and and my theory?
You are right, <global-method-security> is applied at per-context basis. However, you don't need to move the whole security configuration to servlet-context.xml, just add a <global-method-security> element to it.
See Spring Security FAQ (emphasis mine). If you apply pointcuts to service layer you only need to set <global-method-security> in your app's security context.
In a Spring web application, the application context which holds the
Spring MVC beans for the dispatcher servlet is often separate from the
main application context. It is often defined in a file called
myapp-servlet.xml, where “myapp” is the name assigned to the Spring
DispatcherServlet in web.xml. An application can have multiple
DispatcherServlets, each with its own isolated application context.
The beans in these “child” contexts are not visible to the rest of the
application. The “parent” application context is loaded by the
ContextLoaderListener you define in your web.xml and is visible to all
the child contexts. This parent context is usually where you define
your security configuration, including the
element). As a result any security constraints applied to methods in
these web beans will not be enforced, since the beans cannot be seen
from the DispatcherServlet context. You need to either move the
declaration to the web context or moved the
beans you want secured into the main application context.
Generally we would recommend applying method security at the service
layer rather than on individual web controllers.
What is the correct way to split Spring's configuration to multiple xml files?
At the moment I have
/WEB-INF/foo-servlet.xml
/WEB-INF/foo-service.xml
/WEB-INF/foo-persistence.xml
My web.xml has the following:
<servlet>
<description>Spring MVC Dispatcher Servlet</description>
<servlet-name>intrafest</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/foo-*.xml
</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/foo-*.xml
</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
The actual questions:
Is this approach correct/best?
Do I really need to specify the config locations both in the DispatcherServlet AND the context-param sections?
What do I need to keep in mind to be able to reference beans defined in foo-servlet.xml from foo-service.xml? Does this have something to do with specifying contextConfigLocation in web.xml?
Update 1:
I'm using Spring framework 3.0. It's my understanding that I don't need to do resource importing like this:
<import resource="foo-services.xml"/>
Is this a correct assumption?
I find the following setup the easiest.
Use the default config file loading mechanism of DispatcherServlet:
The framework will, on initialization
of a DispatcherServlet, look for a
file named [servlet-name]-servlet.xml
in the WEB-INF directory of your web
application and create the beans
defined there (overriding the
definitions of any beans defined with
the same name in the global scope).
In your case, simply create a file intrafest-servlet.xml in the WEB-INF dir and don't need to specify anything specific information in web.xml.
In intrafest-servlet.xml file you can use import to compose your XML configuration.
<beans>
<bean id="bean1" class="..."/>
<bean id="bean2" class="..."/>
<import resource="foo-services.xml"/>
<import resource="foo-persistence.xml"/>
</beans>
Note that the Spring team actually prefers to load multiple config files when creating the (Web)ApplicationContext. If you still want to do it this way, I think you don't need to specify both context parameters (context-param) and servlet initialization parameters (init-param). One of the two will do. You can also use commas to specify multiple config locations.
Mike Nereson has this to say on his blog at:
http://blog.codehangover.com/load-multiple-contexts-into-spring/
There are a couple of ways to do this.
1. web.xml contextConfigLocation
Your first option is to load them all into your Web application
context via the ContextConfigLocation element. You’re already going
to have your primary applicationContext here, assuming you’re writing
a web application. All you need to do is put some white space between
the declaration of the next context.
<context-param>
<param-name> contextConfigLocation </param-name>
<param-value>
applicationContext1.xml
applicationContext2.xml
</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
The above uses carriage returns. Alternatively, yo could just put in a
space.
<context-param>
<param-name> contextConfigLocation </param-name>
<param-value> applicationContext1.xml applicationContext2.xml </param-value>
</context-param>
<listener>
<listener-class> org.springframework.web.context.ContextLoaderListener </listener-class>
</listener>
2. applicationContext.xml import resource
Your other option is to just add your primary applicationContext.xml
to the web.xml and then use import statements in that primary context.
In applicationContext.xml you might have…
<!-- hibernate configuration and mappings -->
<import resource="applicationContext-hibernate.xml"/>
<!-- ldap -->
<import resource="applicationContext-ldap.xml"/>
<!-- aspects -->
<import resource="applicationContext-aspects.xml"/>
Which strategy should you use?
1. I always prefer to load up via web.xml.
Because , this allows me to keep all contexts isolated from each
other. With tests, we can load just the contexts that we need to run
those tests. This makes development more modular too as components
stay loosely coupled, so that in the future I can extract a package
or vertical layer and move it to its own module.
2. If you are loading contexts into a non-web application, I would use the import resource.
There are two types of contexts we are dealing with:
1: root context (parent context. Typically include all jdbc(ORM, Hibernate) initialisation and other spring security related configuration)
2: individual servlet context (child context.Typically Dispatcher Servlet Context and initialise all beans related to spring-mvc (controllers , URL Mapping etc)).
Here is an example of web.xml which includes multiple application context file
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<display-name>Spring Web Application example</display-name>
<!-- Configurations for the root application context (parent context) -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/spring/jdbc/spring-jdbc.xml <!-- JDBC related context -->
/WEB-INF/spring/security/spring-security-context.xml <!-- Spring Security related context -->
</param-value>
</context-param>
<!-- Configurations for the DispatcherServlet application context (child context) -->
<servlet>
<servlet-name>spring-mvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/spring/mvc/spring-mvc-servlet.xml
</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>spring-mvc</servlet-name>
<url-pattern>/admin/*</url-pattern>
</servlet-mapping>
</web-app>
#eljenso : intrafest-servlet.xml webapplication context xml will be used if the application uses SPRING WEB MVC.
Otherwise the #kosoant configuration is fine.
Simple example if you dont use SPRING WEB MVC, but want to utitlize SPRING IOC :
In web.xml:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:application-context.xml</param-value>
</context-param>
Then, your application-context.xml will contain: <import resource="foo-services.xml"/>
these import statements to load various application context files and put into main application-context.xml.
Thanks and hope this helps.
I'm the author of modular-spring-contexts.
This is a small utility library to allow a more modular organization of spring contexts than is achieved by using Composing XML-based configuration metadata. modular-spring-contexts works by defining modules, which are basically stand alone application contexts and allowing modules to import beans from other modules, which are exported ín their originating module.
The key points then are
control over dependencies between modules
control over which beans are exported and where they are used
reduced possibility of naming collisions of beans
A simple example would look like this:
File moduleDefinitions.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:module="http://www.gitlab.com/SpaceTrucker/modular-spring-contexts"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.gitlab.com/SpaceTrucker/modular-spring-contexts xsd/modular-spring-contexts.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config />
<module:module id="serverModule">
<module:config location="/serverModule.xml" />
</module:module>
<module:module id="clientModule">
<module:config location="/clientModule.xml" />
<module:requires module="serverModule" />
</module:module>
</beans>
File serverModule.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:module="http://www.gitlab.com/SpaceTrucker/modular-spring-contexts"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.gitlab.com/SpaceTrucker/modular-spring-contexts xsd/modular-spring-contexts.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config />
<bean id="serverSingleton" class="java.math.BigDecimal" scope="singleton">
<constructor-arg index="0" value="123.45" />
<meta key="exported" value="true"/>
</bean>
</beans>
File clientModule.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:module="http://www.gitlab.com/SpaceTrucker/modular-spring-contexts"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.gitlab.com/SpaceTrucker/modular-spring-contexts xsd/modular-spring-contexts.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config />
<module:import id="importedSingleton" sourceModule="serverModule" sourceBean="serverSingleton" />
</beans>