spring boot通過@Bean注解定義一個Controller


功能需求

  1. 提供一個公共的jar包給其他業務模塊依賴,需要在這個公共的jar中暴露一個restful API

  2. 采用spring auto config機制,在公共jar包中定義spring.factories文件,將jar包需要注入到spring容器中的bean定義好,業務模塊依賴后直接使用,不需要額外定義bean,也不需要指定ComponentScan

之前做法:根據spring文檔給的方案調用RequestMappingHandlerMapping的registerMapping方法手動注冊一個mapping,可以不使用@Controller注解就可以追加一個rest 接口,可是spring 5之后,spring推出了spring web flux,而RequestMappingHandlerMapping也分成了是spring webmvc版和spring webflux兩個,我們給定的jar又不能限定業務模塊使用spring web還是spring web flux開發,所以這種方式就不適用了。

解決方式

我們知道,無論是webmvc還是webflux中的RequestMappingHandlerMapping類,都是在afterPropertiesSet方法中查找所有帶有Controller或者RequestMapping注解的類,再把對應類中的帶有RequestMapping注解的方法解析后注冊到對應的RequestMappingHandlerMapping中的,其中判斷一個類是否帶有@Controller或@RequestMapping的方法如下

@Override
	protected boolean isHandler(Class<?> beanType) {
		return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
				AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
	}

對應的AnnotatedElementUtils.hasAnnotation方法,最終會調用到AnnotatedElementUtils.searchWithFindSemantics方法,代碼片段如下

  else if (element instanceof Class) {
	Class<?> clazz = (Class<?>) element;
	if (!Annotation.class.isAssignableFrom(clazz)) {
		// Search on interfaces 在實現接口中查找
		for (Class<?> ifc : clazz.getInterfaces()) {
			T result = searchWithFindSemantics(ifc, annotationTypes, annotationName,
					containerType, processor, visited, metaDepth);
			if (result != null) {
				return result;
			}
		}
		// Search on superclass 在父類中查找
		Class<?> superclass = clazz.getSuperclass();
		if (superclass != null && superclass != Object.class) {
			T result = searchWithFindSemantics(superclass, annotationTypes, annotationName,
					containerType, processor, visited, metaDepth);
			if (result != null) {
				return result;
			}
		}
	}
}

發現這個地方查找是否有指定注解時,如果繼承的類或實現的接口有相應的注解也是可以的,利用這個特性,我們可以采用如下思路來實現。

  1. 定義一個標記Controller,里面什么方法也沒有,僅僅追加了一個注解

    @RestController
    public class MarkController {
    
    }
    
  2. 定義具體的Controller繼承這個標記類,注意這個類不需要用RestController注解

public class HelloController extends MarkController {

	@RequestMapping("/hello")
	public String hello() {
		return "hello";
	}

}


3. 在一個Configuration類中用@Bean注解聲明這個類

	```
	@Configuration
	public class BeanConfig {
	
		@Bean
		public HelloController helloController() {
			return new HelloController();
		}
		
	}
	
	```
	
	這樣我們就可以通過@Bean的形式聲明Controller,之后把這個BeanConfig直接追加到spring.factories中,其他模塊依賴這個jar之后,自動就會有一個/hello的接口了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM