Smart2.0開發指南——特性

4.特性

4.1MVC

  借鑑 Spring MVC 與 RESTful Web Service 的思路,打造了一款輕量級 MVC 框架。Model 由 Entity 或 JavaBean 充當,View 爲 JSON 格式的數據(也可爲 XML 格式),Controller 在框架中被命名爲 Action。


  請求統一發送到 DipatcherServlet,由它轉發到相應的 Action(@Action標記過的類) 中,可經過 @Request 註解在 Action 中設置轉發規則。
  相比1.0版本,再也不繼承BaseAction,減小了依賴。

4.1.1MVC註解

  1. @Action:定義Action類,只有標記過的類纔會被轉發。
  2. @Request:定義URL映射規則。

4.1.2映射規則

  在 @Request 註解的參數中,使用「請求方法 + 請求地址」的格式定義一個完整的請求。
  1. 請求方法:
    GET、POST、PUT、DELETE四種,不區分大小寫(推薦大寫)。
  2. 請求地址: 請求地址要以「/」開頭,末尾無需「/」,一樣不區分大小寫(推薦小寫)。在地址中,容許使用一個或多個帶「{xxx}」格式的佔位符,依次對應 Action 方法中的參數,類型通常爲 long 或 String。可以使用 Map<String, Object> 封裝全部請求參數,key 爲參數名,value 爲參數值,可根據實際狀況對 Object 類型的參數值進行轉型。
  經過一個 Action 類來講明 @Request 註解的用法,代碼以下:
@Action
public class ProductAction {

    @Request("GET:/product")
    public Page index() {
		......
    }

    @Request("POST:/product/create")
    public Result create(Map<String, Object> fieldMap, List<Multipart> multipartList) {
		......
    }
	
	@Request("PUT:/product/update/{id}")
    public Result update(long id, Map<String, Object> fieldMap) {
		......
    }

    @Request("DELETE:/product/delete/{id}")
    public Result delete(long id) {
		......
    }
}

4.1.3Action方法

  1. Action方法參數
    容許定義空參數、long、Map 、List四種類型,以及它們之間的各類組合。例如:
    public Page index();
    public Result delete(long id) 
    public Page search(Map<String, Object> fieldMap)
    public Result update(long id, Map<String, Object> fieldMap)
    public Result create(List<Multipart> multipartList)
    public Result create(Map<String, Object> fieldMap, List<Multipart> multipartList)
    public Result uploadPicture(long id, Map<String, Object> fieldMap, List<Multipart> multipartList) 
    ......
  2. Action方法返回值

    Action方法返回值容許爲voidPageResult三種。 java

    a. 以void爲返回值的方法,處理完業務後,不作後續操做,例以下載等。 mysql

    b. 以Page爲返回值的方法,則轉發或重定向到相應的頁面中。 ajax

    Page經常使用方法:      sql

      void setPath(String path);//設置路徑
      void setData(Map<String, Object> data);//設置參數

    c. 以Result爲返回值的方法,則返回包含基本信息的json字符串。 數據庫

    Result經常使用方法: json

    void setSuccess(boolean success);//設置成功標誌
      void setError(int error);//設置錯誤代碼
      void setData(Object data);//設置響應數據

4.2IOC

     借鑑 Spring IOC 的思路,可將任意的類標註爲@Bean,當應用啓動時,框架會自動建立這些 Bean實例,並放入容器中。隨後可以使用 BeanHelper.getBean() 方法獲取對應的 Bean實例。通常狀況下,會將 Action類標註爲@Action,Service實現類標註爲 @Service,可在 Action類中使用 @Inject註解注入所需的 Service接口。 app

Action類:
  @Action
  public class ProductAction {
  
  @Inject
  private ProductService productService;
  
   ...
  }

Service 實現類:
  @Service
  public class ProductServiceImpl implements ProductService { 
     
  ...
  }
     若在項目中一個接口同時存在多個實現類,此時只能選擇其中的一個實現類生效,可在接口上使用 @Impl指定具體的實現類。 

4.3AOP

  借鑑 Spring 與 AspectJ 的思路,去掉了複雜的切點表達式,簡化爲指定包名與指定類名進行橫向攔截,需使用 @Aspect 註解定義一個 Aspect 類,並繼承 AspectProxy方法,經過覆蓋父類中的某些鉤子方法,來實現編寫對應的加強代碼。
  包括如下幾種加強類型,即鉤子方法:

方法 框架

說明 jsp

void begin() ide

在進入方法時執行。

boolean intercept(Method method, Object[] args)

設置過濾條件。默認返回 true,表示無過濾條件。

void before(Method method, Object[] args)

在目標方法調用前執行。

void after(Method method, Object[] args)

在目標方法調用後執行。

void error(Method method, Object[] args, Exception e)

在拋出異常時執行。

void end()

在方法執行完畢前執行。

  相比1.0版本,再也不繼承BaseAspect,再也不須要@Bean註解,新增了@Order註解。

4.3.1AOP註解

  1. @Aspect:定義須要攔截的類,只有知足條件的類或方法纔會被攔截。
    參數:pkg定義須要攔截的包名。
         cls須要攔截的類名。若是爲空,則攔截該包下的全部類;不然若是包名與類名均不爲空,則添加指定類。
  2. @Order:多個攔截方法的執行順序。
    參數:value序號的值越小越靠前

如下編寫了一個 Aspect 類,用於橫切 com.smart.sample.action下全部類的方法,除了SystemAction類,代碼以下:

@Aspect(pkg = "com.smart.sample.action")
@Order(0)
public class AccessAspect extends AspectProxy {

    @Override
    public boolean intercept(Class<?> cls, Method method, Object[] params) throws Exception {
        boolean result = true;
        if (cls == SystemAction.class) {
            result = false;
        }
        return result;
    }

    @Override
    public void before(Class<?> cls, Method method, Object[] params) throws Exception {
        Long userId = DataContext.Session.get(Constant.USER_ID);
        if (userId == null) {
            WebUtil.setRedirectURL(DataContext.getRequest(), Constant.REDIRECT_URL);
            throw new AccessException();
        }
    }
}
可在 intercept() 方法中,對目標方法(Method 對象)的名稱、參數、返回值、註解等信息設置過濾條件。

4.4ORM

  借鑑 JPA 的思路,對 ORM 映射規則進行了簡化,儘可能減小相關的註解配置。每一個 Entity 類必須繼承 BaseEntity,該父類中提供了實體主鍵字段,名稱爲 id,類型爲 long,在實際項目中可對主鍵字段進行統一修改。
  經過一個簡單的示例來講明 ORM 映射規則,代碼以下:
public class Product extends BaseEntity {

    private long productTypeId;

    private String productName;

    private String productCode;

    private int price;

    private String description;

    ... getter/setter 方法
}

映射規則以下:

Entity 

數據庫

類名(如:Product

表名(如:product

屬性名(如:productTypeId

列名(如:product_type_id

      可見,將 Entity 類的類名與屬性名的「駝峯式」轉爲「下劃線式」,即爲數據庫的表名與列名。若表名帶有前綴(如:t_product),則此時須要藉助 @Table 註解,將其標註在 Entity 類上,在該註解的參數中指定所對應的表名(如:@Table(「t_product」))。

同理,對於屬性名與列名的不規則映射規則,能夠藉助 @Column 註解實現映射關係。當列名爲 Java 關鍵字時(如:class),須要考慮此方案。

     在本框架中使用「貧血式」模型,不使用 OneToMany、ManyToOne、ManyToMany 等「充血式」模型,也就是說,一個 Entity 與一個 Table 對應,一個 Property 與一個 Column 對應。建議使用 Java 原始類型,而不是封裝類型,如:建議使用 int,而不是 Integer 

Entity 屬性類型只能從如下類型中選擇:

類型

說明

int

對於長度較小數值類型數據,推薦使用 int 類型,不要使用 Integer 類型

long

對於長度較長的數值類型數據,推薦使用 long 類型,不要使用 Long 類型

double

對於全部浮點類型數據,推薦使用 double 類型,不要使用 Doublefloat/Float 等類型

String

對於字符類型數據,推薦使用 String 類型,不要使用 char 類型

注意:對於日期或時間,推薦使用 String 或 long 類型,而不要使用 Date 類型。

4.5DAO

      封裝 Apache DbUtils 類庫,提供 DataSet 工具類,可執行基於單表的 SQL 語句,詳細的 DataSet API 請見附錄。
對於複雜的 SQL 語句,可編寫在代碼中,也可編寫在 sql.properties 文件中,經過 SQLHelper.getSQL() 方法來獲取具體的 SQL 語句,可配合使用 DBHelper 來執行 SQL 語句。

4.6事務控制

  借鑑 Spring 聲明式事務控制的思路,提供了一個 @Transaction 註解,將此註解標註在 Service 實現類中打算進行事務控制的方法上,該方法在運行時就會具有事務特性。事務使用默認的傳播行爲,本框架不考慮複雜的傳播行爲,也不考慮嵌套事務與跨庫事務。
  相比1.0版本,新增@Service註解,用於區分其餘類型的Bean,做用和@Bean註解同樣。

4.7異常處理

      採用 C/C++ 編碼風格,採用錯誤代碼取代異常處理機制,該方案僅用於 Action 方法中。經過一個簡單的示例展現基於錯誤代碼的異常處理機制,代碼以下:
@Action
public class ProductAction {

    @Inject
    private ProductService productService;

    ...

    @Request("get:/product/{id}")
    public Result getProductById(long productId) {
        if (productId == 0) {
            return new Result(false).error(ERROR_PARAM);
        }
        Product product = productService.getProduct(productId);
        if (product != null) {
            return new Result(true).data(product);
        } else {
            return new Result(false).error(ERROR_DATA);
        }
    }

    ...
}
     當 productId 爲 0 時,返回一個 Result 對象,成功標誌爲 false,錯誤代碼 ERROR_PARAM(值爲 10)。錯誤代碼可統必定義。
     同理,當 product 不爲 null 時,返回一個 Result 對象,成功標誌爲 true,相關數據爲 product,不然,也返回一個 Result 對象,成功標誌爲 false,錯誤代碼爲 ERROR_DATA(值爲 20)。Action 方法的返回值,也就是 Result 對象,將會序列化爲 JSON 格式的數據,經過 AJAX 的方式返回到回調函數中,可經過獲取 result 對象的 success 屬性來判斷操做是否成功,可訪問 error 屬性獲取相應的錯誤代碼,並給出具體的提示信息。AJAX 代碼示例以下:
...
    $.ajax({
        url: '/product/' + productId,
        type: 'get',
        success: function(result) {
            if (result.success) {
                var product = result.data;
                $('#product_type_id').val(product.productTypeId);
                $('#product_name').val(product.productName);
                $('#product_code').val(product.productCode);
                $('#price').val(product.price);
                $('#description').val(product.description);
            } else {
                switch (result.error) {
                    case 10:
                        alert('The parameter is error!');
                        break;
                    case 20:
                        alert('The data is error!');
                        break;
                }
            }
        }
    });
...

4.8單元測試

      使用 JUnit 做爲單元測試框架,並對其進行了擴展,可以使用 @Order 註解設置被測方法的執行順序。如下是一個 Test 類,代碼以下:
public class ProductServiceTest extends BaseTest {

    private ProductService productService = BeanHelper.getBean(ProductServiceImpl.class);

    @BeforeClass
    @AfterClass
    public static void init() {
        initSQL("sql/product.sql");
    }

    @Test
    @Order(1)
    public void getProductListTest() {
        List<Product> productList = productService.getProductList();
        Assert.assertNotNull(productList);
        Assert.assertEquals(productList.size(), 7);
    }

    @Test
    @Order(2)
    public void getProductTest() {
        long productId = 1;
        Product product = productService.getProduct(productId);
        Assert.assertNotNull(product);
    }

    ...
}
      Test 類必須繼承 BaseTest 類。在類中可以使用 BeanHelper.getBean() 方法初始化相關的 Bean 實例(這裏是 Service 實現類實例),注意:此時不能使用 @Inject 實現依賴注入。可以使用 JUnit 提供的 @Test、@BeforeClass、@AfterClass 等註解來標註被測方法。在 init() 方法上同時標註了 @BeforeClass 與 @AfterClass,表示該方法會在測試以前與測試以後被 JUnit 框架所調用,用於執行數據初始化腳本,該腳本在 test/resources/sql/ 目錄下。
      可在 test/resources/config.prperties 文件中單獨配置單元測試所對應的數據源,通常狀況下一個應用對應兩個數據庫,一個做爲開發,另外一個做爲測試。 建議保證單元測試方法的順序性,併合理使用 @Order 註解,讓整個單元測試可反覆測試。

4.9MVC配置文件

    MVC中全部涉及到的參數配置都在根類路徑下config.properties文件中。參數說明見:
#應用名稱
app.name=smart-sample
#Action類所在的包
app.package=com.smart.sample
#網站靜態資源路徑
app.www_path=/www/
#jsp路徑
app.jsp_path=/WEB-INF/jsp/
#登陸地址
app.home_page=/login
#上傳文件尺寸大小(MB)
app.upload_limit=10

#數據庫類型
jdbc.type=mysql
#驅動名
jdbc.driver=com.mysql.jdbc.Driver
#數據庫連接地址
jdbc.url=jdbc:mysql://localhost:3306/sample
#登陸名
jdbc.username=root
#密碼
jdbc.password=root

#是否動態加載i18n文件
i18n.reloadable=true

#上傳路徑
sample.upload_path=/www/upload/
相關文章
相關標籤/搜索