1. 程式人生 > >Karaf教程第9部分基於註解的blueprint和JPA

Karaf教程第9部分基於註解的blueprint和JPA

本部分演示如何用模型持久層和基於CDI註解的UI建立一個小的應用。

1 blueprint-maven-plugin

編寫blueprint xml檔案是很繁瑣的,太大的blueprint xml檔案很難與程式碼修改保持同步,尤其是程式碼重構。所以很多人喜歡使用註解來進行宣告。理想情況下,這些註解應該被標準化,這樣就很清晰地定義註解的功能。允許使用註解配置blueprint。它會掃描一個或多個路徑下的註解類,然後target/generated-resources建立blueprint.xml檔案。請參閱maven-blueprint-plugin文件。

2示例tasklist-blueprint-cdi

本部分演示如何用模型持久層和UI建立一個小的應用,而完全不用手動編寫blueprint xml檔案。

2.1工程結構

  • features
  • model
  • persistence
  • ui

2.2建立bundle

這些bundle都是使用maven-bundle-plugin建立的。這個建立只在父工程使用,它會用<_include>osgi.bnd</_include>抽取OSGi配置到單獨的檔案。所以每一個bundle工程只需要空的、可包含額外配置的osgi.bnd檔案。

由於bnd會自動計算出大多部分配置,因此osgi.bnd檔案一般都比較小。

2.3 Features

定義karaf feature

,安裝示例以及必要的依賴。

2.4模型

Model工程定義了Task為一個jpa實體,定義了服務介面TaskService。由於model工程不需要任何依賴注入,所以不涉及到blueprint-maven-plugin

Task JPA Entity

@Entity

public class Task {

    @Id

    Integer id;

    String title;

    String description;

    Date dueDate;

    boolean finished;

    // Getters and setters omitted

}

TaskService (Task的CRUD操作)

public interface TaskService {

    Task getTask(Integer id);

    void addTask(Task task);

    void updateTask(Task task);

    void deleteTask(Integer id);

    Collection<Task> getTasks();

}

persistence.xml

    <persistence-unit name="tasklist" transaction-type="JTA">

        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>

        <jta-data-source>osgi:service/tasklist</jta-data-source>

        <properties>

            <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>

            <property name="hibernate.hbm2ddl.auto" value="create-drop"/>

        </properties>

    </persistence-unit>

</persistence>

Persistence.xml檔案定義了持久單元的名字為"tasklist",使用JTA事務。jta-data-source元素指向一個DataSource服務"tasklist"jndi名字。所以除了JTA DataSource名字,它是一個標準的hibernate 4.3風格的持久化定義,自動建立schema

另一個比較重要的事情是maven-bundle-plugin外掛的配置。

配置maven bundle plugin

<Meta-Persistence>META-INF/persistence.xml</Meta-Persistence>

<Import-Package>*, org.hibernate.proxy, javassist.util.proxy</Import-Package>

Meta-Persistence指向一個persistence.xml檔案, 它用於觸發aries jpa建立這個BundleEntityManagerFactoryImport-Package配置匯入了兩個packagehibernate在執行時優化處理時需要。由於在編譯時,這個優化是未知的,所以需要給maven-bundle-plugin一些提示。

2.5持久化

tasklist-cdi-persistence bundle是這個示例當中第一個使用blueprint-maven-plugin的模組。在pom檔案中,我們設定搜尋路徑為".tasklist.persistence.impl"。所以所有在這個包或者子包中的類都會被掃描。在pom檔案中,我們需要一個maven-bundle-plugin的特殊配置:
<Import-Package>!javax.transaction, *, javax.transaction;version="[1.1,2)"</Import-Package>在依賴中,我們使用事務API1.2作為包含@Transactional註解的第一個規範版本。在執行時,雖然我們不需要這個註解,且karaf只提供事務API1.1版本。所以我們調整import使用karaf提供的版本。一旦事務API 1.2版本可用,這一行就不再需要了。

TaskServiceImpl

@OsgiServiceProvider(classes = {TaskService.class})

@Singleton

@Transactional

public class TaskServiceImpl implements TaskService {

    @PersistenceContext(unitName="tasklist")

    EntityManager em;

    @Override

    public Task getTask(Integer id) {

        return em.find(Task.class, id);

    }

    @Override

    public void addTask(Task task) {

        em.persist(task);

        em.flush();

    }

    // Other methods omitted

}

TaskServiceImpl使用了很多註解。使用註解@Singleton標記這個類為blueprint bean。使用註解@OsgiServiceProvider標記這個類為介面TaskServiceOSGi服務。使用註解@Transactional標記這個類支援事務操作。

所有的方法都在jta事務中執行。這意味著如果沒有事務,那就會創建出來一個事務。如果已經有一個事務,那麼方法就會參與其中。在事務的邊界,事務要麼被提交,要麼因為異常而被回滾。

EntityManager用於持久單元"tasklist",被注入到欄位em。它為每一個方法透明地提供一個EntityManager,按需建立,並在事務邊界關閉。

InitHelper

@Singleton

public classInitHelper {

    LoggerLOG= LoggerFactory.getLogger(InitHelper.class);

   @InjectTaskService taskService;

   @PostConstruct

   public voidaddDemoTasks() {

       try{

            Task task =newTask(1,"Just a sample task","Some more info");

            taskService.addTask(task);

        }catch(Exception e) {

           LOG.warn(e.getMessage(), e);

        }

    }

}

InitHelper類不是嚴格必須的。它只是簡單滴建立和持久化第一個任何,這樣UI就可以顯示一些東西了。

@Singleton註解標記這個類作為一個blueprint bean建立。
@Inject TaskService taskService
注入在blueprint上下文中找到的第一個型別為TaskServicebean,並賦給欄位taskService
@PostConstruct
註解確保在這個bean的所有欄位都被注入後,呼叫addDemoTasks()方法。

另一件有趣的事情是模組中的測試類TaskServiceImplTest。它在OSGi之外執行,使用特殊的persistence.xml用於測試時建立EntityManagerFactory,而不需要jndi DataSource,因為這個很難提供。它也使用了RESOURCE_LOCAL事務,這樣我們就不需要建立事務管理。測試注入了普通的EntityManger物件到TaskServiceImpl類中。所以我們必須手動開啟事務和提交事務。這個例子說明了用普通的java測試JPA程式碼,測試簡單快速。

2.6 Servlet UI

tasklist-ui模組使用OSGi service TaskServicePax-web whiteboard bundle會檢測匯出的servlet,並使用HttpService釋出它。在pom檔案中,這個模組需要blueprint-maven-plugin有適當的scanPath

TasklistServlet

@OsgiServiceProvider(classes={Servlet.class})

@Properties({@Property(name="alias", value="/tasklist")})

@Singleton

public class TaskListServlet extends HttpServlet {

    @Inject @OsgiService

    TaskService taskService;

    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException,

        IOException {

        // Actual code omitted

    }

}

TaskListServlet用介面javax.servlet.Servlet匯出,服務屬性別名為"/tasklist"。這樣,通過就可以訪問到。

@Inject @OsgiService TaskService taskService這條語句會建立一個blueprint的引用元素,匯入TaskService介面定義的OSGI服務。然後,這個服務就被注入到上面這個類的taskService欄位。如果這個介面存在多個服務例項,那麼filter屬性可用於選擇使用哪一個。