user886596
user886596

Reputation: 2440

Controller defined without component scanning or bean definition

I have a controller in my codebase whose package is not component scanned. This controller is also not defined in a bean in any XML.

Somehow, the controller is working. I'm guessing this is because there is some way of defining a controller in Spring without component scanning or defining it in a bean. However, this controller implements a class called AbstractControllerImpl, and the Helper implementation class IS getting component scanned.

Does the fact that the Helper is getting component scanned mean the Controller gets scanned too? Or if not, how is it possible that this controller works?

@Controller
@RequestMapping("/something")
public class SomeController extends AbstractControllerImpl<SomeControllerHelper> {  
    //Some request mappings here
}

The abstract controller class it extends:

public abstract class AbstractControllerImpl<H extends Helper>
    implements Controller<H>
{    
    private H helper;    

    private BaseValidator validator;

    public H getHelper()
    {
        return helper;
    }

    public void setHelper(H helper)
    {
        this.helper = helper;
    }   

    public void setValidator(BaseValidator validator)
    {
        this.validator = validator;
    }

    public BaseValidator getValidator()
    {
        return validator;
    }

    public Errors doValidation(Object obj)
    {
        Errors validationErrors = new BindException(this, "");
        if (validator != null)
        {
            validator.validate(obj, validationErrors);
        }
        return validationErrors;
    }   
}

The Controller interface:

public interface Controller<H extends Helper>
{
    H getHelper();
}

The helper definition:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">


   <bean id="someHelper" class="com.controller.SomeHelperImpl" />   

</beans>

The helper impl class (which is getting component scanned):

@Component("SomeControllerHelper")
public class ASomeControllerHelperImpl implements SomeControllerHelper {
    //Some methods here
}

Edit: My web.xml. I removed a few of the servlet mappings and changed some of the names, but this is what it looks like:

<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
    <display-name>SomeApp</display-name>

<welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
</welcome-file-list>

<listener>
    <listener-class>
        com.Log4jLoaderServlet
    </listener-class>
</listener>

<context-param>
    <param-name>crossContext</param-name>
    <param-value>true</param-value>
</context-param>

<!-- This listener will load other application context file in addition to springweb-servlet.xml -->
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- The context params that read by ContextLoaderListener  -->
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/spring/root-application-context.xml</param-value>
</context-param>
<!-- Spring security filter -->
<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> 

<listener>
    <listener-class>
        org.springframework.web.context.request.RequestContextListener
    </listener-class>
</listener>

<servlet>
    <servlet-name>rest</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/spring/servlets/rest-servlet.xml</param-value>
    </init-param>       
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>rest</servlet-name>
    <url-pattern>/service/*</url-pattern>
</servlet-mapping>

<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/servlets/app-servlet.xml</param-value>
    </init-param>       

    <load-on-startup>1</load-on-startup>
</servlet>

<error-page>
    <error-code>404</error-code>        
    <location>/portal/error/notfound/</location>
</error-page>

<error-page>
    <error-code>500</error-code>
    <location>/portal/error/internalsystem/</location>
</error-page>

<jsp-config>
    <taglib>
        <taglib-uri>http://java.sun.com/jsp/jstl/core</taglib-uri>
        <taglib-location>/WEB-INF/tags/c.tld</taglib-location>
    </taglib>
    <taglib>
      <taglib-uri>http://jakarta.apache.org/taglibs/unstandard-1.0</taglib-uri>
      <taglib-location>/WEB-INF/tld/unstandard.tld</taglib-location>
    </taglib>
</jsp-config>   

Here is app-servlet.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:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.0.xsd">


    <bean id="viewResolver"
        class="org.springframework.web.servlet.view.UrlBasedViewResolver">
        <property name="viewClass" value="org.springframework.web.servlet.view.tiles2.TilesView" />
        <property name="order" value="0" />
    </bean>

    <bean id="jspViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/views/"/>
        <property name="order" value="1" />
    </bean> 

    <bean  class="org.springframework.web.servlet.view.ResourceBundleViewResolver">

        <property name="basename" value="views"/>
    </bean>

    <bean id="tilesConfigurer"
        class="org.springframework.web.servlet.view.tiles2.TilesConfigurer">
            <property name="definitions">
                <list>                                 
                    <value>/WEB-INF/views/something/views.xml</value>
                    <value>/WEB-INF/views/views.xml</value>
                </list>
            </property>
            <property name="preparerFactoryClass"
                value="org.springframework.web.servlet.view.tiles2.SpringBeanPreparerFactory"/>
    </bean>     
</beans>

rest-servlet.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:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context-3.0.xsd">

    <context:component-scan base-package="com.dw.spring3.rest.controller" />
    <!-- To enable @RequestMapping process on type level and method level -->
    <bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping" />
    <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
        <property name="messageConverters">
            <list>
                <ref bean="jsonConverter" />
            </list>
        </property>
    </bean>

    <bean id="jsonConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
        <property name="supportedMediaTypes" value="application/json" />
    </bean>

</beans>

root-application-cnntext.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:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.0.xsd">
    <context:component-scan
        base-package="com.base.notcontroller" />

    <import resource="/something/common-context.xml" />          
</beans>

Upvotes: 1

Views: 1586

Answers (1)

Sotirios Delimanolis
Sotirios Delimanolis

Reputation: 280122

Does the fact that the Helper is getting component scanned mean the Controller gets scanned too? Or if not, how is it possible that this controller works?

No, that is not possible.

It is not entirely clear what your context configuration looks like given that you've

removed a few of the servlet mappings and changed some of the names

but your @Controller class must be loaded into the context somewhere, possibly in

rest-servlet.xml:

which has

<context:component-scan base-package="com.dw.spring3.rest.controller" />

which is loaded by the DispatcherServlet named `rest.

<servlet>
    <servlet-name>rest</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/spring/servlets/rest-servlet.xml</param-value>
    </init-param>       
    <load-on-startup>1</load-on-startup>
</servlet>

You can add a no-arg constructor to your @Controller class and put a breakpoint in, look at the stack trace and identify in which context it is being initialized.


Note that AnnotationMethodHandlerAdapter has been deprecated in Spring 3.2. Consider using <mvc:annotation-driven> to configure your MVC environment.

Upvotes: 2

Related Questions