Search code examples
javasessionspring-mvcspring-annotations

Spring MVC, Scoped Controller using Class-Based Proxy: 'There is already scopedTarget bean method.'


I'm creating a visual editor Web app using Spring MVC.

The visualizaton has some properties:

public class VisualizationProperties {
    double strokeWidth = 1;
    Rectangle selectedArea;
}

Every browser session should have one, so I define it as a session-scoped bean:

@Component
@Scope("session")
public class VisualizationProperties {
    ...

Since I want to access it from the service...

@Service
public class VisualizationService {

    @Resource
    private VisualizationProperties properties;

    public void createVisualization () {
        //create visualization using properties
        ...
    }
}

...I define access through a proxy:

@Component
@Scope(value="session", proxyMode=ScopedProxyMode.TARGET_CLASS)
public class VisualizationProperties {
    ...

make the members private and add getters + setters to it.

Perfect so far.

Now I want the client to be able to read and update the properties.

So I change the bean to become a controller and add methods to access itself:

@Controller
@Scope(value="session", proxyMode=ScopedProxyMode.TARGET_CLASS)
public class VisualizationProperties {

    private double strokeWidth = 1;
    private Rectangle selectedArea;

    ... //getters + setters

    @RequestMapping(value="/properties", method=RequestMethod.GET)
    public @ResponseBody VisualizationProperties getProperties () {
        return this;
    }

    @RequestMapping(value="/properties", method=RequestMethod.POST)
    public @ResponseBody void setProperties (@RequestBody VisualizationProperties newProperties) {
        this.strokeWidth  = newProperties.strokeWidth;
        this.selectedArea = newProperties.selectedArea;
    }

}

Causes the following exception on start-up:

IllegalStateException: Ambiguous mapping found.

Cannot map 'visualizationProperties' bean method getProperties() to {[/properties],methods=[GET],...}:

There is already 'scopedTarget.visualizationProperties' bean method.

Why is that?


Solution

  • The issue here is two-fold. First you're annotating a class that is already annotated with @Controller.

    @Controller
    @Scope(value="session", proxyMode=ScopedProxyMode.TARGET_CLASS)
    public class VisualizationProperties {
    

    Second, you are using scoped proxies. In this case, Spring will register two bean definitions. One with the name visualizationProperties for the actual bean and one with the name scopedTarget.visualizationProperties which will act as a FactoryBean for creating proxies.

    The handler method registration process (for @Controller beans and @RequestMapping methods) works by finding all bean names in the context, finding their type, scanning the methods of that type and registering those methods if they are annotated with @RequestMapping.

    Since Spring resolves both visualizationProperties and scopedTarget.visualizationProperties bean names as beans of type VisualizationProperties which has @RequestMapping annotated methods, it will try to register both, failing on the second because of mapping clashes (you can't have two handler mapped to the same request).

    My suggested solution is to refactor and create a dedicated @Controller class (that isn't proxied) that delegates to a session scoped VisualizationProperties bean.