Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Declaring Spring Bean in Parent Context vs Child Context

Tags:

I have a spring bean (dao) object which I instantiate in my ServletContext via the following xml:

<bean id="userDao" class="com.company.dao.impl.UserDaoImpl">     <property name="sessionFactory" ref="sessionFactory" /> </bean> 

This bean is declared inside my webapp-servlet.xml file and is used by my app within the ServletContext.

I am also using SpringSecurity. It is my understanding that this runs in a different context (the SecurityContext).

My application has a webapp-security.xml where I instantiate a custom authentication provider. I would like to use my dao that is used in my app to also do the user lookup in my security context, but when I run:

<bean id="userAuthenticationProvider" class="com.company.security.UserAuthenticationProvider">     <property name="userDao" ref="userDao" /> </bean> 

I get errors saying that there is no such bean "userDao". The bean is autowired fine in beans declared in my other context, but not inside my security context. According to the Spring Docs, I believe both separate contexts are needed in web.xml

<listener>     <listener-class>org.springframework.web.context.ContextLoaderListener     </listener-class> </listener>  <listener>     <listener-class>         org.springframework.security.web.session.HttpSessionEventPublisher     </listener-class> </listener> 

So my question is, how can I get access to my DAO that lives in my ServletContext inside my SecurityContext? Is there a scope modifier for my dao, or could I somehow get the ServletContext at runtime within my authentication provider? For reference, this is how I want to use it inside my authentication provider:

public class UserAuthenticationProvider extends     AbstractUserDetailsAuthenticationProvider {      @Override protected UserDetails retrieveUser(String userName,         UsernamePasswordAuthenticationToken authenticationToken)         throws AuthenticationException {      // use dao here 

thanks for explaining this to me

UPDATE:

Continuing my investigation, it seems that the DispatcherServlet where I'm using my daos is a child context, and the security context is somewhere higher up. Consequently, beans in my DispatcherServlet can not be seen by parent contexts. I think the answer is to move my bean declarations into the parent application context somehow, but i'm not sure how to do this. Here is my web.xml

<context-param>     <param-name>contextConfigLocation</param-name>     <param-value>WEB-INF/spring-*.xml     </param-value> </context-param>  <listener>     <listener-class>         org.springframework.security.web.session.HttpSessionEventPublisher     </listener-class> </listener>  <listener>     <listener-class>org.springframework.web.context.ContextLoaderListener     </listener-class> </listener>  <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>     <servlet-name>myapp</servlet-name>     <servlet-class>org.springframework.web.servlet.DispatcherServlet     </servlet-class>     <load-on-startup>1</load-on-startup>     <init-param>         <param-name>listings</param-name>         <param-value>true</param-value>     </init-param> </servlet>      ... 

I moved all of my dao creation out to a spring-dao.xml, and in my spring-security.xml I am now doing a:

<import resource="spring-dao.xml" /> 

The daos stil remain visible to the DispatcherServlet context and invisible to my SecurityContext though.

ANSWERED:

Alright, I figured it out. Here were some helpful links:

http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/beans.html#context-create

http://forum.springsource.org/showthread.php?115774-Spring-Security-Custom-UserDetailsService-to-use-User-Service-Dao

http://static.springsource.org/spring-security/site/faq.html#faq-method-security-in-web-context

So the problem was we need to make sure that the dao exists in the ApplicationContext (higher up spring container). To make sure this happened I changed my web.xml to be:

<context-param>     <param-name>contextConfigLocation</param-name>     <param-value>WEB-INF/spring-dao.xml WEB-INF/spring-security.xml     </param-value> </context-param>  <listener>     <listener-class>org.springframework.web.context.ContextLoaderListener     </listener-class> </listener>  <listener>     <listener-class>         org.springframework.security.web.session.HttpSessionEventPublisher     </listener-class> </listener>  <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>     <servlet-name>webapp</servlet-name>     <servlet-class>org.springframework.web.servlet.DispatcherServlet     </servlet-class>     <load-on-startup>1</load-on-startup>     <init-param>         <param-name>listings</param-name>         <param-value>true</param-value>     </init-param> </servlet> 

I thought this would make sure that the first context loader that starts up will read my dao config (and create my dao beans), then my security config. Since the dao beans are being created this way, I removed the previous "import resource="spring-dao.xml"" statement in the security.xml because it will no longer be needed.

Right after that context-param configuration I created the ContextLoaderListener. This is a higher-level spring container than the DispatcherServlet, so I figured putting this first would be the first guy to read those config files, and he would then create the beans. Then, any child-context would have access to them. This may not be how it works as DispatcherServlet may not even read the contextConfigLocation, but even if it does, I figured that at this point the beans would already be declared, so too bad, the parent context owns them.

Now, for another trick... in order to get my DAO, I could not @Autowired it. I had to manually inject it via XML:

    <bean id="userAuthenticationProvider" class="com.company.app.security.UserAuthenticationProvider">     <property name="userDao" ref="userDao" /> </bean> 

Of course, I made the getter and setter methods on my dao, and voila! I do not know why the @Autowired does not work here. I assume it's by design. Perhaps this is particular to the SecurityContext (it will not pull from other contexts), or perhaps @Autowired in general only pulls from the current context, or maybe because I created the bean via XML, I have to also set any properties via xml and not via annotations? (annotations are enabled and working in my top-level application namespace).

Anyways.. still a lot I don't understand, but the important point is it's finally working.

like image 891
dev Avatar asked Oct 12 '11 21:10

dev


People also ask

Are beans defined in parent context visible to child contexts?

Beans from parent context are visible in child context but not vice-versa. That way, a child context can use beans and configuration from parent context and override only what's necessary. Example of that can be a security defined in parent context and used/overridden in child contexts.

How do you define a bean in Spring context?

In Spring, the objects that form the backbone of your application and that are managed by the Spring IoC container are called beans. A bean is an object that is instantiated, assembled, and otherwise managed by a Spring IoC container.

What is parent context?

“parent context” is the name we use to name the context that would be inherited by the child (where child adds it own job to).

What is @configuration and @bean in Spring?

Annotating a class with the @Configuration indicates that the class can be used by the Spring IoC container as a source of bean definitions. The @Bean annotation tells Spring that a method annotated with @Bean will return an object that should be registered as a bean in the Spring application context.


1 Answers

If you're going to use Spring MVC, you definitely need to understand Spring MVC's ApplicationContext hierarchy. You should also learn something about the basic components and lifecycles in a servlet container, since you seem to be confused about how listeners and servlets work, too.

To explain your situation briefly:

  1. You're creating two ApplicationContexts: the root context and the DispatcherServlet context. The root context is created by the ContextLoaderListener based on the files named in the contextConfigLocation. This context is intended to contain the beans that compose the core logic of your app. The DispatcherServlet context is created when that servlet starts and is based on the file named "webapp-servlet.xml". This context is intended to contain any beans that support the DispatcherServlet instance that it's associated with and should only have view-related beans in it.
  2. The DispatcherServlet context becomes a child of the root context. That allows your core beans from the root context to be injected into the view-layer beans. Visibility is one-way. The view-layer beans aren't available to the core beans, which is desirable. This is why your DAO couldn't be injected into your authentication provider. The DAO was in the child context.
  3. Annotation-based services only apply within the context where they're declared. If @Autowired isn't working for a particular bean, it's because you haven't declared <context:component-scan/> or <context:annotation-config/> in the context where that bean exists.
like image 161
Ryan Stewart Avatar answered Oct 09 '22 20:10

Ryan Stewart