spring boot經過@Bean註解定義一個Controller

功能需求

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

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

以前作法:根據spring文檔給的方案調用RequestMappingHandlerMapping的registerMapping方法手動註冊一個mapping,能夠不使用@Controller註解就能夠追加一個rest 接口,但是spring 5以後,spring推出了spring web flux,而RequestMappingHandlerMapping也分紅了是spring webmvc版和spring webflux兩個,咱們給定的jar又不能限定業務模塊使用spring web仍是spring web flux開發,因此這種方式就不適用了。restful

解決方式

咱們知道,不管是webmvc仍是webflux中的RequestMappingHandlerMapping類,都是在afterPropertiesSet方法中查找全部帶有Controller或者RequestMapping註解的類,再把對應類中的帶有RequestMapping註解的方法解析後註冊到對應的RequestMappingHandlerMapping中的,其中判斷一個類是否帶有@Controller或@RequestMapping的方法以下mvc

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

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

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;
            }
        }
    }
}

發現這個地方查找是否有指定註解時,若是繼承的類或實現的接口有相應的註解也是能夠的,利用這個特性,咱們能夠採用以下思路來實現。ide

  1. 定義一個標記Controller,裏面什麼方法也沒有,僅僅追加了一個註解rest

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

    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的接口了。接口

相關文章
相關標籤/搜索