幾乎我們所有的項目都有一個持久層,即關系數據庫,文檔存儲或僅XML文件。 通常,您將使用DAO模式在業務對象和數據存儲之間實現抽象接口。
在這篇文章中,但我將解釋另一種可以代替DAO模式使用的模式。 活動記錄模式是一種體系結構模式,它迫使您對模型類實施CRUD操作,因此模型類本身負責保存,刪除和從數據庫加載。
要實現此模式,有許多策略可以遵循,但是對我而言,最好的方法是使用面向方面的編程 ,因為我們仍然保持關注點分離,有利于隔離的單元測試,而不破壞封裝。
面向方面的編程需要將程序邏輯分解為不同的部分。 這些部分被稱為橫切關注點,因為它們“ 跨越 ”程序中的多個抽象。 橫切關注點的示例可以是日志記錄,事務管理器 , 錯誤管理器或拆分大型數據集 。 對于在這里沒有太多秘密的方面的人們來說,要使用它們,您只需創建一個定義建議和切入點的方面,然后就可以執行您的方面了。
我想我們大多數人都使用了上一段中所述的面向方面的編程,但是使用ITD(類型間聲明)功能的人會更少。
類型間聲明提供了一種表達橫切關注點的方法,這些關注點影響了模塊的結構,從而使程序員可以聲明另一個類的成員。
正如我們在我的國家說“ 不好說,但很好理解 “,ITD是從一個方面宣布新的組件類 ( 屬性,方法,注解 )的方式。
AspectJ是Java 的面向方面的擴展。 AspectJ支持ITD ,因此在本文中將使用它。 此外,我建議您安裝AJDT插件,因為它將幫助您開發方面,并快速概述了哪些Java類已被方面化。

如果您不了解什么是ITD ,請不用擔心,這是一個典型的概念示例,最好通過示例來理解。
讓我們從一個簡單的例子開始:
想象一下必須為汽車建模。 您將擁有一個帶有某些屬性的汽車類,在此示例中,三個屬性( vin號,行駛里程和model )就足夠了。
public class Car {public void setVin(String vin) {this.vin = vin;}public String getVin() {return this.vin;}private String vin;public void setMileNum(int mileNum) { this.mileNum = mileNum;}public int getMileNum() {return this.mileNum;}private int mileNum;public void setModel(String model) {this.model = model;}public String getModel() {return this.model;}private String model; }
這是一個具有三個屬性以及它們的getter和setter的POJO 。
現在我們要添加持久層,但是在這種情況下,我們將POJO持久化為 XML文件而不是數據庫。 因此,應將Car對象轉換為XML流。 為此,將使用JAXB批注。 對于那些不知道的人, JAXB允許開發人員將Java類映射到XML表示,反之亦然。
我確信,想到的第一個想法是使用@XmlRootElement注釋Car類(在JAXB中映射根元素的注釋)。 不要那樣做,使用方面 。 您的第一個任務是嘗試維護Car文件盡可能簡單。 要使用ITD添加注釋,很簡單:
public aspect Car_Jaxb {declare @type: Car: @XmlRootElement;
}
使用@type可以公開注釋哪個成員。 在這種情況下,只能上課。 其他方法是@method , @constructor和@field 。 然后元素模式應該被注釋,在這種情況下是Car類,但是您可以使用任何正則表達式,例如o rg.alexsotob .. *。 最后是注釋 。
下一步是使用JAXB類來編組/解組對象。 在此示例中,我使用spring-oxm軟件包,簡要地您將了解原因。 Spring-oxm是spring-core的一部分,其中包含用于處理O / X Mapping的類 。
這個spring模塊為每個受支持的Xml綁定包含一個類。 在我們的情況下, Jaxb2Marshaller用作編組器和解組器。
您可能正在考慮創建一個服務類,在其中注入Jaxb2Marshaller實例。 該服務將包括兩個方法(保存和加載),以Car類作為參數或返回值。 抱歉,這樣做是要實現DAO模式。 讓我們實現Active Record模式方法。 正如您可能會想到的, aspectj可以幫助您避免將概念混入同一源文件中。
讓我們更新以前的方面文件,以便JAXB所需的所有邏輯都在同一文件中。
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.oxm.jaxb.Jaxb2Marshaller;public aspect Car_Jaxb {declare @type: Car: @XmlRootElement;@Autowiredtransient Jaxb2Marshaller Car.marshaller;public void Car.save(OutputStream outputStream) throws IOException {this.marshaller.marshal(this, new StreamResult(outputStream));}public Car Car.load(InputStream inputStream) throws IOException {return (Car)this.marshaller.unmarshal(new StreamSource(inputStream));}}
看到除了注釋Car類,我們還創建了兩個方法和一個帶注釋的屬性。 屬性必須遵循與方法,< 類名 >點(。)和< 屬性名 >相同的規則。 請注意,在這種情況下,屬性是瞬態的,因為不應綁定到XML文件中。
最后一步是在spring上下文文件中配置marshaller 。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:oxm="http://www.springframework.org/schema/oxm"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/oxm http://www.springframework.org/schema/oxm/spring-oxm-3.0.xsdhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"><oxm:jaxb2-marshaller id="marshaller"><oxm:class-to-be-bound name="org.alexsotob.itd.Car"/></oxm:jaxb2-marshaller></beans>
沒有太多秘密。 現在讓我們編寫一個單元測試代碼。
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="/context.xml")
public class CarOxmBehaviour {@Testpublic void shouldSaveCarToXml() throws Exception {//GivenCar car = new Car();car.setMileNum(1000);car.setModel("Ferrari");car.setVin("1M8GDM9AXKP042788"); //From http://en.wikipedia.org/wiki/Vehicle_Identification_Number//WhenByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();car.save(byteArrayOutputStream);//ThenString expectedMessage = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><car><mileNum>1000</mileNum><model>Ferrari</model><vin>1M8GDM9AXKP042788</vin></car>";String xmlMessage = byteArrayOutputStream.toString("UTF-8");assertThat(the(xmlMessage), isEquivalentTo(the(expectedMessage))); }}
用令人驚訝的NullPointerException運行紅色的junit類和BOOM 。 Marshaller是在Spring上下文中創建的,但是沒有注入到Car類中(Car不是由spring 容器管理的,因此無法注入)。 現在,我想您是在告訴自己:“ 我告訴您,服務層會更好,因為它將由Spring進行管理,并且自動裝配功能會完美工作 。” 但是,拭目以待。 如何使用spring-aspects模塊? Spring Aspects包含一個注釋驅動的方面( @Configurable ),允許任何對象的依賴項注入,無論是否受容器控制。 因此,讓我們應用最后兩個更改,應用程序將運行。
首先是創建一個新的aspectj文件,以將Car類注釋為Configurable 。
import org.springframework.beans.factory.annotation.Configurable;public aspect Car_Configurable {declare @type: Car: @Configurable;}
最后修改spring上下文文件以允許@Configurable注釋。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:oxm="http://www.springframework.org/schema/oxm"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/oxm http://www.springframework.org/schema/oxm/spring-oxm-3.0.xsdhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"><oxm:jaxb2-marshaller id="marshaller"><oxm:class-to-be-bound name="org.alexsotob.itd.Car"/></oxm:jaxb2-marshaller><context:spring-configured></context:spring-configured>
</beans>
添加< context:spring-configured > </ context:spring-configured >命名空間就足夠了。 結果,任何時候您實例化一個對象(通過“ new ”關鍵字)時,Spring都會嘗試對該對象執行依賴注入。
現在再次運行單元測試,綠色將侵入您的計算機:D。
ITD是一個很好的解決方案,可以自己負責設計類。 它為您提供了編寫可維護且易于理解的代碼的機會,而不會丟失封裝。 當然,您應該注意不要在各方面的類中具有較高的耦合,而應將它們轉換為“上帝類”。
注意,實現相同的方法但是使用關系數據庫,就像將Jaxb2Marshaller更改為EntityManager一樣簡單。
我希望您發現這篇文章有用。
下載完整代碼
參考:來自JCG合作伙伴的 Spring AOP實現活動記錄模式 ? 在一個罐子統治他們所有博客的亞歷克斯·索托。
翻譯自: https://www.javacodegeeks.com/2012/02/implementing-active-record-pattern-with.html