<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    神奇好望角 The Magical Cape of Good Hope

    庸人不必自擾,智者何需千慮?
    posts - 26, comments - 50, trackbacks - 0, articles - 11
      BlogJava :: 首頁 ::  :: 聯(lián)系 :: 聚合  :: 管理

    JAX-RS(JSR 311 - Java™ API for RESTful Web Services,用于 REST 風(fēng)格的 Web 服務(wù)的 Java™ API)是 Java EE 6 規(guī)范的一部分,其目標(biāo)在于簡化和標(biāo)準(zhǔn)化用 Java 開發(fā) REST 風(fēng)格的 Web 服務(wù)。雖然 Java EE 6 剛出爐的時候,樓主也從頭到尾看過這份規(guī)范,但苦于沒有實際的項目練手,看過又忘了,現(xiàn)在最多算達(dá)到大成傻逼的境界。這次邊看邊寫,期望完成后至少能破入小成牛逼。先從 REST 本身開始。


    REST(REpresentational State Transfer,代表性狀態(tài)傳輸)自稱是一種風(fēng)格而非標(biāo)準(zhǔn),這在樓主看來有炒作的嫌疑。如果僅僅是一種風(fēng)格,那么不同的框架如何兼容?所以才有 JAX-RS 的誕生。REST 最大的貢獻(xiàn)是帶來了 HTTP 協(xié)議的復(fù)興。為什么叫復(fù)興呢?本來 HTTP 的功能挺豐富的,可惜長期以來只被用作傳輸數(shù)據(jù),大好青年被埋沒了。樓主記得剛開始學(xué) Servlet 的時候,一向是把 doGetdoPost 兩個方法一視同仁的,因為書上這么教,很多 Web 框架也這么搞,以至于弄了很久才搞清楚 GETPOST 是兩種不同的請求。現(xiàn)在 REST 拍磚說道,HTTP 早就定義好了一堆操作,以前一直被混淆使用,現(xiàn)在應(yīng)該重新賦予它們本來的意義了,而且如果充分發(fā)揮 HTTP 的功能,完全能夠勝任分布式應(yīng)用的開發(fā)(傳說中的 SOA)。


    SOA 的理念在于將系統(tǒng)設(shè)計為一系列可重用的、解耦的、分布式的服務(wù)。這也不是新鮮玩意兒了,早期有 CORBA,稍晚有 SOAP 等等。REST 作為后起之秀,能夠快速崛起,也必有其非同尋常的特色。下面一一列舉。

    可尋址性(Addressability)

    系統(tǒng)中的每個資源都可以通過唯一標(biāo)識符來訪問。小插一句,“標(biāo)識”的正確讀音是 biāozhì。REST 使用 URI(統(tǒng)一資源標(biāo)識符)管理資源的地址。URI 的概念不解釋。一個 URI 可以指向一個或者多個資源。

    統(tǒng)一的受限接口(The Uniform, Constrained Interface)

    實際上強(qiáng)調(diào)了 HTTP 操作的原意。REST 主要使用了 GET、PUT、DELETE、POST、HEAD 和 OPTIONS 這 6 種操作。此處有兩個曾經(jīng)被忽略的 HTTP 概念:冪等(idempotent)和安全(safe)。冪等應(yīng)該是 HTTP 從數(shù)學(xué)借來的一個術(shù)語(原始的數(shù)學(xué)意義樓主也不懂),意味著若干次請求的副作用與單次請求相同,或者根本沒有副作用。GET、PUT、DELETE、HEAD 和 OPTIONS 都是冪等的:GET、HEAD 和 OPTIONS 都是讀操作,容易理解;PUT 用于創(chuàng)建或更新已知 URI 的資源,多次創(chuàng)建或更新同一個資源顯然和一次的效果相同;DELETE 刪除資源,亦然。安全表示操作不會影響服務(wù)器的狀態(tài)。GET、HEAD 和 OPTIONS 是安全的。POST 既不冪等又不安全,因為和 PUT 不同,POST 創(chuàng)建資源的時候并不知道資源的 URI,所以多個 POST 請求將會創(chuàng)建多個資源。

    面向表象(Representation-Oriented)

    表象這個詞有點(diǎn)拗口,傳聞在一個 REST 風(fēng)格的系統(tǒng)中,服務(wù)端和客戶端之間傳輸?shù)倪诉司褪潜硐蟆硐罂梢允羌兾谋尽ML、JSON……或者自編的山寨格式。唉,不就是數(shù)據(jù)么?只不過可以用任意的格式來傳輸,因為 HTTP 正文里面啥都能放。Content-Type 頭用來聲明格式,一般是 MIME(多用途因特網(wǎng)郵件擴(kuò)展),像 text/plaintext/htmlapplication/pdf 這些。MIME 可以帶屬性,例如 text/html; charset=UTF-8

    無態(tài)通信(Communicate Statelessly)

    REST 服務(wù)器只管理資源,而不會像 Web 服務(wù)器一樣記錄客戶的會話狀態(tài),這些應(yīng)該由客戶端來管理,如此就能增強(qiáng) REST 服務(wù)器的伸縮性。此處的客戶端可以是客戶端程序、瀏覽器,甚至一個 Web 應(yīng)用。總之,REST 只負(fù)責(zé)庫管啦!

    HATEOAS

    猛詞砸來了!HATEOAS = Hypermedia As The Engine Of Application State,超媒體作為應(yīng)用狀態(tài)的引擎,怎么看起來比 SaaS(Software as a Service,軟件作為服務(wù))還要嚇人呢?超文本不過是一只紙老虎,超媒體也瞞不過樓主的天眼:超媒體就是是由文字、圖像、圖形、視頻、音頻……鏈成一體的大雜燴!很簡單的一個例子,有些坑爹的電影網(wǎng)站經(jīng)常發(fā)布一些內(nèi)嵌了廣告的電影,播放的時候會彈出廣告窗口,里面很多鏈接,你去點(diǎn)兩下就中招了:這個電影文件就算是超媒體。

    其實這個詞最關(guān)鍵的地方是“狀態(tài)引擎”。例如樓主在去網(wǎng)購,先選了幾個東西,接下來可以干啥呢?可以繼續(xù)選、可以把購物車清空、可以結(jié)賬……樓主可以從現(xiàn)狀“轉(zhuǎn)換”到其他某些狀態(tài),而控制狀態(tài)轉(zhuǎn)換的那個咚咚就被冠名為狀態(tài)引擎。多么聰明的詞匯啊!樓主發(fā)現(xiàn)凡是高手都是造詞磚家呀!用超媒體來控制狀態(tài)轉(zhuǎn)換,就是 HATEOAS:你是要繼續(xù)看電影還是看廣告?看哪個廣告?自己慢慢考慮……


    REST 相比 CORBA、SOAP、WS-* 之流確實獨(dú)樹一幟,但也難逃玩弄概念的嫌疑。記得大學(xué)里講數(shù)據(jù)庫的老師說過:“你們現(xiàn)在學(xué)了這么多理論,其實以后在工作中未必管用。在大街上隨便找一個軟件培訓(xùn)學(xué)校出來的小伙子,他哪兒懂什么第二第三范式啊?但卻能把數(shù)據(jù)庫玩兒得飛轉(zhuǎn)!”

    posted @ 2011-09-16 15:31 蜀山兆孨龘 閱讀(5472) | 評論 (3)編輯 收藏

    考慮兩個具有一對一關(guān)聯(lián)的實體類,受控方除了有一個自己的主鍵,還有一個引用主控方的外鍵。如果主控方和受控方是同生同滅的關(guān)系,換句話說,雙方的一對一關(guān)聯(lián)一旦確立就不可更改,就可以考慮讓雙方共享相同的主鍵,簡化受控方的表結(jié)構(gòu)。下面就讓樓主通過實例來說明如何用 JPA 2.0 來實現(xiàn)這種映射。

    盯著眼前的電腦,樓主想到了一個也許不太貼切的例子:員工和公司配的電腦的關(guān)系。員工的主鍵就是工號。雖然電腦可能被換掉,但電腦實體完全可以用工號做主鍵,只是把電腦配置的詳細(xì)信息改掉而已。此處的難點(diǎn)就在與如何將電腦的主鍵字段同時映射一個員工,請看樓主是怎么一步步推導(dǎo)出來的。

    一開始是最想當(dāng)然的寫法:

            public class Computer implements Serializable {
                @Id
                @OneToOne
                private Employee employee;
                // 此處省略若干行
            }
        

    然而根據(jù)規(guī)范,只有這些類型可以作為主鍵:Java 原始類型(例如 int)、原始包裝類型(例如 Integer)、Stringjava.util.Datejava.sql.Datejava.math.BigDecimaljava.math.BigInteger。所以直接拿 Employee 做主鍵是不行的。順便提一下,也許某些 JPA 實現(xiàn)自己做了擴(kuò)展,使得可以直接拿實體類做主鍵,這已經(jīng)超出了 JPA 規(guī)范的范疇,此處不討論。

    直接映射是行不通了,那有什么間接的方式嗎?這時樓主想起了一個特殊的注解:EmbeddedId。該注解的本意是用于聯(lián)合主鍵,不過變通一下,是否可以將 Employee 包裝進(jìn)一個嵌入式主鍵,然后再將這個嵌入式主鍵作為 Computer 的主鍵以達(dá)到目的?帶著這種想法,樓主有了下面的代碼:

            @Embeddable
            public class ComputerId implements Serializable {
                @OneToOne
                private Employee employee;
                // 此處省略若干行
            }
        
            public class Computer implements Serializable {
                @EmbeddedId
                private ComputerId id;
                // 此處省略若干行
            }
        

    現(xiàn)在又出現(xiàn)了新的問題:JPA 不支持定義在嵌入式主鍵類中的關(guān)聯(lián)映射。好在天無絕人之路,EmbeddedId 的文檔中直接指出,可以使用 MapsId 注解來間接指定嵌入式主鍵類中的關(guān)聯(lián)映射,而且還附帶了一個例子!于是最終的成品就出爐了:

            @Embeddable
            public class ComputerId implements Serializable {
                private String employeeId;
                // 此處省略若干行
            }
        
            public class Computer implements Serializable {
                @EmbeddedId
                private Employee employee;
                @MapsId("employeeId")
                @OneToOne
                private Employee employee;
                // 此處省略若干行
            }
        

    唯一的遺憾是,邏輯上的主控方 Employee 現(xiàn)在不得不成為受控方了,好在可以定義級聯(lián)操作來達(dá)到差不多的效果:

            public class Employee implements Serializable {
                @Id
                private String id;
                @OneToOne(mappedBy = "employee", cascade = CascadeType.ALL)
                private Computer computer;
                // 此處省略若干行
            }
        

    雖然做到了,但確實挺繞的。希望未來版本的 JPA 能直接支持將實體類作為主鍵,樓主個人覺得不是一個技術(shù)問題。

    posted @ 2011-09-13 11:27 蜀山兆孨龘 閱讀(3895) | 評論 (0)編輯 收藏

    JSF 都 2.0 了,尼瑪居然還是無法識別 multipart/form-data(至少參考實現(xiàn) Mojarra 如此),綁定的屬性一個都讀不出來,坑爹啊!!!既然官方不支持,咱就自己搞一個補(bǔ)丁,讓它不從也得從。

    說到底,JSF 的屬性綁定功能不外乎是利用 FacesServlet 幫我們把參數(shù)進(jìn)行轉(zhuǎn)換和校驗,然后拼裝成受管 Bean。而 FacesServlet 必定是通過 HttpServletRequest 的相關(guān)方法來讀取請求參數(shù),因此只需要在 FacesServlet 之前增加一個過濾器,把文本類型的 Part 參數(shù)轉(zhuǎn)換為普通參數(shù)就可以了。至于文件類型的 Part,則可以使用一些第三方工具來綁定,例如使用 PrimeFaces 將文件綁定到 File 對象。下圖是這種思路的流程:

    multipart/form-data 的處理流程

    第二步的過濾器就是給 JSF 打的“補(bǔ)丁”:

            /**
             * 該過濾器幫助 {@link FacesServlet} 識別 {@code multipart/form-data} 格式的 POST 請求。
             */
            @WebFilter("*.xhtml")
            public class MultipartFilter implements Filter {
                @Override
                public void init(FilterConfig filterConfig) throws ServletException {
                }
    
                @Override
                public void doFilter(ServletRequest request, ServletResponse response,
                        FilterChain chain) throws IOException, ServletException {
                    String contentType = request.getContentType();
                    // 判斷請求的格式是否為 multipart/form-data。
                    if (contentType != null && contentType.startsWith("multipart/form-data")) {
                        MultipartRequest req = new MultipartRequest((HttpServletRequest) request);
                        for (Part part : req.getParts()) {
                            // 如果該 Part 的內(nèi)容類型不為 null, 那它是一個文件,忽略。
                            if (part.getContentType() == null) {
                                req.addParameter(part.getName(), decode(part));
                            }
                        }
                        chain.doFilter(req, response);
                    } else {
                        chain.doFilter(request, response);
                    }
                }
    
                @Override
                public void destroy() {
                }
    
                /**
                 * 將 {@link Part} 對象解碼為字符串。
                 */
                private String decode(Part part) throws IOException {
                    try (InputStreamReader in = new InputStreamReader(
                            part.getInputStream(), StandardCharsets.UTF_8)) {
                        char[] buffer = new char[64];
                        int nread = 0;
                        StringBuilder sb = new StringBuilder();
                        while ((nread = in.read(buffer)) != -1) {
                            sb.append(buffer, 0, nread);
                        }
                        return sb.toString();
                    }
                }
    
                /**
                 * {@link HttpServletRequest} 中的請求參數(shù)映射是只讀的,所以自己封裝一個。
                 */
                private static class MultipartRequest extends HttpServletRequestWrapper {
                    private Map<String, String[]> parameters;
    
                    public MultipartRequest(HttpServletRequest request) {
                        super(request);
                        parameters = new HashMap<>();
                    }
    
                    private void addParameter(String name, String value) {
                        String[] oldValues = parameters.get(name);
                        if (oldValues == null) {
                            parameters.put(name, new String[] {value});
                        } else {
                            int size = oldValues.length;
                            String[] values = new String[size + 1];
                            System.arraycopy(oldValues, 0, values, 0, size);
                            values[size] = value;
                            parameters.put(name, values);
                        }
                    }
    
                    @Override
                    public String getParameter(String name) {
                        String[] values = getParameterValues(name);
                        return values == null ? null : values[0];
                    }
    
                    @Override
                    public Map<String, String[]> getParameterMap() {
                        return parameters;
                    }
    
                    @Override
                    public Enumeration<String> getParameterNames() {
                        final Iterator<String> it = parameters.keySet().iterator();
                        return new Enumeration<String>() {
                            @Override
                            public boolean hasMoreElements() {
                                return it.hasNext();
                            }
    
                            @Override
                            public String nextElement() {
                                return it.next();
                            }
                        };
                    }
    
                    @Override
                    public String[] getParameterValues(String name) {
                        return parameters.get(name);
                    }
                }
            }
        

    這兒噴一下,為什么 HttpServletRequest 里面的請求參數(shù)映射是只讀的,非得要通過繼承 HttpServletRequestWrapper 這種蛋疼的彎路來黑?

    posted @ 2011-09-09 11:23 蜀山兆孨龘 閱讀(1959) | 評論 (0)編輯 收藏

    最近閑來無事(樓主確實太懶了),重翻舊賬,搗鼓了下 JPA 2.0,通過不斷地寫代碼和谷歌,又有了一些舊瓶裝新酒的發(fā)現(xiàn)和吐槽。樓主將在這一系列文章中慢慢道來。本次開篇帶來的是兩個模板類:用作實體類基礎(chǔ)框架的 AbstractEntity, 以及實現(xiàn)了對實體的基本 CRUD 操作的 BasicEntityDao

    一個實體類必須實現(xiàn) java.io.Serializable 接口,必須有一個 ID 字段作為主鍵,且最好覆蓋 equalshashCode 方法。因為實體類和數(shù)據(jù)表有對應(yīng)關(guān)系,所以往往根據(jù) ID 來實現(xiàn) equalshashCode。這很自然地可以引出一個模板類,所有的實體類都可以從它繼承:

            /**
             * 該類可作為實體類的模板,其 {@link #equals(Object)} 和 {@link hashCode()} 方法基于主鍵實現(xiàn)。
             * 子類只需要實現(xiàn) {@link #getId()} 方法。
             */
            public abstract class AbstractEntity implements Serializable {
                /**
                 * 返回主鍵。
                 */
                public abstract Object getId();
    
                @Override
                public boolean equals(Object obj) {
                    if (this == obj) {
                        return true;
                    }
                    if (obj == null || getClass() != obj.getClass()) {
                        return false;
                    }
                    return getId() == null ? false
                            : getId().equals(((AbstractEntity) obj).getId());
                }
    
                @Override
                public int hashCode() {
                    return Objects.hashCode(getId());
                }
            }
        

    針對主鍵的類型,AbstractEntity 可以進(jìn)一步擴(kuò)展。例如,可以擴(kuò)展出一個 UuidEntity,它使用隨機(jī)生成的 UUID 作為主鍵:

            @MappedSuperclass
            public class UuidEntity extends AbstractEntity {
                @Id
                private String id;
    
                @Override
                public String getId() {
                    return id;
                }
    
                @PrePersist
                private void generateId() {
                    // 僅在持久化前生成 ID,提升一點(diǎn)性能。
                    id = UUID.randomUUID().toString();
                }
            }
        

    繼續(xù)發(fā)揮想象,讓它支持樂觀鎖:

            @MappedSuperclass
            public class VersionedUuidEntity extends UuidEntity {
                @Version
                private int version;
            }
        

    這兒順便插嘴吐槽下主鍵的類型。用整數(shù)還是 UUID 好呢?這個問題在網(wǎng)上也是爭論紛紛。在樓主看來,兩者各有優(yōu)劣:整數(shù)主鍵性能高,可讀性也好,但會對數(shù)據(jù)遷移,例如合并兩個數(shù)據(jù)庫,造成不小的麻煩,因為可能出現(xiàn)一大堆重復(fù)的主鍵;UUID 性能差些,看起來晃眼,雖然據(jù)說有些數(shù)據(jù)庫針對性地做了優(yōu)化,想來也不大可能優(yōu)于整數(shù),不過好處就是理論上出現(xiàn)重復(fù)主鍵的概率比中彩票還小(福彩除外)。說這么一大堆,其實還是蠻糾結(jié)啊……樓主一般傾向于用 UUID,只要服務(wù)器的配置夠勁,想來不會出現(xiàn)明顯的性能問題。

    接下來說說 BasicEntityDao,它提供了基本的 CRUD 實現(xiàn),可以用來為會話 Bean 做模板:

            /**
             * 提供了對實體進(jìn)行基本 CRUD 操作的實現(xiàn),可作為會話 Bean 的模板。
             */
            public abstract class BasicEntityDao<T> {
                private Class<T> entityClass;
                private String entityClassName;
                private String findAllQuery;
                private String countQuery;
    
                protected BasicEntityDao(Class<T> entityClass) {
                    this.entityClass = Objects.requireNonNull(entityClass);
                    entityClassName = entityClass.getSimpleName();
                    findAllQuery = "select e from " + entityClassName + " e";
                    countQuery = "select count(e) from " + entityClassName + " e";
                }
    
                /**
                 * 返回用于數(shù)據(jù)庫操作的 {@link EntityManager} 實例。
                 */
                protected abstract EntityManager getEntityManager();
    
                public void persist(T entity) {
                    getEntityManager().persist(entity);
                }
    
                public T find(Object id) {
                    return getEntityManager().find(entityClass, id);
                }
    
                public List<T> findAll() {
                    return getEntityManager().createQuery(findAllQuery, entityClass).getResultList();
                }
    
                public List<T> findRange(int first, int max) {
                    return getEntityManager().createQuery(findAllQuery, entityClass)
                            .setFirstResult(first).setMaxResults(max).getResultList();
                }
    
                public long count() {
                    return (Long) getEntityManager().createQuery(countQuery).getSingleResult();
                }
    
                public T merge(T entity) {
                    return getEntityManager().merge(entity);
                }
    
                public void remove(T entity) {
                    getEntityManager().remove(merge(entity));
                }
            }
        

    子類只需要提供 getEntityManager() 的實現(xiàn)即可。假設(shè)樓主要做一個養(yǎng)雞場管理系統(tǒng),對雞圈進(jìn)行操作的會話 Bean 就可以簡單地寫成:

            @Stateless
            public class CoopDao extends BasicEntityDao<Coop> {
                @Persistence
                private EntityManager em;
    
                public CoopDao() {
                    super(Coop.class);
                }
    
                @Override
                protected EntityManager getEntityManager() {
                    return em;
                }
    
                // 更多方法……
            }
        

    posted @ 2011-09-07 17:40 蜀山兆孨龘 閱讀(3570) | 評論 (8)編輯 收藏

         摘要: JSF 2.0 大量采用標(biāo)注,從而使 web/WEB-INF/faces-config.xml 不再必需。本文介紹并比較了三種途徑來定義可從頁面上的 EL 表達(dá)式中引用的受管 Bean。  閱讀全文

    posted @ 2010-05-15 19:10 蜀山兆孨龘 閱讀(4078) | 評論 (0)編輯 收藏

    僅列出標(biāo)題
    共8頁: 上一頁 1 2 3 4 5 6 7 8 下一頁 
    主站蜘蛛池模板: 又粗又大又猛又爽免费视频| 精品免费国产一区二区| 色噜噜AV亚洲色一区二区| 国产亚洲视频在线播放大全| 日韩精品视频免费观看| 亚洲男人的天堂网站| 国产高清视频在线免费观看| 亚洲精品无码久久| 国产男女猛烈无遮挡免费视频网站| 亚洲国产精品免费观看 | 国产三级在线免费观看| 久久亚洲国产成人精品无码区| 深夜A级毛片视频免费| 亚洲偷自拍拍综合网| 久久国产免费一区二区三区| 亚洲黄色在线视频| 成年男女男精品免费视频网站 | 亚洲av永久无码| 亚洲国产成人精品女人久久久| 两个人看的www免费高清| 亚洲男人天堂av| 最近免费中文字幕4| 特级毛片在线大全免费播放| 亚洲精品美女久久777777| 18未年禁止免费观看| 亚洲欧洲av综合色无码| 久久亚洲国产成人影院网站| 免费人成在线观看网站品爱网| 激情亚洲一区国产精品| 四虎永久在线精品免费影视| 99久久免费国产精品热| 亚洲剧情在线观看| 免费在线黄色网址| 免费国产黄网站在线观看视频| 亚洲综合久久精品无码色欲| 国产日产亚洲系列最新| 成人浮力影院免费看| 精品国产免费人成网站| 亚洲一级毛片视频| 久久久亚洲精品蜜桃臀| 国产成人免费在线|