夸父 Java Web MVC开发框架,提供极简快速开发。
新建一个maven项目
pom.xml中引入框架jar包
<dependency>
<groupId>tech.yixiyun.framework</groupId>
<artifactId>kuafu-mvc</artifactId>
<version>0.0.7</version>
<type>jar</type>
</dependency>
pom.xml中还需要配置下build
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.*</include>
</includes>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>你的jdk版本,不低于11</source>
<target>你的jdk版本,不低于11</target>
<encoding>UTF-8</encoding>
<compilerArgument>-parameters</compilerArgument>
</configuration>
</plugin>
</plugins>
</build>
创建一个类,写一个main方法,启动项目
public class Main {
public static void main(String[] args) {
TomcatStarter.start();
}
}
创建一个Controller类
import tech.yixiyun.framework.kuafu.controller.BaseController;
import tech.yixiyun.framework.kuafu.view.View;
/**
* 演示Demo
* @author Yixiyun
* @version 1.0
* @date 2021-05-16 15:00
*/
public class DemoController extends BaseController {
public View add(){
return json("name", "zhangsan", "age", 14);
}
}
重启下应用,访问 localhost:80/demo/add,就会看到浏览器显示一个json结构的数据了
按照正常的web项目结构,在webapp文件夹下,创建一个hello.jsp 文件
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Demo</title>
</head>
<body>
Hello Kuafu
</body>
</html>
到DemoController中写一个新方法
public View hello() {
return jsp("hello");
}
重新下应用,访问 localhost:80/demo/hello,就会发现页面被打开了
框架会自动识别Controller组件,识别的依据就是类上或父类身上是否有@Controller注解。识别为Controller组件后,框架会继续分析它的方法,只要方法是public修饰的,并且返回类型是View的,框架就会自动生成路由映射。
路由对应的url生成规则是:
在刚写的hello方法上加一个@Route("/")
@Route("/")
public View hello() {
return jsp("hello");
}
现在重启下应用,访问 localhost:80/,就会发现,hello页面被打开了 (注意webapp目录下不要存在index.jsp、index.html等欢迎页)
在 DemoController 类上加一个 @Controller("abc")
@Controller("abc")
public class DemoController extends BaseController {
...
}
访问 localhost:80/abc/add,就会发现页面显示json数据了
BaseController 提供了很多方便的的方法,例如
写一个Service 业务类
/**
* 演示Service
* @author Yixiyun
* @version 1.0
* @date 2021-05-15 13:22
*/
public class DemoService extends BaseService {
public void add() {
LOGGER.info("执行了add方法");
}
}
任何类身上有@Service注解,或者父类身上有@Service注解,就会被识别为Service组件。
注意这里我们使用了LOGGER 日志工具类,它基于log4j2实现,做了优化,无需每个类声明一个静态变量,都用它提供的静态方法记录即可。在框架的默认配置中,它只显示info以上级别的信息,并且输出记录到控制台以及日志文件中。后面会讲到如何修改配置
在DemoController中注入DemoService实例
/**
* 演示Demo
* @author Yixiyun
* @version 1.0
* @date 2021-05-16 15:00
@Controller("abc")
*/
public class DemoController extends BaseController {
private DemoService demoService;
public View add() {
//在这里调用一下demoService的方法
demoService.add();
return json("name", "zhangsan", "age", 14);
}
...
}
重新在浏览器访问下 demo/add,就会发现控制台打印了日志
只要是框架组件,就会自动识别为Bean,并自动注入,并默认以单例模式实例化。
如果其他类想被纳入Bean管理,只需要类身上加上@Bean注解,注解可以控制实例化方式和是否懒加载。之后就可以通过BeanContext获取实例了。它也会被其他Bean类自动执行依赖注入。
接下来开始熟悉配置相关的,我们首先尝试修改一下tomcat的启动端口
在resources文件夹下新建一个名为 app-config.json 的文件
然后在文件中,按照 json 的写法写上
{
"system": {
"server": {
"port":8080
}
}
}
重启应用,就会发现控制台提示
******** ^^^ 应用启动成功 ^^^ 启动地址:http://localhost:8080/ ********
对于一个项目来说,一般会有两个配置文件,一个是生产环境的,一个是开发环境的。刚才那个 app-config.json 正常来说对应的就是生产环境配置文件,当然你也可以两个环境下都用它。那么如果你想为开发环境另弄一个配置文件怎么办呢?只需要新建一个 app-config-dev.json 配置文件就行了,项目打包时,注意把它移除。一旦框架检测到有 app-config-dev.json,就会优先加载它,忽略 app-config.json. 接下来你我们尝试配置一个开发环境的配置文件,在这个配置里,我们干两个事,一个是tomcat端口改为9090,另一个是将日志的记录级别改为DEBUG,并且让日志不输出到文件,只在控制台打印。
{
"system": {
"server": {
"port":8080
},
"run": {
"logger": {
"configuration": {
"loggers": {
"logger": [{
"name": "kuafuLogger",
"level": "debug",
"additivity": "false",
"AppenderRef": [
{"ref": "dev_console"}
]
}]
}
}
}
}
}
}
你可能会觉得这个日志的配置怎么这么麻烦,这个是log4j2 日志框架官方提供的 json 配置方式,框架本身提供的预配置 其实已经不需要怎么改了,如果就是有特别需要,请自行查阅 log4j2 官方文档进行自定义配置,但一定要注意 ** logger的name 一定不要改名字,除非你不想用框架提供的LOGGER工具类 **
回到配置文件上,你可能会有疑问,配置为什么要这么写,规则是什么?其实很简单,框架提供了一个预配置 default-config.json 文件,你只需要按照预配置的 json结构,在你的配置文件中去重写你想改变的配置项即可。框架会用 你的配置去覆盖 预配置。下面是default-config.json 的内容
{
"system": {
//server相关配置
"server": {
"port": 80,
"contextPath": "/",
"connectionTimeout": "60000", //接收到请求后,等待处理的超时时间
"listen": "*", //如果服务器有多个网卡,而应用只需要监听某一个网卡的请求,就在这里配置上网卡的hostname或者ip。*代表服务器所有网卡
"compress": "off", //是否对文本进行压缩,三个值可用,on、off、force(强制)
"protocol": "HTTP/1.1", //暂时支持 HTTP/1.1 、AJP/1.3两种协议 HTTP/2目前未看到明显性能提升,且要求较多
"errorPages": { //错误页面,一般只对嵌入式服务器有效
"500": "/500.ftl", //发生500时显示的页面
"404": "/404.ftl" //发生404时显示的页面
}
},
//启动阶段
"boot": {
"tomcat": { //针对Tomcat内嵌服务器的启动阶段的一些配置
"scanJars": false, //tomcat启动时是否扫描所有jar包,用于TLD和web-fragment扫描,这会增加启动时间
"uriEncoding": "UTF-8",
"minThread": 10, //tomcat处理请求的最少线程数
"maxThread": 200, //tomcat处理请求的并发线程数,超过的请求会被放入等待队列中
"maxWaitCount": 100 //最多可等待的请求数,超过后会拒绝请求
},
"preHandler": "" //前置处理器,在启动工作开始前执行的工作
},
//运行阶段
"run": {
//开发模式还是生产模式
"mode": "dev",
//是否启用热加载,需要配合jrebel使用
"hotdeploy": false,
//是否启用应用监控,用于实时查看服务器和应用的运行状态
"monitor": {
"enable": true
},
//请求相关
"request": {
//是否支持跨域
"cors": true,
//可以请求的资源路径
"resources": {
//只有匹配这些规则的请求,才会被框架处理,否则交由Servlet容器处理,语法同Filter的url-pattern语法
"include": ["/*"],
//在include的基础上,匹配这些规则的请求,将跳过拦截器处理,直接请求。语法同Filter的url-pattern语法
"exclude": ["*.ico"]
},
"session": {
"manager": "tech.yixiyun.framework.kuafu.controller.session.ServletSessionManager" //用于获取Session实例的类,需要实现ISessionManager接口
},
"upload": {
"singleMaxSize": 51200, //上传的单文件最大大小,单位K,默认50M
"totalMaxSize": 51200, //上传时单次请求最大大小,单位K,默认50M
"savePath": "/upload/{date:yyyyMMdd}/{uuid}.{suffix}", //上传文件的默认保存位置,以webroot位置为基准
"notAllowSuffix": [ //不允许上传的文件类型
//不允许上传的文件类型
".exe",
".bat",
".sh",
".dll",
".jsp",
".php",
".jar",
".class",
".js",
".asp",
".jspx",
".html",
".htm",
".shtml",
".ftl",
".py"
]
}
},
//模板相关
"template": {
"freemarker": { //freemarker模板,
"config": { //配置项参考文档:https://freemarker.apache.org/docs/api/freemarker/template/Configuration.html#setSetting-java.lang.String-java.lang.String-
"ContentType": "text/html; charset=UTF-8",
"TemplatePath": "[/,classpath:templates/]",
"locale": "zh_CN",
"template_exception_handler": "rethrow",
"date_format": "yyyy-MM-dd",
"time_format": "HH:mm",
"datetime_format": "yyyy-MM-dd HH:mm",
"template_update_delay": "2000",//单位毫秒,默认5000,开发环境设置低一点,生产环境建议设高
"default_encoding": "UTF-8",
"number_format": "0.###",
"incompatible_improvements": "2.3.30"
}
}
},
//响应相关
"response": {
},
//数据库相关
"db": {
//事务相关
"transaction": {
//自动开启事务的方法名前缀
"autoOpenPrefix": ["add","modify","del","update","do","save", "create", "alter", "insert"],
//默认的事务隔离级别,参考TransactionLevel枚举类中的定义
"defaultLevel": "REPEATABLE_READ",
//事务执行超时警告,一旦一个事务执行时间超过设定的毫秒值,就打印警告语句
"timeoutWarning": 1000
}
},
//日志相关
"logger": {
"default": "kuafuLogger", //默认使用的记录器
"configuration": { //配置参考log4j官网,链接http://logging.apache.org/log4j/2.x/manual/configuration.html#JSON
"status": "error",
"name": "kuafu",
"appenders": {
"appender": [
{
"type": "Console",
"name": "console",
"PatternLayout": {
"pattern": "%-d{MM-dd HH:mm:ss} [%p]-[%C{1}.%M()]: %m %n"
}
},
{ //开发环境用的打印,可以显示颜色
"type": "Console",
"name": "dev_console",
"PatternLayout": {
"pattern": "%-d{MM-dd HH:mm:ss} [%highlight{%-5level}{INFO=Magenta, TRACE=White, DEBUG=Blue}]-[%highlight{%C{1}.%M()}{INFO=Magenta, TRACE=White, DEBUG=Blue}]: %highlight{%m%n}{INFO=Magenta, TRACE=White, DEBUG=Blue}"
}
},
{
"type": "RollingFile",
"name": "file",
"fileName": "logs/run.log", //日志文件保存路径
"filePattern" : "logs/%d{MM-dd}_%i.log.gz", //分割后的命名规则
"PatternLayout": {
"pattern": "%-d{MM-dd HH:mm:ss} [%p]-[%C{1}.%M()]: %m %n"
},
"Policies": {
"CronTriggeringPolicy": {
"schedule": "0 0 0 * * ? *", //日志每天零点分割一次
"evaluateOnStartup": true
},
"SizeBasedTriggeringPolicy": { "size": "60M" } //单个日志文件超过60M也分割一次
},
"DefaultRolloverStrategy": {
"max": 10 //所有的日志文件最多保留10个
}
}
]
},
"loggers": {
"logger": [{
"name": "kuafuLogger",
"level": "info",
"additivity": "false",
"AppenderRef": [ //默认会向这两个地方写入,上线后,可以不向console写入
{
"ref": "console"
},
{
"ref": "file"
}
]
}],
"root": {
"level": "info",
"AppenderRef": {
"ref": "console"
}
}
}
}
}
},
"stop": {
//系统停止运行阶段
}
}
}
你应该会注意到,框架依赖的配置都写在了 system 配置项下,为了和你的项目相关的配置区分开,极力建议你,不要把你的 项目相关的配置写在 system 下,而应该另外建一个 键,例如 app 或者 project
{
"system": {
//这里都是框架依赖的配置项
},
"app": {
//这里写与你项目相关的配置,例如数据库配置、缓存配置等等
}
}
为了方便你从配置文件中读取配置,这里提供了一个AppConfig工具类,它可以根据 key (大部分配置项的key路径我们都在ConfigKey中定义了) 从 所有生效的配置文件中提取配置数据,并转成对应结构,例如
List<String> prefixs = AppConfig.getAsStringList("system.run.db.autoOpenPrefix");
Integer port = AppConfig.getAsInt("system.server.port");
HashMap<String, String> errorMap = AppConfig.getAsStringMap(ConfigKey.SERVER_ERRORPAGES);
接下来我们创建一个实体类
package tech.yixiyun.demo.user;
import tech.yixiyun.framework.kuafu.domain.BaseDomain;
import java.util.Date;
/**
* 用户实体类
*
* @author Yixiyun
* @version 1.0
* @date 2021-05-17 14:20
*/
public class User extends BaseDomain {
//唯一标识
private Integer id;
//姓名
private String name;
//爱好
private String favors[];
//性别,true代表男,false代表女
private Boolean gender;
//出生年月日
private java.sql.Date birthday;
//记录的创建时间
private Date createTime;
//getter setter 这里我就不写了,你一定要写上
}
创建完,我们来看一下Controller方法如何自动解析和转换请求参数。回到 hello.jsp 中,我们构建一个表单。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Demo</title>
<style>
form {
display: flex;
flex-direction: column;
}
</style>
</head>
<body>
<form action="/demo/add" method="post">
<label >
姓名:
<input type="text" name="user.name" />
</label>
<label >
爱好:
<input type="checkbox" name="user.favors" value="游泳">游泳
<input type="checkbox" name="user.favors" value="健身">健身
<input type="checkbox" name="user.favors" value="写代码">写代码
</label>
<label >
性别:
<input type="radio" name="user.gender" value="1">男
<input type="radio" name="user.gender" value="0">女
</label>
<label >
出生年月:
<input type="date" name="user.birthday" />
</label>
<label >
创建时间:
<input type="datetime-local" name="user.createTime" />
</label>
<button type="submit">提交</button>
</form>
</body>
</html>
可以看出,这个表单最终会提交到 demo/add 这个地址,所以回到 DemoController 的add方法,我们改动一下,把接收到的user对象返回给浏览器
public class DemoController extends BaseController {
private DemoService demoService;
public View add(User user) {
demoService.add();
return json(user);
}
public View hello() {
return jsp("hello");
}
}
现在可以尝试访问 /demo/hello 打开表单页,然后输入一些信息,点击提交看一下效果了,正常来说你应该能看到一个json结构的字符串,里面正是你提交的数据。如果失败了,请检查下pom.xml 中编译插件是否配置上了 -parameters
框架会自动解析请求参数,根据请求参数名和方法参数名进行匹对处理。同时,**框架会根据请求的Content-Type,自动判断该如何转化参数,所以即使使用 axios 等库,以application-json形式发起请求,无需任何注解,框架也可正常解析请求参数 **
接下来我们尝试把数据存储到数据库中,首先我们注册一个数据源,框架提供了阿里的Druid数据源支持,所以这里以注册Druid数据源为例:
在maven中添加druid依赖和mysql依赖
<dependency> <!-- 数据库连接池 -->
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${druid.version}</version>
</dependency>
<dependency><!-- mysql-connector-java -->
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
在app-config-dev.json 配置文件中添加数据源配置
{
"app": {
"datasource": { //数据源配置,注意值都是string类型,不能用其他类型,具体配置项参考 https://github.com/alibaba/druid/wiki/DruidDataSource%E9%85%8D%E7%BD%AE%E5%B1%9E%E6%80%A7%E5%88%97%E8%A1%A8
"name": "main", //数据源标识
"url": "jdbc:mysql://你的数据库连接?useSSL=false&autoReconnect=true&zeroDateTimeBehavior=CONVERT_TO_NULL&serverTimezone=Asia/Shanghai",
"username": "数据库用户名",
"password": "数据库密码",
"keepAlive": "true"
}
}
}
写一个IDataSourceProvider实现类
public class DataSourceProvider implements IDataSourceProvider {
@Override
public DataSourceDefinition[] get() {
DataSourceDefinition[] definitions = {new DruidDataSourceDefinition("app.datasource")};
return definitions;
}
}
完成以上步骤,数据源就完成注册了。接着我们来根据Domain类,自动生成表结构,打开DemoController类,写一个方法
/**
* 根据Domain类生成或者更新表结构
* @return
*/
public View generateTable() {
DbKit.createOrAlterAllSingleTable();
return jsonSuccess();
}
然后我们重启系统,通过浏览器访问 /demo/generateTable,返回{state:"SUCCESS"} 就代表执行成功,打开数据库我们应该就可以看到创建成功的表了。
接着我们尝试向数据库添加一条数据,回到DemoService 中的add方法,我们将它改动一下:
public void add(User user) {
LOGGER.info("执行了add方法");
insertOne(user);
}
然后改一下DemoController的add方法
public View add(User user) {
demoService.add(user);
return json(user);
}
然后我们访问一下 /demo/hello,在页面填写一些数据,点击提交试试效果吧
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。
1. 开源生态
2. 协作、人、软件
3. 评估模型