ActFramework 入门指南 转

Java框架

浏览数:196

2019-7-4

AD:资源代下载服务

声明: 本文来自 ActFramework QQ 群 “冰儿!-北京-Java” 同学的博客

以下博客正文:

ActFramework 是一款高性能 Java 全栈框架,用于开发传统的 MVC 应用或 RESTful 服务。和其他现有 MVC/RESTful 框架相比,ActFramework 的优势在于表达力和简洁易用。

这是ActFramework的一句自述。

表达力和简洁易用,是它的优点,同时也是我所喜欢的地方。

这篇文章,将带你体验并展开一个基本的MVC项目。 (Controller,Service,Dao标准结构)

既是全栈框架,从MVC到ORM一个不少,本文章将通过ActFramework + Hibernate(JPA)来演示。 模板渲染引擎呢,则采用默认的Rythm。

PS:ActFramework项目暂时无法使用lombok,或者其他非Java的JVM语言。 PS:本文章内容大部分与 ActFramework 文档内容重合。本文章可作为入门时快速理解框架的引导使用,更加具体内容请参见文档。 本文章内代码地址:GitHub,Gitee

初步了解

创建新项目

首先让我们来建立一个Hello ActFramework项目。 ActFramework 提供了完整的 Maven 生态,我们可以借由 Maven 很快的展开一个 ActFramework 的基础项目。

让我们给还没用熟IDEA的小伙伴一点提示。

新增一个 Maven 的展开项

org.actframework
archetype-quickstart
1.8.23.0

然后填写上你项目的基本信息

然后根据你的需要配置一下你的项目信息。 基本上除了路径也不需要额外配置。 最后创建项目,等着 Maven 自动展开就好啦。 PS:建议同学们按照Java 比较通用的命名规则来对项目进行命名,请不要学习我瞎命名。

熟悉结构

这是一个 ActFramework 项目展开后的基本结构。 一个标准的 Maven 项目结构 test 我们暂且不看,让我们的焦点聚集在 main 文件夹里面。 java 呢,众所周知,就是代码路径。 默认会给你生成一个负责启动项目的类AppEntry。

这不是大头,大头在resources文件夹下面。

resources
|--conf 这个就是存放配置文件的文件夹
|--rythm 一般用来存放模板文件的路径

除了这些呢,其他的就暂且不管。 conf 文件夹下还会有一些子文件夹,这些子文件夹下还会有app.properties。 这些也是配置文件,当然配置文件也不会都在这里。

它和 conf 下 app.properties 的区别就是子文件夹下的 app.properties 是用来应对不同的环境的(如开发环境,测试环境,生产环境)。 也是按照标准套路,在当前环境下的 app.properties 没有的配置则会启用conf目录下 app.properties 的配置。

模板目录。 当然 rythm 并不一定会是最终的模板目录。 在默认情况下 ActFramework 会按照 /resources/模板引擎id/Controller类名/方法名.后缀名来寻找模板文件。 当然你也可以手动指定。 因为展开的项目在 AppEntry 类内 带有一个Action,所以也默认附带了一个模板文件 位于 resources/rythm/com/IceCreamQAQ/test/AppEntry下的home.html 同理,之后你需要添加什么模板,按这个规则走就行了,方便。

启动项目

视线转向默认生成的启动类 AppEntry 。

public class AppEntry {

    @GetAction
    public void home(@DefaultValue("World") [@Output](https://my.oschina.net/output1314) String who) {
    }

    public static void main(String[] args) throws Exception {
        Act.start();
    }

}

main方法,简单易懂,启动方法。 用过Spring Boot的同学可能会比较简单的理解这种启动方式。 但是如果是从J2EE过来的同学可能就不太懂了。

ActFramework 并非采用了传统的J2EE那套,而是一个基于Java SE实现了HTTP协议的标准Java程序。 简而言之就是ActFramework并不需要(标准意义上的)Web容器(如:Tomcat)。 ActFramework 本身就是一个Web容器。 当然这不重要。 不过一时之间从用的习惯的 HttpServletRequest、HttpServletResponse 里面跳出来,需要一点点的适应的。

执行这个 main 方法我们就可以启动 ActFramework 项目了。

       _           _          _  ___   _   _                _           _    _      
  |_|  |_  |   |   / \   /\   /    |   |_  |_)   /\   |\/|  |_  \    /  / \  |_)  |/ 
  | |  |_  |_  |_  \_/  /--\  \_   |   |   | \  /--\  |  |  |_   \/\/   \_/  | \  |\ 
                                                                                     
                                                powered by ActFramework r1.8.23-d4dee
 
  version: v1.0-SNAPSHOT
 scan pkg: com.IceCreamQAQ.test
 base dir: C:\project\IceCreamQAQ\Act\HelloActFramework
      pid: 6972
  profile: dev
     mode: DEV
       OS: WINDOWS
      jdk: Java HotSpot(TM) 64-Bit Server VM 1.8

      zen: Complex is better than complicated.
 
 2019-06-25 22:41:26,649 INFO  a.Act@[main] - loading application(s) ...
 2019-06-25 22:41:26,661 INFO  a.a.App@[main] - App starting ....
 2019-06-25 22:41:26,674 INFO  o.xnio@[Thread-0] - XNIO version 3.3.8.Final
 2019-06-25 22:41:26,695 INFO  o.x.nio@[Thread-0] - XNIO NIO Implementation Version 3.3.8.Final
 2019-06-25 22:41:27,111 WARN  a.a.DbServiceManager@[main] - DB service not initialized: No DB plugin found
 2019-06-25 22:41:27,631 WARN  a.m.MailerConfig@[jobs-thread-3] - smtp host configuration not found, will use mock smtp to send email
 2019-06-25 22:41:27,690 INFO  a.Act@[main] - network client hooked on port: 5460
 2019-06-25 22:41:27,694 INFO  a.Act@[main] - network client hooked on port: 5463
 2019-06-25 22:41:27,695 INFO  a.Act@[main] - CLI server started on port: 5461
 2019-06-25 22:41:28,233 INFO  a.Act@[main] - app is ready in 1746ms at: http://192.168.0.105:5460
 
 
 2019-06-25 22:41:28,280 INFO  a.a.ApiManager@[jobs-thread-3] - start compiling API book
 2019-06-25 22:41:28,346 INFO  a.a.ApiManager@[jobs-thread-3] - API book compiled

这时,启动浏览器并打开http://127.0.0.1:5460 能看到默认的主页啦。

Controller 与 Action

AppEntry 这个启动类本身就是一个 Controller。 ActFramework 的 Controller 不要求标注任何注解,也不要求继承或实现任何一个类。 只要 Controller 含有 Action,那么它就是一个 Controller 。(这时作者说的,但是我也不知道含有拦截器的算不算Controller,但是问题不大,这个并不影响)

让我们来关注一下 AppEntry 的 home 方法。

@GetAction
public void home(@DefaultValue("World") @Output String who) {
}

方法不长,但是却是一个非常标准的 Action 。 @GetAction 注解 代表了 这是一个 Get 方法。 同理还有 @PostAction 、 @PutAction 、 @DeleteAction 。 还有一个 @Action 注解,用来定义全部或指定 HTTP 请求方法的路由。 @DefaultValue 代表了 当 who 为 null 时的默认值。 @Output 则代表了这个参数会被带到模板。

用过 Spring 的同学可能可以理解 who 变量具体的含义,以及值来源。 这里简要的解释一下 who 变量的值来源。 在 Action 方法参数内的变量,一般会被匹配请求参数。 如变量名为 who 则会匹配 名为 who 的请求参数。 不信邪的同学可以尝试访问下 http://127.0.0.1:5460/?who=ActFramework 来验证下结果。

当然,参数不仅仅能用String,还可以使用Integer 等等,以及可以使用Java 实体类。 ActFramework 会根据请求头来区分请求内容。表单请求,或是 application/json 又或者是其他什么,一视同仁。 不需要 @RequestBody 。

同时,ActFramework 支持匹配url路径,让我们写一个简单的 Action 。

@GetAction("test/{name}")
public String test(String name){
    return name;
}

Spring 的同学可能会非常感动,终于不用写烦人的@PathVariable了! 同时 ActFramework 会根据你方法的返回值类型来确定该怎样渲染页面。 如果你的方法是 void 的,ActFramework 则会自动去寻找模版文件。 如果是其他类型,ActFramework 则会选择一个合适的方式将你的返回值渲染出去。 也就是说,我们也不再需要 @ResponseBody 了!

此时,先不要着急点击你的重启按钮,直接访问下 http://127.0.0.1:5460/test/wow 试试! 是不是wow! ActFramework 的 热加载 完全不需要你进行任何操作(虽然我还是会习惯性的点一下重启)。 你只需要编码 然后 查看结果就好啦。

使用 Spring 的同学可能会非常感动,终于不用等一两分钟的重启时间了。 这时候要吐槽一下,IDEA启动 build 项目,加上启动 Tomcat 最后算上 Spring 启动,我的天啊,这个启动时间。 有了Spring Boot 之后稍微改善了一点,项目一大,启动还是得一分多钟,改一点一分钟,改一点一分钟,工作效率就是在这没得。

有些同学可能想说,单单这些,也不全够啊。 有些时候还是需要 Request 和 Response 啊。

别急,你在方法的参数里面写上 H.Request 和 H.Response 就行了。 其实在 Http协议 下,大家都差不多。 这块我就不复制了,建议直接查看文档(在Gitee的,方便国内的同学查看)。

同时 ActFramework 提供了很多非常方便的方法。 如: 大家可以选择 import static act.controller.Controller.Util.*; 或者 Controller extends Controller.Util 来食用。 当然你也可以选择不用。

Rythm 模板引擎

 <!DOCTYPE html>
 <html lang="en">
 @args String who
 <head>
   <title>Hello World - ActFramework</title>
 </head>
 <body>
   <h1>Hello @who</h1>
   <p>
     Powered by ActFramework @act.Act.VERSION.getVersion()
   </p>
 </body>
 </html>

这是默认生成的 AppEntry 的 home 方法的模板。

Rythm 对比其他模板引擎来说,看起来最大的不同就是,他需要显示的声明变量,和变量类型。 @args String who 代表了 声明一个 名为 who 的 String 类型变量。 这里的 String 就是 java.lang.String 。 然后再需要输出这个变量内容的时候,写上@ + 变量名 就可以了。

可能有些同学要问了,如果我要声明一个实体变量怎么办。 很简单,你可以@args Entity name ,就好了。 你不会真的以为这么简单吧? 没那么简单,但是也不会很难,你只需要先导包。 @import com.IceCreamQAQ.test.entity.Entity;

有些同学可能会想,我是在写 Java 吗?还要先导包? 对的!没错! 你就是在写 Java 。 Rythm 的模板文件,在被载入的时候会被 Rythm 动态生成成一个 Java Class,并通过这个类完成渲染。 所以,你看起来在写模板,实际上还是在写 Java 。

在你写在@之后的代码都会被转意成Java代码。 同理,如果你想使用某个工具类的某个静态方法,你可以 @com.IceCreamQAQ.test.util.Utils.method() 你还可以在括号里面写参数(也可以用之前声明过的变量,此时就不需要加@符号了)

@if 逻辑判断语句,结构是

@if(boolean){
    //输出内容
}else{
    //否则输出内容
}

当然 else 块是可选的。 不过需要注意的是,大括号内的内容会被直接输出,而不是被转意成 Java 代码。 所以要在大括号块内进行操作,仍需要 @ 符号的帮助。

开搞!

建立结构

基础的我们都了解完了,接下来该上主菜了,开搞!

首先我们建几个包。 controller , dao , entity , service , service.impl

本文章目标,完成基础的用户登录,注册。

添加 Maven 依赖

<!-- ActFramework Hibernate插件,必加 -->
<dependency>
    <groupId>org.actframework</groupId>
    <artifactId>act-hibernate</artifactId>
</dependency>
<!-- 数据库连接池,HikariCP,Druid选加其一 -->
<dependency>
    <groupId>com.zaxxer</groupId>
    <artifactId>HikariCP</artifactId>
</dependency>
<!-- MySql 数据库驱动包 根据你所使用的的数据库选加 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.47</version>
</dependency>

编写实体类

建立 User.java 实体类

@Entity
@Table(name = "user")
public class User implements SimpleBean {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    public Integer id;
    @Column
    public String password;
    @Column
    public String email;
    @Column
    public String phone;

    public static String getPasswordHash(String password) {
        return Act.crypto().passwordHash(password);
    }
}

一个标准的 JPA 实体类,不知道 JPA 是啥的同学,可以百度,Google 了解一下,这里不做过多赘述。 因为 ActFramework 暂不兼容 lombok 但我们又懒得写 Getter ,Setter ,只需要实现 act.util.SimpleBean 框架就会帮你自动生成 Getter ,Setter 。

Act.crypto().passwordHash(String password) 是框架提供的密码通用处理方案。 当然,你喜欢什么方案用什么,此处不做强求,但是我懒,有啥用啥。

只不过这个生成的时间在应用启动之后,并非 lombok 的编译时。 所以在程序内编码时只能将成员变量设置为 public ,并通过 user.name 的形式获取和赋值。 据作者本人说 xxx = user.name; 在运行时会被转换成 xxx = user.getName(); user.name = xxx; 在运行时会被转换成 user.setName(xxx);

但是我并没有仔细求证过这个,好奇的同学可以 dump 一下 jvm 验证一下。

编写数据访问层(Dao)

编写 UserDao.java

public class UserDao extends JPADao<Integer, User> {
}

Dao 层就比较好编写了,只需要继承自 act.db.jpa.JPADao<主键,实体类> 就好了。 更多的Dao 我建议自己编写一个适合自己业务的 BaseDao<T,PK> extends JPADao<PK,T> 建议 BaseDao 为 abstract class Act 给了我们一些基础的 增删改查。 具体更复杂的,我们就需要用到 EntityManager 。

编写业务层(Service)

编写 UserService.java

@AutoBind
public interface UserService {
    
    int register(User user);
    
    User login(Integer id,String password);
    
}

@AutoBind 注解,代表了在 ActFramework 启动时,扫描到实现类,进行自动关联,用于依赖注入。

编写 UserServiceImpl.java

public class UserServiceImpl implements UserService {

    @Inject
    private UserDao dao;

    @Override
    @Transactional
    public int register(User user) {
        dao.save(user);
        return 0;
    }

    @Override
    @Transactional
    public User login(Integer id, String password) {
        User user=dao.findById(id);
        if (Act.crypto().verifyPassword(password, user.password)) return user;
        else return null;
    }
}

Act.crypto().verifyPassword(String password, String passHash) 上面说的密码通用处理方案这里是框架提供的密码验证方案。

@Inject 为依赖注入注解。 我们无需对 Dao 添加任何额外的注解,即可注入 Dao 。

和 Spring 不同的是,ActFramework 并不需要任何额外注解来表示这个类需要由框架管理。 ActFramework 更加倾向于他帮你管理所有的类的实例,当然从技术上来说这不太可能。 但是 ActFramework 能帮助你管理大部分类的实例。

编写控制层(Controller)

编写 UserController.java

public class UserController extends Controller.Util {

    @Inject
    private UserService service;

    @GetAction("register")
    public void register() {

    }

    @GetAction("login")
    public void login() {

    }

    @PostAction("doRegister")
    public Result doRegister(User user) {
        try{
            user.password=User.getPasswordHash(user.password);
            service.register(user);
        }catch (Exception e){
            e.printStackTrace();
            return renderHtml("<script>alert('\\u6CE8\\u518C\\u5931\\u8D25\\uFF01');window.history.back();</script>");
        }
        return renderHtml("<script>alert('\\u6CE8\\u518C\\u6210\\u529F\\uFF01uid:"+user.id+"');location.href='login';</script>");
    }

    @PostAction("doLogin")
    public Result doLogin(Integer uid,String pwd) {
        try{
            User user=service.login(uid,pwd);
            if (user!=null)return renderHtml("<script>alert('\\u767B\\u5F55\\u6210\\u529F\\uFF01');location.href='register';</script>");
            return renderHtml("<script>alert('\\u767B\\u5F55\\u5931\\u8D25\\uFF01');window.history.back();</script>");
        }catch (Exception e){
            e.printStackTrace();
            return renderHtml("<script>alert('\\u767B\\u5F55\\u5931\\u8D25\\uFF01');window.history.back();</script>");
        }
    }
}

配合 Controller.Util 食用,我们可以非常简单的写出提示信息。 当然我建议你们使用页面来展示信息而并非我这种弹框。 显得很 low ,但是演示而已,谁又在乎呢?不是吗?

编写模板

编写 rythm/com/IceCreamQAQ/test/controller/UserController/register.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>注册</title>
</head>
<body>
<form action="doRegister" method="post">
    <input name="password" type="password"/><br />
    <input name="phone" /><br />
    <input name="email" /><br />
    <button>注册</button>
</form>
</body>
</html>

编写 rythm/com/IceCreamQAQ/test/controller/UserController/register.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录</title>
</head>
<body>
<form action="doLogin" method="post">
    <input name="uid" /><br />
    <input name="pwd" type="password"/><br />
    <button>登录</button>
</form>
</body>
</html>

两个异常简单的页面,几乎没有什么与功能无关的内容。 简单明了,简而易懂(逃 其实就是懒,但是满足演示就没问题了吧?

编写配置

编写数据源配置文件

数据源配置文件位于 /resource/conf/db.properties 当然,你也可以使用多环境配置,但是我懒。

db.impl=act.db.hibernate.HibernatePlugin
db.driver = com.mysql.jdbc.Driver
db.url = jdbc:mysql://127.0.0.1:3306/Test?useSSL=false&useUnicode=true&characterEncoding=utf-8
db.username = test
db.password = 12345678

简单明了的配置文件。 事实上 ActFramework 对多数据源支持不错,你只需要在 db 后面加一个序号,如 db1.url 。 当然现在谈这些意义不大,我们日后再谈。

编写 Hibernate 配置文件

实际上,JPA 模式下的 Hibernate 的配置文件,就是位于 classpath 目录下的 hibernate.properties 。 也就是说 hibernate.properties 这个配置文件要直接放置在 /resource ,而并非 /resource/conf 下。 这也是我在上面为什么会说,并不是所有的配置文件都在这的原因了。 编写 /resource/hibernate.properties

hibernate.show_sql=true

开始测试

写好这些之后简单的注册登录功能就已经做好了。 什么?你问我要数据表? 自己想去。 这时候我们就需要启动项目(添加依赖后需要重新启动一次项目,热部署无法动态加载依赖。) 然后打开浏览器,去玩吧!

http://127.0.0.1:5460/register

入门部分,就到此结束。 这部分小菜吃完了,各位同学对 ActFramework 有一定的了解了吗? 接下来的进阶部分才是主菜哦~ 别走开,等我填完坑,更精彩!

进阶

在进阶部分,我们会对这个 mini 项目,进行一些基本的完善,并完成一点基础功能。 与此同时,对 ActFramework 进行更深入的学习。

进阶部分的目标:对本项目功能丰满。 目标功能:每个用户可以发布文章,这些文章仅自己可见。(备忘录)

当然这个部分,我不会再把所有代码展示出来。 我只展示部分关键代码,引导你了解 ActFramework 。 具体细节,就由你自己推敲了!毕竟,自己杀掉的 Boss 才有成就感嘛。

拦截器

待完善

路由

待完善

Session

待完善

作者:罗格林