声明: 本文来自 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 能看到默认的主页啦。
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 来食用。 当然你也可以选择不用。
<!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 验证一下。
编写 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 。
编写 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 能帮助你管理大部分类的实例。
编写 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 才有成就感嘛。
待完善
待完善
待完善