精彩文章免费看

springmvc controller 的继承问题

一、问题
在书写controller时 我们一般会先定义一个baseController,里面设置获取一些通用业务信息,比如获取当前登录用户信息等。
这种情况下的baseController里 ,不会做RequestMapping 也就是web请求不会直接到这个父类,这种继承是不会有任何问题的。

@Component
public class BaseController
 * 获取当前用户对象
 * @return
protected ShiroUser getCurrentUser() {
    return (ShiroUser) SecurityUtils.getSubject().getPrincipal();

当有些业务的controller 也需要被继承呢,类似这种 带有requestMapping 注解

@Controller
@RequestMapping("/misSync")
public class MisSyncController extends BaseController {

如果我们写一个这样的业务类来继承它

@Controller
@RequestMapping("/misSync/")
public class MisSyncZYJController extends MisSyncController {

那么就会有惊喜

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'requestMappingHandlerMapping' defined in class path resource [org/springframework/boot/autoconfigure/web/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]: Invocation of init method failed; nested exception is java.lang.IllegalStateException: Ambiguous mapping. Cannot map 'misSyncZYJController' method 
public com.onlyou.mis.ext.bo.ResponseResultBO com.onlyou.mis.sas.web.MisSyncController.syncCorpInfo(com.onlyou.mis.ext.bo.MisSyncBO)
to {[/misSync/syncCorpInfo.json],methods=[POST]}: There is already 'misSyncController' bean method
public com.onlyou.mis.ext.bo.ResponseResultBO com.onlyou.mis.sas.web.MisSyncController.syncCorpInfo(com.onlyou.mis.ext.bo.MisSyncBO) mapped.
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1583)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:545)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
    at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:207)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.addCandidateEntry(DefaultListableBeanFactory.java:1286)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1255)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeans(DefaultListableBeanFactory.java:1161)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1086)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1056)
    at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:835)
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:741)
    ... 33 common frames omitted
Caused by: java.lang.IllegalStateException: Ambiguous mapping. Cannot map 'misSyncZYJController' method 

简单说 就会告诉你 模糊的映射 也就是无法确定请求分配到哪个controller的方法
容易看出子类MisSyncZYJController 继承了MisSyncController 连带requestMapping 也设为一样,requestMapping 可以理解为请求派发的命名空间,相同的空间里,子类享有父类protected及其以上的访问权限,当容器启动做方法请求映射时,就会发现父类的一般请求方法,全部都没有明确派发地址了。
1、隔离命名空间,这是最简单的,直接把子类的requestMapping 多一级或者换个名称

@Controller
@RequestMapping("/misSync/zyj/")
public class MisSyncZYJController extends MisSyncController {
@Controller
@RequestMapping("/misSyncZYJ/")
public class MisSyncZYJController extends MisSyncController {

2、 放弃继承带有requestMapping的一般业务类,而只继承没有请求派发的一般父类,类似上文提到的baseController
3、带有疑问的不可行方案,技术上讲,如果把父类的方法设置为private(private方法不影响web请求派发),子类是不可访问的,那么我们不改requestMapping,把有问题的业务父类方法设置成private时可以吗,答案是 no

@RequestMapping(value = "/syncOrg.json", method = RequestMethod.POST)
    @ResponseBody
    private ResponseResultBO syncOrg(@RequestBody MisSyncBO misSyncBO) {
Caused by: java.lang.IllegalStateException: Ambiguous mapping. Cannot map 'misSyncZYJController' method 
private com.onlyou.mis.ext.bo.ResponseResultBO com.onlyou.mis.sas.web.MisSyncController.syncOrg(com.onlyou.mis.ext.bo.MisSyncBO)
to {[/misSync/syncOrg.json],methods=[POST]}: There is already 'misSyncController' bean method

看到尽管已经标记为private ,依然还是报模糊的映射规则。
不过话说回来 就算这个方案技术可行,也是不符合主流规范的,纯粹测试,至于原因 有待深究。
如果两个没有关联 或者说并行的controller想用一样的映射空间 比如'/misSync', 可以吗,答案是没问题的。
只是在找请求的时候,本来只要在一个misSync对应的controller里找,现在变成要找其他也标记为misSync的controller,如果一个controller已经非常庞大,命名空间也不能改,确实也可以考虑这样拆分一部分出去。
当然最好还是一个命名空间一个controller,方便跟踪请求了。