文档章节

Smart2.0开发指南——特性

小菜的粉丝
 小菜的粉丝
发布于 2014/01/23 22:43
字数 2795
阅读 1014
收藏 8

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三种。

    a. 以void为返回值的方法,处理完业务后,不做后续操作,例如下载等。

    b. 以Page为返回值的方法,则转发或重定向到相应的页面中。

    Page常用方法:     

      void setPath(String path);//设置路径
      void setData(Map<String, Object> data);//设置参数

    c. 以Result为返回值的方法,则返回包含基本信息的json字符串。

    Result常用方法:

    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接口。

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

Service 实现类:
  @Service
  public class ProductServiceImpl implements ProductService { 
     
  ...
  }
     若在项目中一个接口同时存在多个实现类,此时只能选择其中的一个实现类生效,可在接口上使用 @Impl指定具体的实现类。 

4.3AOP

  借鉴 Spring 与 AspectJ 的思路,去掉了复杂的切点表达式,简化为指定包名与指定类名进行横向拦截,需使用 @Aspect 注解定义一个 Aspect 类,并继承 AspectProxy方法,通过覆盖父类中的某些钩子方法,来实现编写对应的增强代码。
  包括以下几种增强类型,即钩子方法:

方法

说明

void begin()

在进入方法时执行。

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/

© 著作权归作者所有

上一篇: asasasassssssssssss
下一篇: asasasassssssssssss
小菜的粉丝

小菜的粉丝

粉丝 95
博文 2
码字总数 3842
作品 3
成都
后端工程师
私信 提问
加载中

评论(2)

slider
slider
网站登不进去。
寒山
寒山
79辛苦啦,沙发赞
Smart2.0开发指南——开发工具

说明:本文为《Smart2.0开发指南》系列文章 1.1 准备开发工具 常言道:“工欲善其事,必先利其器”。下面介绍一些市场比较主流、比较犀利的开发工具用于Smart 应用开发,工具如下: Java 编译...

大漠真人
2014/01/21
1K
6
Smart2.0开发指南——入门

说明:本文为《Smart2.0开发指南》系类文章 第一步:创建一个基于Servlet3.0的Maven Webapp工程    打开Eclipse,点击File>New>Maven Project 选择项目保存位置,默认为Workspace目录,点击...

大漠真人
2014/01/22
1K
29
赠书福利丨Kubernetes权威指南第4版

将本篇内容转发至朋友圈,并截图于“京东云开发者社区”公众号后台进行回复。 我们将为第6、16、66、99、106位回复者送出《Kubernetes权威指南(第4版)》。 Kubernetes从一个新生事物发展为...

京东云技术新知
05/24
596
1
Dubbo (二) ——- 项目结构解析

本文主要说明点 概述 背景 需求 架构 Dubbo源代码项目结构 概述 分享 Dubbo 的项目结构 ,通过本文可以大致了解到Dubbo整个项目的结构 背景 将一个项目进行拆分, 进行分布式架构。 需要解决...

小刀爱编程
2018/10/16
761
0
Nginx模块开发指南:使用C++11和Boost程序库

【下载地址】 Nginx 是由俄罗斯工程师Igor Sysoev 开发的一个高性能Web 服务器,运行效率远超传统的Apache、Tomcat,是世界第二大Web 服务器,被国内外诸多顶级互联网公司采用。 Nginx 的一个...

winter730
2018/05/25
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Mybatis Plus删除

/** @author beth @data 2019-10-17 00:30 */ @RunWith(SpringRunner.class) @SpringBootTest public class DeleteTest { @Autowired private UserInfoMapper userInfoMapper; /** 根据id删除......

一个yuanbeth
30分钟前
4
0
总结

一、设计模式 简单工厂:一个简单而且比较杂的工厂,可以创建任何对象给你 复杂工厂:先创建一种基础类型的工厂接口,然后各自集成实现这个接口,但是每个工厂都是这个基础类的扩展分类,spr...

BobwithB
今天
4
0
java内存模型

前言 Java作为一种面向对象的,跨平台语言,其对象、内存等一直是比较难的知识点。而且很多概念的名称看起来又那么相似,很多人会傻傻分不清楚。比如本文我们要讨论的JVM内存结构、Java内存模...

ls_cherish
今天
4
0
友元函数强制转换

友元函数强制转换 p522

天王盖地虎626
昨天
5
0
js中实现页面跳转(返回前一页、后一页)

本文转载于:专业的前端网站➸js中实现页面跳转(返回前一页、后一页) 一:JS 重载页面,本地刷新,返回上一页 复制代码代码如下: <a href="javascript:history.go(-1)">返回上一页</a> <a h...

前端老手
昨天
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部