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?
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.