Spring裏Controller繼承接口的RequestMapping註解

背景

經常會遇到Controller繼承一個接口的實現方式,接口上的方法會加上RequestMapping的註解,但是在瀏覽器裏請求會發現代理錯誤。

結構圖

  • 首先InitializingBean裏面有個afterPropertiesSet,這個方法會在類注入容器完後調用,具體的可以看doCreateBean()方法裏面。
  • detectHandlerMethods() 這個方法開始找方法與url對應關係,這裏面有個函數式接口MetadataLookup需要注意一下。這個接口的getMappingForMethod()是核心的代碼。

    這個UML畫的有點爛,不過自己看應該沒問題吧

在這裏插入圖片描述

protected void detectHandlerMethods(Object handler) {
		Class<?> handlerType = (handler instanceof String ?
				obtainApplicationContext().getType((String) handler) : handler.getClass());

		if (handlerType != null) {
			Class<?> userType = ClassUtils.getUserClass(handlerType);
			Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
					(MethodIntrospector.MetadataLookup<T>) method -> {
						try {
							//查找這個方法的對應的method 方法 這是個回調接口 請注意一下
							return getMappingForMethod(method, userType);
						}
						catch (Throwable ex) {
							throw new IllegalStateException("Invalid mapping on handler class [" +
									userType.getName() + "]: " + method, ex);
						}
					});
			if (logger.isTraceEnabled()) {
				logger.trace(formatMappings(userType, methods));
			}
			methods.forEach((method, mapping) -> {
				Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
				registerHandlerMethod(handler, invocableMethod, mapping);
			});
		}
	}

繼承圖:
在這裏插入圖片描述

  • 主要看RequestMappingHandlerMapping裏的getMappingForMethod方法裏面的代碼,裏面有一個獲取類上的所有接口,然後再找這個方法的具體的對應的註解。

備註

  • 在實驗的時候發現@Inherited的註解,只能放在接口上,不能放在類上,這個JDK的註釋上面已經介紹了
  • 在idea的run configuration 的vm 參數裏插入下面的代碼,我們生成運行後生成的動態代理文件
## 這個是JDK的動態代理 生成的文件是$Proxy+數字
-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true
## 這個是生成CGLIB的代理類的具體文件
-Dcglib.debugLocation=D:\\\\code
  • 當我們的Controller類繼承一個接口,那麼這個接口會用JDK的代理生成文件,此時如果RequestMapping放在接口的函數裏,會出現類似這樣的錯誤:

    The mapped controller method class ‘cController’ is not an instance of the actual controller bean instance ‘com.sun.proxy.$Proxy’.If the controller requires proxying (e.g. due to @Transactional), please use class-based proxying

    1. 這個主要要改變代理類生成的方式,我們要用CGlib代理就沒有問題。
      @Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
      
    2. 這個原因就是見這個地方:意思就是隻能基於類的Jdk動態代理,不能基於接口的Jdk動態代理,那爲啥呢,其實我也沒明白。 https://reachmnadeem.wordpress.com/2019/04/11/spring-controllers-implementing-interfaces/
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章