Monday, July 1, 2013

Spring - How to make a bean object available via @Autowired at any layer

Hi,

Recently in a project I faced a situation where there was a need that one java object needs to be accessed by different layers like Controller, Biz, DAO layers etc. 

PROBLEM TAKEN
I had a UserInfo object that contains the User information who has logged into the system and his authorities.  You can think it as ThreadLocal object containing user information and his authorities.  And I want to access it in most of the other spring component classes.

TECHNOLOGY STACK
  •       Spring
  •       Hibernate
  •       Tomcat
  •       Maven
  •       jQuery


SOLUTION APPROACH
To solve this problem I made one CustomSuccessHandler by extending org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler.

This handler populates the User object in UserInfo class after successful authentication.  Below is the structure of UserInfo class.

            UserInfo.java
@Component
public class UserInfo
{
    private User ctxUser;
    ....

    /**
     * @return the ctxUser
     */
    public User getCtxUser()
    {
        return ctxUser;
    }

    /**
     * @param ctxUser
     *            the ctxUser to set
     */
    public void setCtxUser(User ctxUser)
    {
        this.ctxUser = ctxUser;
        populateGroupsAuth();
    }

    ....
    ....
}

You can place additional variables if required.  Here User is my own pojo class.  It has some fields and getters/setters.

Now to support
    @Autowired
    User ctxUser;

In any java file within Spring Context I include below entries in applicationContext.xml or you can do that in any of your context representing xml file.         

First way:
    <bean id="userInfo" class="UserInfo"
        scope="session">
        <aop:scoped-proxy />
    </bean>

    <bean
        class="org.springframework.web.context.support.ServletContextAttributeExporter"
        p:attributes-ref="servletContextAttributesMap">
    </bean>

    <util:map id="servletContextAttributesMap">
        <entry key="appCtx">
            <ref bean="userInfo" />
        </entry>
    </util:map>

Second way:
Assuming all your views are going via some controller that is intercepted by spring’s InternalResourceViewResolver
    <bean id="viewResolver"
        class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix">
            <value>/pages/</value>
        </property>
        <property name="suffix">
            <value>.jsp</value>
        </property>
        <property name="exposeContextBeansAsAttributes" value="true"/>
        <property name="exposedContextBeanNames">
            <list>
                <value>userInfo</value>
            </list>
        </property>
    </bean>

                In this case you can now access UserInfo bean at class level and use it.

Third way:
To allow a bean object to be available in your request object below is the configuration for the same class
    <bean id='userInfo' class='UserInfo' />

    <bean scope="request" factory-bean="userInfo"
        factory-method="getCtxUser">
        <aop:scoped-proxy />
    </bean>

Please do let me know if you need more information.

Thanks
Shailendra