clojure java.jdbc_Clojure驱动的Web开发
Clojure是運(yùn)行在JVM之上的Lisp方言,提供了強(qiáng)大的函數(shù)式編程的支持。由于Java語(yǔ)言進(jìn)化的緩慢,用Java編寫(xiě)大型應(yīng)用程序時(shí),代碼往往十分臃腫,許多語(yǔ)言如Groovy、Scala等都把自身設(shè)計(jì)為一種可替代Java的,能直接編譯為JVM字節(jié)碼的語(yǔ)言。Clojure則提供了Lisp在JVM的實(shí)現(xiàn)。
Clojure經(jīng)過(guò)幾年的發(fā)展,其社區(qū)已經(jīng)逐漸成熟,有許多活躍的開(kāi)源項(xiàng)目,足以完成大型應(yīng)用程序的開(kāi)發(fā)。由Twitter開(kāi)源的著名的分布式并行計(jì)算框架Storm就是用Clojure編寫(xiě)的。
Clojure提供了對(duì)Java的互操作調(diào)用,對(duì)于那些必須在JVM上繼續(xù)開(kāi)發(fā)的項(xiàng)目,Clojure可以利用Java遺留代碼。對(duì)大多數(shù)基于SSH(Spring Struts Hibernate)的Java項(xiàng)目來(lái)說(shuō),是時(shí)候扔掉它們,用Clojure以一種全新的模式來(lái)進(jìn)行開(kāi)發(fā)了。
本文將簡(jiǎn)要介紹使用Clojure構(gòu)建Web應(yīng)用程序的開(kāi)發(fā)環(huán)境和技術(shù)棧。相比SSH,相同的功能使用Clojure僅需極少的代碼,并且無(wú)需在開(kāi)發(fā)過(guò)程中不斷重啟服務(wù)器,可以極大地提升開(kāi)發(fā)效率。
安裝Clojure開(kāi)發(fā)環(huán)境
由于Clojure運(yùn)行在JVM上,我們只需要準(zhǔn)備好JDK和Java標(biāo)配的Eclipse開(kāi)發(fā)環(huán)境,就可以開(kāi)始Clojure開(kāi)發(fā)了!
我們的開(kāi)發(fā)環(huán)境是:
Java 8 SDK:可以從Oracle官方網(wǎng)站下載最新64位版本;
Eclipse Luna SR1:可以從Eclipse官方網(wǎng)站下載Eclipse IDE for Java Developers最新64位版本。
安裝完JDK后,通過(guò)命令java -version確認(rèn)JDK是否正確安裝以及版本號(hào):
$ java -version
java version "1.8.0_20"
Java(TM) SE Runtime Environment (build 1.8.0_20-b26)
Java HotSpot(TM) 64-Bit Server VM (build 25.20-b23, mixed mode)
Clojure開(kāi)發(fā)環(huán)境可以通過(guò)Eclipse插件形式獲得,Counterclockwise提供了非常完善的Clojure開(kāi)發(fā)支持。
首先運(yùn)行Eclipse,通過(guò)菜單“Help”-“Eclipse Marketplace...”打開(kāi)Eclipse Marketplace,搜索關(guān)鍵字counterclockwise,點(diǎn)擊Install安裝:
安裝完Counterclockwise后需要重啟Eclipse,然后,我們就可以新建一個(gè)Clojure Project了!
選擇菜單“File”-“New”-“Project...”,選擇“Clojure”-“Clojure Project”,填入名稱(chēng)“cljweb”,創(chuàng)建一個(gè)新的Clojure Project:
找到project.clj文件,把:dependencies中的Clojure版本由1.5.1改為最新版1.6.0:
(defproject cljweb "0.1.0-SNAPSHOT"
:description "FIXME: write description"
:url "http://example.com/FIXME"
:license {:name "Eclipse Public License"
:url "http://www.eclipse.org/legal/epl-v10.html"}
:dependencies [[org.clojure/clojure "1.6.0"]])
保存,然后你會(huì)注意到Leiningen會(huì)自動(dòng)編譯整個(gè)工程。
Leiningen是什么
Leiningen是Clojure的項(xiàng)目構(gòu)建工具,類(lèi)似于Maven。事實(shí)上,Leiningen底層完全使用Maven的包管理機(jī)制,只是Leiningen的構(gòu)建腳本不是pom.xml,而是project.clj,它本身就是Clojure代碼。
如果Leiningen沒(méi)有自動(dòng)運(yùn)行,你可以點(diǎn)擊菜單“Project”-“Build Automatically”,勾上后就會(huì)讓Leiningen在源碼改動(dòng)后自動(dòng)構(gòu)建整個(gè)工程。
第一個(gè)Clojure版Hello World
在src目錄下找到自動(dòng)生成的core.clj文件,注意到已經(jīng)生成了如下代碼:
(ns cljweb.core)
(defn foo
"I don't do a whole lot."
[x]
(println x "Hello, World!"))
只需要添加一行代碼,調(diào)用foo函數(shù):
(println (foo "Clojure"))
然后,點(diǎn)擊菜單“Run”-“Run”就可以直接運(yùn)行了:
Leiningen會(huì)啟動(dòng)一個(gè)REPL,并設(shè)置好classpath。第一次REPL啟動(dòng)會(huì)比較慢,原因是JVM的啟動(dòng)速度慢。在REPL中可以看到運(yùn)行結(jié)果。REPL窗口本身還支持直接運(yùn)行Clojure代碼,這樣你可以直接在REPL中測(cè)試代碼,能極大地提高開(kāi)發(fā)效率。
Clojure函數(shù)式編程
Clojure和Java最大的區(qū)別在于Clojure的函數(shù)是頭等公民,并完全支持函數(shù)式編程。Clojure自身提供了一系列內(nèi)置函數(shù),使得編寫(xiě)的代碼簡(jiǎn)潔而高效。
我們隨便寫(xiě)幾個(gè)函數(shù)來(lái)看看:
;; 定義自然數(shù)序列
(defn natuals []
(iterate inc 1))
;; 定義奇數(shù)序列
(defn odds []
(filter odd? (natuals)))
;; 定義偶數(shù)序列
(defn evens []
(filter even? (natuals)))
;; 定義斐波那契數(shù)列
(defn fib []
(defn fib-iter [a b]
(lazy-seq (cons a (fib-iter b (+ a b)))))
(fib-iter 0 1))
這些函數(shù)的特點(diǎn)是擁有Clojure的“惰性計(jì)算”特性,我們可以極其簡(jiǎn)潔地構(gòu)造一個(gè)無(wú)限序列,然后通過(guò)高階函數(shù)做任意操作:
;; 打印前10個(gè)數(shù)
(println (take 10 (natuals)))
(println (take 10 (odds)))
(println (take 10 (evens)))
(println (take 10 (fib)))
;; 打印1x2, 2x3, 3x4...
(println (take 10 (map * (natuals)
(drop 1 (natuals)))))
再識(shí)Clojure
Clojure自身到底是什么?Clojure自身只是一個(gè)clojure.jar文件,它負(fù)責(zé)把Clojure代碼編譯成JVM可以運(yùn)行的.class文件。如果預(yù)先把Clojure代碼編譯為.class,那么運(yùn)行時(shí)也不需要clojure.jar了。
Clojure自身也作為Maven的一個(gè)包,你應(yīng)該可以在用戶(hù)目錄下找到Maven管理的clojure-1.6.0.jar以及源碼:
.m2/repository/org/clojure/clojure/1.6.0/
如果要在命令行運(yùn)行Clojure代碼,需要自己把classpath設(shè)置好,入口函數(shù)是clojure.main,參數(shù)是要運(yùn)行的.clj文件:
$ java -cp ~/.m2/repository/org/clojure/clojure/1.6.0/clojure-1.6.0.jar clojure.main cljweb/core.clj
Clojure: Hello, World!
nil
(1 2 3 4 5 6 7 8 9 10)
(1 3 5 7 9 11 13 15 17 19)
(2 4 6 8 10 12 14 16 18 20)
(0 1 1 2 3 5 8 13 21 34)
(2 6 12 20 30 42 56 72 90 110)
在Eclipse環(huán)境中,Leiningen已經(jīng)幫你設(shè)置好了一切。
訪(fǎng)問(wèn)數(shù)據(jù)庫(kù)
Java提供了標(biāo)準(zhǔn)的JDBC接口訪(fǎng)問(wèn)數(shù)據(jù)庫(kù),Clojure的數(shù)據(jù)庫(kù)接口clojure.java.jdbc是對(duì)Java JDBC的封裝。我們只需要引用clojure.java.jdbc以及對(duì)應(yīng)的數(shù)據(jù)庫(kù)驅(qū)動(dòng),就可以在Clojure代碼中訪(fǎng)問(wèn)數(shù)據(jù)庫(kù)。
clojure.java.jdbc是一個(gè)比較底層的接口。如果要使用DSL的模式來(lái)編寫(xiě)數(shù)據(jù)庫(kù)代碼,類(lèi)似Java的Hibernate,則可以考慮幾個(gè)DSL庫(kù)。我們選擇Korma來(lái)編寫(xiě)訪(fǎng)問(wèn)數(shù)據(jù)庫(kù)的代碼。
由于Clojure是Lisp方言,它繼承了Lisp強(qiáng)大的“代碼即數(shù)據(jù)”的功能,在Clojure代碼中,編寫(xiě)SQL語(yǔ)句對(duì)應(yīng)的DSL十分自然,完全無(wú)需Hibernate復(fù)雜的映射配置。
我們先配置好MySQL數(shù)據(jù)庫(kù),然后創(chuàng)建一個(gè)表來(lái)測(cè)試Clojure代碼:
create table courses (
id varchar(32) not null primary key,
name varchar(50) not null,
price real not null,
online bool not null,
days bigint not null
);
新建一個(gè)db.clj文件,選擇菜單“File”-“New”-“Other...”,選擇“Clojure”-“Clojure Namespace”,填入名稱(chēng)db,就可以創(chuàng)建一個(gè)db.clj文件。
在編寫(xiě)代碼前,我們首先要在project.clj文件中添加依賴(lài)項(xiàng):
[org.clojure/java.jdbc "0.3.6"]
[mysql/mysql-connector-java "5.1.25"]
[korma "0.3.0"]
使用Korma操作數(shù)據(jù)庫(kù)十分簡(jiǎn)單,只需要先引用Korma:
(ns cljweb.db
(:use korma.db
korma.core))
定義數(shù)據(jù)庫(kù)連接的配置信息:
(defdb korma-db (mysql {:db "test",
:host "localhost",
:port 3306,
:user "www",
:password "www"}))
然后定義一下要使用的entity,也就是表名:
(declare courses)
(defentity courses)
現(xiàn)在,就可以對(duì)數(shù)據(jù)庫(kù)進(jìn)行操作了。插入一條記錄:
(insert courses
(values { :id "s-201", :name "SQL", :price 99.9, :online false, :days 30 })))
使用Clojure內(nèi)置的map類(lèi)型,十分直觀(guān)。
查詢(xún)語(yǔ)句通過(guò)select宏實(shí)現(xiàn)了SQL DSL到Clojure代碼的自然映射:
(select courses
(where {:online false})
(order :name :asc)))
這完全得益于Lisp的S表達(dá)式的威力,既不需要直接拼湊SQL,也不需要重新發(fā)明類(lèi)似HQL的語(yǔ)法。
利用Korma的提供的sql-only和dry-run,可以打印出生成的SQL語(yǔ)句,但實(shí)際并不執(zhí)行。
Web接口
傳統(tǒng)的JavaEE使用Servlet接口來(lái)劃分服務(wù)器和應(yīng)用程序的界限,應(yīng)用程序負(fù)責(zé)提供實(shí)現(xiàn)Servlet接口的類(lèi),服務(wù)器負(fù)責(zé)處理HTTP連接并轉(zhuǎn)換為Servlet接口所需的HttpServletRequest和HttpServletResponse。Servlet接口定義十分復(fù)雜,再加上Filter,所需的XML配置復(fù)雜度很高,而且測(cè)試?yán)щy。
Clojure的Web實(shí)現(xiàn)最常用的是Ring。Ring的設(shè)計(jì)來(lái)自Python的WSGI和Ruby的Rack,以WSGI為例,其接口設(shè)計(jì)十分簡(jiǎn)單,僅一個(gè)函數(shù):
def application(env, start_response)
其中env是一個(gè)字典,start_response是響應(yīng)函數(shù)。由于WSGI接口本身是純函數(shù),因此無(wú)需Filter接口就可以通過(guò)高階函數(shù)對(duì)其包裝,完成所有Filter的功能。
Ring在內(nèi)部把Java標(biāo)準(zhǔn)的Servlet接口轉(zhuǎn)換為簡(jiǎn)單的函數(shù)接口:
(defn handler [request]
{:status 200
:headers {"Content-Type" "text/html"}
:body "Hello World"})
上述函數(shù)就完成了Servlet實(shí)現(xiàn)類(lèi)的功能。其中request是一個(gè)map,返回值也是一個(gè)map,由:status、:headers和:body關(guān)鍵字指定HTTP的返回碼、頭和內(nèi)容。
把一系列handler函數(shù)串起來(lái)就形成了一個(gè)處理鏈,每個(gè)鏈都可以對(duì)輸入和輸出進(jìn)行處理,鏈的最后一個(gè)處理函數(shù)負(fù)責(zé)根據(jù)URL進(jìn)行路由,這樣,完整的Web處理?xiàng)>涂梢詷?gòu)造出來(lái)。
Ring把handler稱(chēng)為middleware,middleware基于Clojure的函數(shù)式編程模型,利用Clojure自帶的->宏就可以直接串起來(lái)。
一個(gè)完整的Web程序只需要定義一個(gè)handler函數(shù),并啟動(dòng)Ring內(nèi)置的Jetty服務(wù)器即可:
;; hello.clj
(ns cljweb.hello
(:require [ring.adapter.jetty :as jetty]))
(defn handler [request]
{:status 200,
:headers {"Content-Type" "text/html"}
:body "
Hello, world.
"})(defn start-server []
(jetty/run-jetty handler {:host "localhost",
:port 3000}))
(start-server)
運(yùn)行hello.clj,將啟動(dòng)內(nèi)置的Jetty服務(wù)器,然后,打開(kāi)瀏覽器,在地址欄輸入http://localhost:3000/就可以看到響應(yīng):
handler函數(shù)傳入的request是一個(gè)map,如果你想查看request的內(nèi)容,可以簡(jiǎn)單地返回:
(defn handler [request]
{:status 200,
:headers {"Content-Type" "text/html"}
:body (str request)})
URL路由
要處理不同的URL請(qǐng)求,我們就需要在handler函數(shù)內(nèi)根據(jù)URL進(jìn)行路由。Ring本身只負(fù)責(zé)處理底層的handler函數(shù),更高級(jí)的URL路由功能由上層框架完成。
Compojure就是輕量級(jí)的URL路由框架,我們要首先添加Compojure的依賴(lài)項(xiàng):
[compojure "1.2.1"]
Compojure提供了defroutes宏來(lái)創(chuàng)建handler,它接收一系列URL映射,然后把它們組裝到handler函數(shù)內(nèi)部,并根據(jù)URL路由。一個(gè)簡(jiǎn)單的handler定義如下:
(ns cljweb.routes
(:use [compojure.core]
[compojure.route :only [not-found]]
[ring.adapter.jetty :as jetty]))
(defroutes app-routes
(GET "/" [] "
Index page
")(GET "/learn/:lang" [lang] (str "
Learn " lang "
"))(not-found "
page not found!
"));; start web server
(defn start-server []
(jetty/run-jetty app-routes {:host "localhost",
:port 3000}))
(start-server)
該defroutes創(chuàng)建了3個(gè)URL映射:
GET /處理首頁(yè)的URL請(qǐng)求,它僅僅簡(jiǎn)單地返回一個(gè)字符串;
GET /learn/:lang處理符合/learn/:lang這種構(gòu)造的URL,并且將URL中的參數(shù)自動(dòng)作為參數(shù)傳遞進(jìn)來(lái),如果我們輸入http://localhost:3000/learn/clojure,將得到如下響應(yīng):
not-found處理任何未匹配到的URL,例如:
使用模板
復(fù)雜的HTML通常不可能在程序中拼接字符串完成,而是通過(guò)模板來(lái)渲染出HTML。模板的作用是創(chuàng)建一個(gè)使用變量占位符和簡(jiǎn)單的控制語(yǔ)句的HTML,在程序運(yùn)行過(guò)程中,根據(jù)傳入的model——通常是一個(gè)map,替換掉變量,執(zhí)行一些控制語(yǔ)句,最終得到HTML。
已經(jīng)有好幾種基于Clojure創(chuàng)建的模板引擎,但是基于Django模板設(shè)計(jì)思想的Selmer最適合HTML開(kāi)發(fā)。
Selmer的使用十分簡(jiǎn)單。首先添加依賴(lài):
[selmer "0.7.2"]
然后創(chuàng)建一個(gè)cljweb.templ的namespace來(lái)測(cè)試Selmer:
(ns cljweb.templ)
(use 'selmer.parser)
(selmer.parser/cache-off!)
(selmer.parser/set-resource-path! (clojure.java.io/resource "templates"))
(render-file "test.html" {:title "Selmer Template",
:name "Michael",
:now (new java.util.Date)})
在開(kāi)發(fā)階段,用cache-off!關(guān)掉緩存,以便使得模板的改動(dòng)可以立刻更新。
使用set-resource-path!設(shè)定模板的查找路徑。我們把模板的根目錄設(shè)置為(clojure.java.io/resource "templates"),因此,模板文件的存放位置必須在目錄resources/templates下:
創(chuàng)建一個(gè)test.html模板:
{{ title }}Welcome, {{ name }}
Time: {{ now|date:"yyyy-MM-dd HH:mm" }}
運(yùn)行代碼,可以看到REPL打印出了render-file函數(shù)返回的結(jié)果:
配置middleware
Compojure可以方便地定義URL路由,但是,完整的Web應(yīng)用程序還需要能解析URL參數(shù)、處理Cookie、返回JSON類(lèi)型等,這些任務(wù)都可以通過(guò)Ring自帶的middleware完成。
我們創(chuàng)建一個(gè)cljweb.web的namespace作為入口,Ring自帶的middleware都提供wrap函數(shù),可以用Clojure的->宏把它們串聯(lián)起來(lái):
(ns cljweb.web
(:require
[ring.adapter.jetty :as jetty]
[ring.middleware.cookies :as cookies]
[ring.middleware.params :as params]
[ring.middleware.keyword-params :as keyword-params]
[ring.middleware.json :as json]
[ring.middleware.resource :as resource]
[ring.middleware.stacktrace :as stacktrace]
[cljweb.templating :as templating]
[cljweb.urlhandlers :as urlhandlers]))
(def app
(-> urlhandlers/app-routes
(resource/wrap-resource (clojure.java.io/resource "resources")) ;; static resource
templating/wrap-template-response ;; render template
json/wrap-json-response ;; render json
json/wrap-json-body ;; request json
stacktrace/wrap-stacktrace-web ;; wrap-stacktrace-log
keyword-params/wrap-keyword-params ;; convert parameter name to keyword
cookies/wrap-cookies ;; get / set cookies
params/wrap-params ;; query string and url-encoded form
))
每個(gè)middleware只負(fù)責(zé)一個(gè)任務(wù),每個(gè)middleware接受request,返回response,它們都有機(jī)會(huì)修改request和response,因此順序很重要:
例如,cookies負(fù)責(zé)把request的Cookie字符串解析為map并以關(guān)鍵字:cookies存儲(chǔ)到request中,后續(xù)的處理程序可以直接從request拿到:cookies:
同時(shí),如果在response中找到了:cookies,就把它轉(zhuǎn)換為Cookie字符串并放入response的:headers中,服務(wù)器就會(huì)在HTTP響應(yīng)中加上Set-Cookie的頭:
Ring沒(méi)有內(nèi)置能渲染Selmer模板的middleware,但是middleware不過(guò)是一個(gè)簡(jiǎn)單的函數(shù),我們可以自己編寫(xiě)一個(gè)wrap-template-response,它在response中查找:body以及:body所包含的:model和:template,如果找到了,就通過(guò)Selmer渲染模板,并將渲染結(jié)果作為string放到response的:body中,服務(wù)器就可以讀取response的:body并輸出HTML:
(ns cljweb.templating
(:use ring.util.response
[selmer.parser :as parser]))
(parser/cache-off!)
(parser/set-resource-path! (clojure.java.io/resource "templates"))
(defn- try-render [response]
(let [body (:body response)]
(if (map? body)
(let [[model template] [(:model body) (:template body)]]
(if (and (map? model) (string? template))
(parser/render-file template model))))))
(defn wrap-template-response
[handler]
(fn [request]
(let [response (handler request)]
(let [render-result (try-render response)]
(if (nil? render-result)
response
(let [templ-response (assoc response :body render-result)]
(if (contains? (:headers response) "Content-Type")
templ-response
(content-type templ-response "text/html;charset=utf-8"))))))))
處理REST API
絕大多數(shù)Web應(yīng)用程序都會(huì)選擇REST風(fēng)格的API,使用JSON作為輸入和輸出。在Clojure中,JSON可以直接映射到Clojure的數(shù)據(jù)類(lèi)型map,因此,只需添加處理JSON的相關(guān)middleware就能處理REST。首先添加依賴(lài):
[ring/ring-json "0.3.1"]
在middleware中,添加wrap-json-response和wrap-json-body:
(def app
(-> urlhandlers/app-routes
(resource/wrap-resource (clojure.java.io/resource "resources")) ;; static resource
templating/wrap-template-response ;; render template
json/wrap-json-response ;; render json
json/wrap-json-body ;; request json
stacktrace/wrap-stacktrace-web ;; wrap-stacktrace-log
keyword-params/wrap-keyword-params ;; convert parameter name to keyword
cookies/wrap-cookies ;; get / set cookies
params/wrap-params ;; query string and url-encoded form
))
wrap-json-body如果讀到Content-Type是application/json,就會(huì)把:body從字符串變?yōu)榻馕龊蟮臄?shù)據(jù)格式。wrap-json-response如果讀到:body是一個(gè)map或者vector,就會(huì)把:body序列化為JSON字符串,并重置:body為字符串,同時(shí)添加Content-Type為application/json。
因此,我們?cè)赨RL處理函數(shù)中,如果要返回JSON,只需要返回map,如果要讀取JSON,只需要讀取:body:
(defroutes app-routes
(GET "/rest/courses" [] (response { :courses (get-courses) }))
(POST "/rest/courses" [] (fn [request]
(let [c (:body request)
id (str "c-" (System/currentTimeMillis))]
(create-course! (assoc c :id id, :online true,))
(response (get-course id)))))
(not-found "
page not found!
"))把數(shù)據(jù)庫(kù)操作、模板以及其他的URL處理函數(shù)都包含進(jìn)來(lái),我們就創(chuàng)建好了一個(gè)完整的基于Clojure的Web應(yīng)用程序。
右鍵點(diǎn)擊項(xiàng)目,在彈出菜單選擇“Leiningen”,“Generate Leiningen Command Line”,在彈出的輸入框里:
輸入命令:
lein ring server
將啟動(dòng)Ring內(nèi)置的Jetty服務(wù)器,并自動(dòng)打開(kāi)瀏覽器,定位到http://localhost:3000/:
以這種方式啟動(dòng)服務(wù)器的好處是對(duì)代碼做任何修改,無(wú)需重啟服務(wù)器就可以直接生效,只要在project.clj中加上:
:ring {:handler cljweb.web/app
:auto-reload? true
:auto-refresh? true}
部署
要在服務(wù)器部署Clojure編寫(xiě)的Web應(yīng)用程序,有好幾種方法,一種是用Leiningen命令:
$ lein uberjar
把所有源碼和依賴(lài)項(xiàng)編譯并打包成一個(gè)獨(dú)立的jar包(可能會(huì)很大),打包前需要先編寫(xiě)一個(gè)main函數(shù)并在project.clj中指定:
:main cljweb.web
把這個(gè)jar包上傳到服務(wù)器上就可以直接通過(guò)Java命令運(yùn)行:
$ java -jar cljweb-0.1.0-SNAPSHOT-standalone.jar start
需要加上參數(shù)start是因?yàn)槲覀冊(cè)趍ain函數(shù)中通過(guò)start參數(shù)來(lái)判斷是否啟動(dòng)Jetty服務(wù)器:
(defn -main [& args]
(if (= "start" (first args))
(start-server)))
要以傳統(tǒng)的war包形式部署,可以使用命令:
$ lein ring war
這將創(chuàng)建一個(gè).war文件,部署到標(biāo)準(zhǔn)的JavaEE服務(wù)器上即可。
小結(jié)
Clojure作為一種運(yùn)行在JVM平臺(tái)上的Lisp方言,它既擁有Lisp強(qiáng)大的S表達(dá)式、宏、函數(shù)式編程等特性,又充分利用了JVM這種高度優(yōu)化的虛擬機(jī)平臺(tái),和傳統(tǒng)的JavaEE系統(tǒng)相比,Clojure不僅代碼簡(jiǎn)潔,能極大地提升開(kāi)發(fā)效率,還擁有一種與JavaEE所不同的開(kāi)發(fā)模型。傳統(tǒng)的Java開(kāi)發(fā)人員需要轉(zhuǎn)變固有思維,利用Clojure替代Java,完全可以編寫(xiě)出更簡(jiǎn)單,更易維護(hù)的代碼。
參考資料
Clojure官方網(wǎng)站:了解并下載Clojure的最新版本;
Leiningen官方網(wǎng)站:了解并下載Leiningen的最新版本;
Korma官方網(wǎng)站:獲取Korma源碼并閱讀在線(xiàn)文檔;
Ring官方網(wǎng)站:獲取Ring源碼并閱讀在線(xiàn)文檔;
Compojure官方網(wǎng)站:獲取Compojure源碼并閱讀在線(xiàn)文檔。
關(guān)于作者
廖雪峰,精通Java/Objective-C/Python/C#/Ruby/Lisp,獨(dú)立iOS開(kāi)發(fā)者,對(duì)開(kāi)源框架有深入研究,著有《Spring 2.0核心技術(shù)與最佳實(shí)踐》一書(shū),其官方博客是http://www.liaoxuefeng.com/,官方微博是@廖雪峰。
總結(jié)
以上是生活随笔為你收集整理的clojure java.jdbc_Clojure驱动的Web开发的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: java时间戳转calender_Jav
- 下一篇: 我的世界连锁挖矿下载JAVA_我的世界1