这里对Nutz与ExtJs都进行二次封装,使前后台代码尽可能的复用,大部分操作都是在基类中完成的。
使用过程请看附件中的视频。
生成后的代码:
后台代码
分别在src目录下生产了java代码,在resource目录下生成了nutz的配置文件。
model层
User.java
- package org.nutz.demo3.model.admin;
-
- import java.util.Date;
- import java.util.List;
- import java.util.Set;
-
- import org.nutz.dao.entity.annotation.Column;
- import org.nutz.dao.entity.annotation.Id;
- import org.nutz.dao.entity.annotation.Name;
- import org.nutz.dao.entity.annotation.Table;
-
- @Table("user")
- public class User {
-
- @Id
- private long id;
-
- @Column
- private String name;
-
- @Column
- private int age;
-
- @Column
- private Date birthday;
-
- @Column
- private String phone;
-
- public long getId() {
- return id;
- }
-
- public void setId(long id) {
- this.id = id;
- }
-
- public String getName() {
- return name;
- }
-
- public void setName(String name) {
- this.name = name;
- }
-
- public int getAge() {
- return age;
- }
-
- public void setAge(int age) {
- this.age = age;
- }
-
- public Date getBirthday() {
- return birthday;
- }
-
- public void setBirthday(Date birthday) {
- this.birthday = birthday;
- }
-
- public String getPhone() {
- return phone;
- }
-
- public void setPhone(String phone) {
- this.phone = phone;
- }
-
-
- }
Srevice层
UserService.java
- package org.nutz.demo3.service.admin;
-
- import com.geor.nutz.common.service.EntityService;
- import org.nutz.demo3.model.admin.User;
-
- public interface UserService extends EntityService<User> {
-
- }
UserServiceImpl.java
- package org.nutz.demo3.service.impl.admin;
-
- import com.geor.nutz.common.service.impl.EntityServiceImpl;
- import org.nutz.demo3.model.admin.User;
- import org.nutz.demo3.service.admin.UserService;
-
- public class UserServiceImpl extends EntityServiceImpl<User> implements UserService {
-
- }
可以看到这里的Service层代码非常简洁(ME觉得已经不能再简洁了,这里可是一行代码都木有呀!),常用的增删改查功能,已经在基类中实现了。
Nutz本身就提供了类似EntityService这样的基类,大家也可以去看看。
这里是觉得自己封装的更加顺手些,并加入了一些Nutz本身没有提供的特性,大家可以仿照Nutz提供的基类,在自己需求的基础上来实现自己的基类。
Action层
UserAction.java
- package org.nutz.demo3.action.admin;
-
- import javax.servlet.http.HttpServletRequest;
-
- import org.nutz.ioc.annotation.InjectName;
- import org.nutz.mvc.annotation.At;
- import org.nutz.mvc.annotation.Param;
-
- import com.geor.nutz.extjs.action.ExtEntityAction;
- import org.nutz.demo3.model.admin.User;
- import org.nutz.demo3.service.admin.UserService;
-
-
- @InjectName("UserAction")
- @At("/admin/user")
- public class UserAction extends ExtEntityAction<UserService, User> {
-
- public void needCombobox() {
- // TODO Auto-generated method stub
- }
-
- public Object fetch(@Param("..") User entity, HttpServletRequest request) {
- // TODO Auto-generated method stub
- return null;
- }
-
- @At()
- public Object insert(@Param("..") User entity, HttpServletRequest request) {
- return getService().insert(entity);
- }
-
- @At()
- public Object update(@Param("..") User entity, HttpServletRequest request) {
- return getService().update(entity);
- }
-
- @At()
- public Object delete(@Param("..") User entity, HttpServletRequest request) {
- return getService().delete(entity);
- }
-
- @At()
- public Object save(@Param("entities") User[] entities, HttpServletRequest request) {
- return getService().save(entities);
- }
-
- }
这里会继承一个ExtEntityAction
在这个基类中,有针对ExtJS的特点,进行了一些列的封装,因为在ExtJS中某些数据,需要使用特定的格式,所以这里只针对前台ExtJS的情况。
将来如果前台框架换了其他的,只需要仿照这个基类,做一个对应的就可以了。而Service层与model层的代码是不需要改动的。
可以看到,这里没有list方法(查询)与clear方法(批量删除),那是因为他们在方法入参没有自定义类型的情况下,可以抽共通,放到基类中。
而insert,update,delete等等方法,因为参数会自动转换为User类型,入参中必须使用这个自定义类型,所以实在是无法省略掉这部分代码,因为本人最初的想法也是将这一层做成像Service层一样,一行代码也没有...那样看起来是多么的爽呀(好吧,ME承认自己有点偏执了)
oh,还有一个Nutz的入口函数,这是Nutz启动时的关键。
Entrance.java
- package org.nutz.demo3.action;
-
- import org.nutz.mvc.annotation.Fail;
- import org.nutz.mvc.annotation.IocBy;
- import org.nutz.mvc.annotation.Modules;
- import org.nutz.mvc.annotation.Ok;
- import org.nutz.mvc.annotation.Views;
- import org.nutz.mvc.ioc.provider.JsonIocProvider;
-
- import com.geor.nutz.extjs.view.ExtViewMaker;
-
- /**
- * 系统入口。
- *
- * @author pangwu86@gmail.com
- *
- */
- @IocBy(type = JsonIocProvider.class, args = { "/action.js", "/aop.js" })
- @Modules(scanPackage = true)
- @Views(ExtViewMaker.class)
- @Ok("ext")
- @Fail("ext")
- public class Entrance {
- }
这里会使用一个自定义View,是针对ExtJS封装的,大家不要问为什么哪里都有封装,如果不封装,不代码复用,生成的代码怎么可能会如此简洁来,代码复用确实是必须的。
其实@Ok("ext"),本质上还是@Ok("json"),只是针对ExtJS格式的特点做了一些特殊处理。
- @Modules(scanPackage = true)
这句话的作用就是扫描当前目录下的文件(包括子目录),发现有Action类的话,就会将其配置的路径信息加载,你在后面访问时,才会进去到对应的方法中。用了它,你就尽管在该目录下写action类吧,自动扫描会一个不拉的全部加载,不用在一个一个配置了。
有了上述代码,后台的就基本完成了,下面是配置文件的情况。
Service.js
- /**
- * 服务。
- */
- var services = {
-
- UserService : {
- type : 'org.nutz.demo3.service.impl.admin.UserServiceImpl',
- singleton : false
- }
-
-
- }
这里用过IOC的都会明白,依赖注入,不解释了。
Action.js
- /**
- *
- * @type
- */
- var actions = {
-
- UserAction : {
- type : "org.nutz.demo3.action.admin.UserAction",
- singleton : false,
- scope : "session"
- }
-
-
- }
这里本身其实可以不用Nutz的容器,但因为为了使用AOP做log输出与事务控制,这里所有的Action必须是在容器中的。
Aop.js
- /**
- * AOP的定义与规则。
- *
- * @type
- */
- var aop = {
- /**
- * 记录日志。
- *
- * @type
- */
- log : {
- type : 'com.geor.nutz.common.aop.LogAop'
- },
- /**
- * 声明式事务。
- *
- * @type
- */
- trans : {
- type : 'com.geor.nutz.common.aop.TransAop'
- },
- /**
- * aop规则。
- *
- * @type
- */
- $aop : {
- type : 'org.nutz.ioc.aop.config.impl.JsonAopConfigration',
- fields : {
- itemList : [
- ['org\.nutz\.demo3\.action\..+\..+', '(insert|update|delete|clear|save)', 'ioc:log'],
- ['org\.nutz\.demo3\.action\..+\..+', '(insert|update|delete|clear|save)', 'ioc:trans']
- ]
- }
- }
-
- };
这种AOP的使用方式应该是Nutz中最简单却最强大好用(兽(又是屏蔽词)兽出品,必属精品)
Nutz本身也就提供了TransAop与LogAop,ME只是对其做了一下汉化与加入了点个性化东西,大家使用默认的即可。
可以看到只要通过一个牛X的正则表达式,你就可以对工程中你想aop的地方插一脚了,so easy!
Jdbc.js
- /**
- * JDBC配置信息。
- *
- * @type
- */
- var jdbc = {
- dataSource : {
- type : "com.mchange.v2.c3p0.ComboPooledDataSource",
- events : {
- depose : 'close'
- },
- fields : {
- driverClass : 'com.mysql.jdbc.Driver',
- jdbcUrl : 'jdbc:mysql://localhost:3306/nutz',
- user : 'root',
- password : 'toor'
- }
- }
- };
数据库连接,直接copy官方文档中的,nutz中有对常用的四种数据库连接池的写法,方便大家copy。
log4j.properties
不贴代码了,没有意义,反正是帮你默认生成一个,如果有现成的,就不会再生成了。
后台代码还一个就是web.xml,新建工程里的web.xml只有首页信息,要加上nutz的配置信息才行。
web.xml
- <?xml version="1.0" encoding="UTF-8"?>
- <web-app xmlns:xsi="http://www./2001/XMLSchema-instance" xmlns="http://java./xml/ns/javaee" xmlns:web="http://java./xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java./xml/ns/javaee http://java./xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
- <display-name>Demo3</display-name>
- <!-- filter定义 -->
- <filter>
- <filter-name>entrance</filter-name>
- <filter-class>org.nutz.mvc.NutFilter</filter-class>
- <init-param>
- <param-name>modules</param-name>
- <param-value>org.nutz.demo3.action.Entrance</param-value>
- </init-param>
- </filter>
- <filter-mapping>
- <filter-name>entrance</filter-name>
- <url-pattern>/*</url-pattern>
- </filter-mapping>
- <!-- listener定义 -->
- <listener>
- <listener-class>org.nutz.mvc.NutSessionListener</listener-class>
- </listener>
- <!-- 首页 -->
- <welcome-file-list>
- <welcome-file>index.html</welcome-file>
- <welcome-file>index.htm</welcome-file>
- <welcome-file>index.jsp</welcome-file>
- <welcome-file>default.html</welcome-file>
- <welcome-file>default.htm</welcome-file>
- <welcome-file>default.jsp</welcome-file>
- </welcome-file-list>
- </web-app>
就是加入了org.nutz.mvc.NutFilter这个过滤器,拦截下所有的请求(/*)
然后通过上面配置的入口函数,进去到对应的Action中。
前台代码
前台代码,默认生成在page目录下,js目录下就是ExtJS的类库了,还有一个针对其部分空间二次封装的类库xExt。
来看一下生成页面文件
user.jsp
- <%@ page language="java" contentType="text/html; charset=UTF-8"
- pageEncoding="UTF-8"%>
- <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
- <html>
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
- <title>用户管理</title>
- <link rel='stylesheet' type='text/css' href='../../js/resources/css/ext-all.css'/>
- <link rel='stylesheet' type='text/css' href='../../js/resources/css/ext-patch.css' />
- <link rel='stylesheet' type='text/css' href='../../js/xExt/xExt.css' />
- <script type='text/javascript' src='../../js/ext-base.js'></script>
- <script type='text/javascript' src='../../js/ext-all.js'></script>
- <script type='text/javascript' src='../../js/ext-lang-zh_CN.js'></script>
- <script type='text/javascript' src='../../js/xExt/xExt-base.js'></script>
- <script type='text/javascript' src='../../js/xExt/xExt.js'></script>
- <script type='text/javascript' src='../../js/s.js'></script>
- <script type='text/javascript' src='user.js'></script>
- </head>
- <body>
- <div id='userDiv' style='width:100%;height:100%;'></div>
- </body>
- </html>
可以看到这里的代码也是非常简洁的,大部分是对js,css文件的导入。
body标签中只有一个div,生成的grid会绑定到该div上。
user.js
- Ext.onReady(function() {
-
- // xExt式样初始化(路径设定,默认当前为二级目录)
- xExt.css.init();
-
- // 配置Grid
- var gridConfig = xExt.grid.GridConfig({
- // 工程名称
- webroot : 'Demo3',
- // 主模块名称
- mainModule : 'admin',
- // 子模块名称
- subModule : 'user',
- // 列相关信息
- columns : [{
- name : 'id',
- type : 'int',
- header : '编号',
- hidden : true,
- filterable : false,
- sortable : false
- }, {
- name : 'name',
- type : 'string',
- header : '用户名称',
- hidden : false,
- filterable : true,
- sortable : true
- }, {
- name : 'age',
- type : 'int',
- header : '年龄',
- hidden : false,
- filterable : true,
- sortable : true
- }, {
- name : 'birthday',
- type : 'date',
- header : '生日',
- hidden : false,
- filterable : true,
- sortable : true
- }, {
- name : 'phone',
- type : 'string',
- header : '电话',
- hidden : false,
- filterable : true,
- sortable : true
- }],
-
- queryMode : 1,
-
- editableGrid : false,
-
- basicGridConfig : {
- // 新建按钮
- needInsertButton : true,
- // 窗口控件
- insertFormItems : [{
- xtype : 'fieldset',
- title : '基本信息',
- layout : 'form',
- style : xExt.css.fieldset.margins_7px,
- defaults : {
- width : xExt.css.form.fieldWidth
- },
- items : [{
- xtype : 'hidden',
- fieldLabel : '编号',
- name : 'id'
- }, {
- xtype : 'textfield',
- fieldLabel : '用户名称',
- name : 'name',
- allowBlank : true,
- maxLength : 50
- }, {
- xtype : 'numberfield',
- fieldLabel : '年龄',
- name : 'age',
- allowBlank : true,
- maxLength : 50
- }, {
- xtype : 'datefield',
- fieldLabel : '生日',
- name : 'birthday',
- allowBlank : true,
- maxLength : 50,
- format : 'Y-m-d'
- }, {
- xtype : 'textfield',
- fieldLabel : '电话',
- name : 'phone',
- allowBlank : true,
- maxLength : 50
- }]
- }],
- // 更新按钮
- needUpdateButton : true,
- // 窗口控件
- updateFormItems : [{
- xtype : 'fieldset',
- title : '基本信息',
- layout : 'form',
- style : xExt.css.fieldset.margins_7px,
- defaults : {
- width : xExt.css.form.fieldWidth
- },
- items : [{
- xtype : 'hidden',
- fieldLabel : '编号',
- name : 'id'
- }, {
- xtype : 'textfield',
- fieldLabel : '用户名称',
- name : 'name',
- allowBlank : true,
- maxLength : 50
- }, {
- xtype : 'numberfield',
- fieldLabel : '年龄',
- name : 'age',
- allowBlank : true,
- maxLength : 50
- }, {
- xtype : 'datefield',
- fieldLabel : '生日',
- name : 'birthday',
- allowBlank : true,
- maxLength : 50,
- format : 'Y-m-d'
- }, {
- xtype : 'textfield',
- fieldLabel : '电话',
- name : 'phone',
- allowBlank : true,
- maxLength : 50
- }]
- }],
- // 删除按钮
- needDeleteButton : true,
- // 批量删除按钮
- needClearButton : true
- },
-
- // 默认双击事件,弹出更新窗口(在有更新窗口的前提下)
- listeners : {
- rowdblclick : true
- }
-
- });
-
- // 生成Grid
- var dataGrid = xExt.grid.GridPanel(gridConfig);
-
- });
可以看到,这里并没有直接使用Ext里面的控件,而是针对这个增删改查页面的需求,封装到了xExt这个类库中。
调用xExt.grid.GridConfig生成配置信息后,调用 xExt.grid.GridPanel就可以生成Grid了。
- // 新建按钮
- needInsertButton : true,
像这个新建按钮,只需要设置true,页面上就会多一个新增按钮,并会有一个弹出窗口,在窗口中可以添加新数据,省去了直接使用Ext生成按钮再生成窗口的一大段代码,总的来说,一样是遵循了代码复用的思想。
总结
以上代码都是使用代码生成工具生成的,可以看到,本人在遵循代码复用的基础上,尽可能的把共通代码提取出来,将变化的地方空出来,留给开发人员使用。生成的代码也基本具有可读性。
做这个代码生成工具的目的,算是对快速开发的一种尝试,Nutz项目中也有代码生成工具,只是目前貌似进度停止了,这里写的这个也是期望能起到一个抛砖引玉的作用,激发其大家的思考。
望Nutz的代码生成工具,早日Release。
***************************邪恶的分割线******************************
代码生成工具的原理,下次再讲
|