以一個簡單的例子說明一下camel的運行原理,例子自己很簡單,目的就是將一個目錄下的文件搬運到另外一個文件夾,處理器只是將文件(限於文本文件)的內容打印到控制檯,首先代碼以下:apache
public static void main(String[] args) throws Exception { 緩存 //建立Camel上下文 ide DefaultCamelContext camelContext = new DefaultCamelContext(); ui //添加一個路由,參數爲路由建造者 this camelContext.addRoutes(new RouteBuilder() { spa @Override 線程 public void configure() throws Exception { debug this.from("file:D:/temp/in").process(new Processor() { component @Override 對象 public void process(Exchange exchange) throws Exception { GenericFile<File> gf = exchange.getIn().getBody(GenericFile.class); File file = gf.getFile(); PrintStream ps = new PrintStream(System.out); BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(file))); String line = null; while((line=br.readLine())!=null) { ps.println(line); } ps.close(); br.close(); } }).to("file:D:/temp/out"); } }); //啓動上下文 camelContext.start(); //防止主線程退出 Object object = new Object(); synchronized (object) { object.wait(); } } |
對於camel來講,其原理核心的部分主要包含路由信息的構建、組件查找、和路由啓動過程三方面,下面結合以上簡單例子對三個方面來對camel的源碼進行分析:
其實這裏說的路由構建實際上是構建路由定義,對應Camel中的RouteDefinition類,一個RouteDefinition對象規定了或者說指定了一個消息從哪裏產生,中間要通過什麼樣的處理,最後路由到什麼地方。RouteDefinition由RouteBuilder進行構建,再具體點就是調用RouteBuilder的configure()方法,構建完成後再添加到CamelContext中,那麼該路由定義就能夠運行了。RouteBuilder的configure()方法是一個抽象方法,因此該方法要由開發者進行實現,其實就是在進行路由定義的構建過程。
首先,調用RouteBuilder的from方法獲取RouteDefinition,而且根據from傳入的參數將一個FromDefinition類型的實例設置給RouteDefinition的inputs(輸入)列表,而後調用RouteDefinition的process()和to()方法,分別生成ProcessDefinition和ToDefinition對象,由於ProcessDefinition和ToDefinition都繼承自ProcessorDefinition,因此均可以看出輸出放到RouteDefinition的outputs(輸出)列表中,至此,整個路由定義就構建完成了,其實該路由定義最重要的就是輸入與輸出,輸入與輸出均可以有多個,默認狀況下,在路由運行後,Camel會依賴調用這些輸出處理器並最終將消息路由到指定目的地。
Camel的運行是由組件(component)進行組織的,咱們前面的調用from()方法傳個字符串,camel是要根據uri來查找到對應的組件,即要維護uri到組件之間的映射關係,查找組件的過程是調用DefaultCamelContext中的getComponent(String name)方法來完成的,是在DefaultCamelContext啓動的時候調用,獲取相應的組件,生成對應的endPoint,組件的查找是從DefaultCamelContext上下文緩存的components中去找,若是沒有找到,就會去根據uri前綴生成一個component,生成的代碼大體以下:
// 先在註冊表中查找 Object bean = null; try { bean = context.getRegistry().lookupByName(name); getLog().debug("Found component: {} in registry: {}", name, bean); } catch (Exception e) { getLog().debug("Ignored error looking up bean: " + name, e); } //省略了一部分代碼.... // 註冊表中沒有時,使用Component工廠建立一個 Class<?> type; try { type = findComponent(name, context); if (type == null) { // not found return null; } } catch (NoFactoryAvailableException e) { return null; } catch (Exception e) { throw new IllegalArgumentException("Invalid URI, no Component registered for scheme: " + name, e); } // 建立Component if (Component.class.isAssignableFrom(type)) { return (Component) context.getInjector().newInstance(type); } else { throw new IllegalArgumentException("Type is not a Component implementation. Found: " + type.getName()); } |
其中的findComponent方法是在camel包中默認路徑META-INF/services/org/apache/camel/component/+uri前綴名
找對應的Properties(不帶.properties後綴)文件,文件中有Component組件實例化的類名,找到後經過反射機制生成一個component組件,放到緩存中。咱們本身也能夠按照這個規則來拓展本身的component。
Camel啓動原理圖
圖一
圖二