Spring AOP造成的@Controller注册失败
来源:互联网 发布:湖南第五届网络文化节 编辑:程序博客网 时间:2024/06/02 12:51
昨天在重构项目时 ,调整了原有Controller的路径 ,并抽取了部分方法形成AbstractController ,所有业务模块的Controller都继承它 .然后启动发现几乎所有移动过的Controller都失效了.
主要对象
- ControllerLogInterceptor : aop拦截器 ,拦截目标路径下所有Controller
@Pointcut("execution(public * com.eddy..*.*Controller.*(..))")public void handleController() {}
- AppController : 一个接口
public interface AppController { String getAppName();}
- AbstractController : 抽象的Controller ,包含一些公用方法 ;实现了AppController接口
@Slf4jpublic abstract class AbstractController implements AppController { @Autowired protected HttpSession session; @Override public String getAppName() { return OSC_APPNAME; }}
- ContactController : 业务逻辑的Controller
@RestController@RequestMapping("/eddy/contact")public class ContactController extends AbstractOSCController {...}
原因分析
- 因为是移动路径过后出现的 ,首先考虑路径影响
- 使用的SpringBoot ,全注解配置的Controller ,路径并不会影响Bean的解析
- 同路径下某一Controller可用 ,该Controller没有继承AbstractController ,继承了另一个类
- 考虑AbstractController的影响
- 之前也是使用过继承Controller的做法 ,没有出现问题
- 这个类也在AOP的匹配范围内
- 修改了AbstractController->AbstractAction ,依旧无效
- AOP主要是代理原有Bean ,Abstract不会生成Spring Bean ,不会产生作用
- 终极大招 ,源码断点:
- 打印Debug Log : ContactController注册为Bean ,但没有扫描到mapping路径
- 断点查看Spring如何扫描Controller并添加mapping的
RequestMapping
通过日志 ,找到扫描Mapping的目标类RequestMappingHandlerMapping (父对象AbstractHandlerMethodMapping#initHandlerMethods方法)
//AbstractHandlerMethodMapping.classprotected 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@Overrideprotected 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部分的代码也很重要 ,我不希望丢掉.同时我发现
* 单一的Controller(无继承关系) 和 AOP 工作的很好
* 继承自其他类的Controller也工作的很好
那么 ,Spring的AOP应该是不会破坏Bean的类型判断的 : 他们的类型都是 com.eddy.ContactController$Spring..Cglib...
这样的 ,是能够通过SpringMVC的检查的
结论
最后的不同就在于那个简单的接口…
* Spring默认使用JDK ,对接口进行动态代理 : ContactController extends AbstractController implement AppController ,所以将与接口有关的类都代理成了Proxy .
* 而继承自单一类/没有接口的 ,使用cglib代理 ,就不存在这个问题 ,他依旧保留了原有class的信息.
增加配置 spring.aop.proxy-target-class=true ,默认统一使用cglib即可
- Spring AOP造成的@Controller注册失败
- Spring AOP拦截对Controller的请求时的配置失败
- Spring AOP拦截对Controller的请求时的配置失败
- Spring MVC 注解下Controller 的AOP
- Spring MVC 注解下Controller 的AOP
- Spring的注解Controller中使用AOP
- spring的controller中使用aop
- 使用Spring AOP预处理Controller的参数
- Spring AOP 对Spring MVC的Controller切面拦截不起作用
- Spring Aop 声明注册
- Spring AOP代理controller类
- spring Aop拦截controller方法
- Spring AOP代理Controller层
- spring aop学习--拦截controller
- AOP在spring的controller里无效的问题
- spring aop 如何切面到mvc 的controller
- Spring MVC下对Controller 的AOP切面
- spring aop 到mvc 的controller配置不起作用
- 事务
- ORA-01123: cannot start online backup; media recovery not enabled
- API汇集
- 设计模式大总结(四):原型模式
- 4、redis的五大数据类型
- Spring AOP造成的@Controller注册失败
- Httpclint 4.x 的用法
- jq/js–ajax局部刷新的陷阱
- 蓝桥杯 真题 打印十字图 java
- Java获取一个文件夹内的所有文件(包括所有子文件夹内的)
- 1030. 完美数列
- 旧版本版本的全志R16平台的tinav2.1的系统打开softAP(分色排版)
- HTML简单笔记
- 注册热键和快捷键