Spring AOP造成的@Controller注册失败

2018-01-07  本文已影响0人  啊冬啊冬

昨天在重构项目时 ,抽取了Controller的公共方法到AbstractController中, 调整了原有Controller的路径 (包名) . 然后启动时发现几乎所有移动过包的Controller都失效了.

主要对象

@Pointcut("execution(public * com.eddy..*.*Controller.*(..))")
public void handleController() {
}
public interface AppController {
    String getAppName();
}
@Slf4j
public abstract class AbstractController implements AppController {
    @Autowired
    protected HttpSession session;

    @Override
    public String getAppName() {
        return OSC_APPNAME;
    }
}
@RestController
@RequestMapping("/eddy/contact")
public class ContactController extends AbstractOSCController {
...
}

原因分析

RequestMapping

通过日志 ,找到扫描Mapping的目标类RequestMappingHandlerMapping (父对象AbstractHandlerMethodMapping#initHandlerMethods方法)

//AbstractHandlerMethodMapping.class
protected void initHandlerMethods() {
        if (logger.isDebugEnabled()) {
            logger.debug("Looking for request mappings in application context: " + getApplicationContext());
        }
        String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
                BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
                getApplicationContext().getBeanNamesForType(Object.class));

        for (String beanName : beanNames) {
            if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
                Class<?> beanType = null;
                try {
                    beanType = getApplicationContext().getType(beanName);
                }
                catch (Throwable ex) {
                    // An unresolvable bean type, probably from a lazy bean - let's ignore it.
                    if (logger.isDebugEnabled()) {
                        logger.debug("Could not resolve target class for bean with name '" + beanName + "'", ex);
                    }
                }
                if (beanType != null && isHandler(beanType)) {
                    detectHandlerMethods(beanName);
                }
            }
        }
        handlerMethodsInitialized(getHandlerMethods());
    }

可以看到 ,通过循环已加载的Bean ,然后通过相应Handler (这里是RequestMappingHandler) 判断是否应该处理.

//RequestMappingHandlerMapping.class
@Override
protected boolean isHandler(Class<?> beanType) {
    //判断对应class类是否 有Controller注解或继承自Controller注解 (RestController) 
        return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
                AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
    }

这里断点调试后 ,发现ContactController直接跳过 ,它没有@Controller注解!!!! 但代码里确实进行了配置 ,在经过多次重编译过后 ... 发现ContactController对应Bean的classType是 com.sun.Proxy...

也就是说SpringAOP代理将这个Bean本有的信息抹除了 ,所以我修改了SpringAOP的匹配路径 ,果然Controller可用了.

但是AOP部分的代码也很重要 ,我不希望丢掉.同时我发现

结论

最后的不同就在于那个简单的接口...

增加配置 spring.aop.proxy-target-class=true ,默认统一使用cglib即可

上一篇 下一篇

猜你喜欢

热点阅读