JLight源码与库文件下载链接: 提取码:5by4
public class ApplicationTest {
public static void main(String[] args) {
GlobalConfig.getConfig().setDatabaseName("test").setRecreate(false);
JLightApplication.start();
}
}
也可以使用注解进行配置
@Config(port = 8089)
public class ApplicationTest {
public static void main(String[] args) {
JLightApplication.start();
}
}
@Api("/v1")
public class ControllerTest {
@Api(value = "/method1",method = ControllerConstance.GET_METHOD)
public Ajax method1(){
System.out.println("hello world");
return ReturnBody.success("hello world");
}
}
@Config(port = 8089,rootPath = "root",scanPackage = "com.baidu")
@DataBase(value = "test_database",username = "root1",password = "121212")
public class ApplicationTest {
public static void main(String[] args) {
JLightApplication.start(ApplicationTest.class);
}
}
注解配置方式是由两个组合注解实现的,@Config和@DataBase。
@Config可以配置端口号、项目根路径、扫描包路径。其中扫描包路径需要填项目的groupId名称。
@DataBase可以配置数据库名称、数据库账号密码、最大连接数、最小连接数、最长等待时间等。
所有配置信息均有默认值,即不用配置任何信息JLightApplication也可以正常启动,默认值可在常量包中查看。
注解配置方式需要传入当前类信息,以让JLight框架获取注解信息。
public class ApplicationTest {
public static void main(String[] args) {
GlobalConfig.getConfig()
.setPort(8089)
.setRootPath("root")
.setScanPackage("com.baidu")
.setDatabaseName("test_database")
.setUsername("root1")
.setPassword("121212");
JLightApplication.start();
}
}
全局配置方式由全局配置对象实现,即GlobalConfig对象,该类为单例模式,只能由getConfig()方法获取实例。
全局配置对象对@Config与@DataBae注解中所有配置均做了对应的实现,且默认值同样由常量包给出。
即不用获取全局配置对象并设置配置信息JLightApplication也可以正常启动。默认值可在常量包中查看。
无论是哪种配置方式,底层均由GlobalConfig全局配置对象保存配置信息,使用者可以通过getConfig()方法获取实例后得到启动的配置信息。在JLightApplication.start()方法中还可以传入自定义任务,可以在Tomcat启动前后执行对应任务。
public class ApplicationTest {
public static void main(String[] args) {
JLightApplication.startWithTask(
() -> System.out.println("启动前任务!"),
() -> System.out.println("启动后任务!"));
}
}
@Api("/v1")
public class ControllerTest {
@Api(value = "/method1",method = ControllerConstance.GET_METHOD)
public Ajax method1(ControllerHelper helper){
String test = helper.get("test");
System.out.println(test);
return ReturnBody.success("hello world");
}
@Api("/method2")
public Ajax method2(){
return ReturnBody.fail("失败信息提示");
}
@Api(value = "/method3",method = ControllerConstance.POST_METHOD)
public String method3(HttpServletRequest request, HttpServletResponse response){
return request.getLocalName();
}
}
控制层的接口映射通过@Api注解实现,该注解可以配置映射路径与映射方法两个参数。
映射路径规范要求以“/”开头,建议将相同模块的功能放在同一个Controller中,如对用户的登录、注册、忘记密码、找回密码等。
映射方法参数可以限制HttpRequest请求访问,若将映射方法设为“GET”方法,则除“GET”方法以外的方法均无法访问。其中所有@Api接口的映射方法参数默认值均为任意方法类型。
方法的返回类型决定了是否以JSON格式将数据返回给前端,仅当返回类型为Ajax类型时,处理器才会对返回值做JSON格式转换并返回,其它类型均不做返回处理。Ajax类型即可通过new的方式手动生成,也可以通过ReturnBody类进行快速生成,ReturnBody类仅对常见的Ajax返回值做封装处理,若返回成功的200与返回失败的500等。
方法的参数个数决定了处理器的参数注入方式,若方法中没有参数,则处理器不会对该方法进行参数注入;若有一个参数,则处理器会将ControllerHelper对象进行初始化并注入;若有两个参数,则处理器会将HttpServletRequest和HttpServletResponse对象进行注入。
ControllerHelper对象是JLight框架提供的快速处理控制层业务逻辑的帮助类,用以实现提取前端参数、获取Session对象、获取Cookie对象、实现文件的上传与下载、实现Token的回写、校验、删除等常见功能,具体方法可以查看ControllerHelpr类的源码。
/**
* 控制层请求帮助类
* 用以提取参数、session、cookie、文件的上传与下载
*/
public class ControllerHelper {
private final HttpServletRequest request;
private final HttpServletResponse response;
private String uploadPath;
private final Log logger = LogFactory.get(ControllerHelper.class);
public ControllerHelper(HttpServletRequest request, HttpServletResponse response){
this.request = request;
this.response = response;
}
public String getUploadPath() {
return uploadPath;
}
public void setUploadPath(String uploadPath) {
this.uploadPath = uploadPath;
}
public String get(String parameterName){
return this.request.getParameter(parameterName);
}
public int getInt(String parameterName){
return Integer.parseInt(get(parameterName));
}
public double getDouble(String parameterName){
return Double.parseDouble(get(parameterName));
}
public boolean getBool(String parameterName){
return Boolean.parseBoolean(get(parameterName));
}
public HttpSession getSession(){
return this.request.getSession();
}
public Cookie getCookie(String cookieName){
return ServletUtil.getCookie(request,cookieName);
}
public <T> T getBean(Class<T> tClass){
try {
T instance = tClass.newInstance();
Field[] fields = ReflectUtil.getFields(tClass);
for (Field field : fields) {
String parameter = get(field.getName());
if (null != parameter) {
Method setMethod;
if (!field.getName().startsWith("is")) {
setMethod = ReflectUtil.getMethodByNameIgnoreCase(tClass, "set" + field.getName());
} else {
setMethod = ReflectUtil.getMethodByNameIgnoreCase(tClass, "set" + field.getName().substring(2));
}
String parameterType = setMethod.getParameterTypes()[0].getName();
switch (parameterType) {
case "int":
case "java.lang.Integer":
setMethod.invoke(instance, Integer.parseInt(parameter));
break;
case "double":
case "java.lang.Double":
setMethod.invoke(instance, Double.parseDouble(parameter));
break;
case "float":
case "java.lang.Float":
setMethod.invoke(instance, Float.parseFloat(parameter));
break;
case "boolean":
case "java.lang.Boolean":
setMethod.invoke(instance, Boolean.parseBoolean(parameter));
break;
case "java.lang.String":
setMethod.invoke(instance, parameter);
break;
}
}
}
return instance;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public String upload(HttpServletRequest request) throws Exception {
return upload(request,(Long) null, (String) null);
}
public String upload(HttpServletRequest request,Long max) throws Exception {
return upload(request,max, (String) null);
}
public String upload(HttpServletRequest request,String... types) throws Exception {
return upload(request,null, types);
}
public String upload(HttpServletRequest request,Long max, String... types) throws Exception {
DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
factory.setSizeThreshold(Integer.MAX_VALUE);
File uploadTemp = new File(uploadPath + "/temp");
if (!uploadTemp.exists())
uploadTemp.mkdirs();
factory.setRepository(uploadTemp);
upload.setFileSizeMax(max == null ? Long.MAX_VALUE : max);
upload.setSizeMax(max == null ? Long.MAX_VALUE : max);
List<FileItem> list = upload.parseRequest(request);
for (FileItem fileItem : list) {
if (!fileItem.isFormField() && fileItem.getName() != null && !"".equals(fileItem.getName())) {
String fileName = fileItem.getName();
File file = new File(uploadPath);
String suffix = fileName.substring(fileName.lastIndexOf("."));
//根据文件后缀名过滤非法文件
boolean flag = false;
if (null != types) {
for (String type : types) {
if (type.equalsIgnoreCase(suffix)) {
flag = true;
break;
}
}
}
if (!flag) {
logger.error("文件类型不合法!");
return null;
}
if (!file.exists())
file.mkdirs();
fileItem.write(new File(uploadPath, fileName));
logger.debug(fileName + "文件上传成功!上传至 : " + uploadPath);
return fileName;
}
}
return null;
}
public void download(String downloadPath) {
FileInputStream fileInputStream;
ServletOutputStream servletOutputStream;
try {
fileInputStream = new FileInputStream(downloadPath);
response.setContentType(null);
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(new File(downloadPath).getName(), "UTF-8"));
int len;
byte[] bytes = new byte[1024];
servletOutputStream = response.getOutputStream();
while ((len = fileInputStream.read(bytes)) > 0) {
servletOutputStream.write(bytes, 0, len);
}
logger.debug(downloadPath + "文件下载成功!");
servletOutputStream.close();
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
logger.error(downloadPath + "文件下载失败!");
}
}
public void setToken(String cookieName,String cookieValue){
setToken(cookieName,cookieValue,30 * 60,"/");
}
public void setToken(String cookieName,String cookieValue,int maxAge,String cookiePath){
Cookie cookie = new Cookie(cookieName, TokenMaker.newToken(cookieValue));
cookie.setMaxAge(maxAge);
cookie.setPath(cookiePath);
logger.debug("token生成成功!" + cookie.getValue());
response.addCookie(cookie);
}
public boolean checkToken(String cookieName,int expiredTime){
Cookie cookie = getCookie(cookieName);
if (cookie == null)
return false;
else
return TokenMaker.checkToken(cookie.getValue(),expiredTime);
}
public String getToken(String cookieName){
Cookie cookie = getCookie(cookieName);
if (cookie != null){
TokenMaker.parseToken(cookie.getValue());
}
return null;
}
public void deleteCookie(String cookieName){
Cookie cookie = new Cookie(cookieName, null);
cookie.setMaxAge(0);
cookie.setPath("/");
response.addCookie(cookie);
}
}
public interface BeanTestDao {
boolean save(BeanTest beanTest);
BeanTest findByID(int id);
}
public class BeanTestDaoImpl extends BaseDaoImpl<BeanTest> implements BeanTestDao {
@Override
public boolean save(BeanTest beanTest) {
String sql = "insert into tb_test(`user_name`,`password`) values(#,#)";
return DML(sql,beanTest.getUsername(),beanTest.getPassword());
}
@Override
public BeanTest findByID(int id) {
String sql = "select * from tb_test where id=#";
return DQLForObject(sql,id);
}
}
持久层的开发由BaseDaoImpl类实现,该类是一个泛型类,在创建该类是确定对应的泛型以实现内部对象类型的自动转换。
BaseDaoImpl实现了BaseDao中的四个核心方法,接口源码如下:
public interface BaseDao<T> {
boolean DML(String sql,Object... params);
T DQLForObject(String sql,Object... params);
ResultSet DQLForResult(String sql, Object... params);
List<T> DQL(String sql, Object... params);
}
DML()方法用以执行增、删、改操作,将sql语句与可变参数传入即可。其中sql语句的参数变量占位符规范规定由“#”实现。
DQL()方法用以执行查询操作,将sql语句与可变参数传入即可。其中sql语句的参数变量占位符规范规定由“#”实现。
DQLForObject()与DQLForResult()底层由DQL()方法实现,分别获取查询结果中的第一个数据与查询结果的结果集。
此模块非开发必需流程,使用者可以自行建表也可以通过配置实体类注解自动建表
@Table("tb_test")
public class BeanTest {
@PrimaryKey
@AutoIncrement
@Column(name = "id",length = 11)
private int id;
@Column(name = "user_name",length = 50)
private String username;
@Column(name = "password",length = 32)
private String password;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
@Table注解用以标记数据库对应表名称。若不使用@Table注解,则该实体类不会被包扫描器捕捉,也就不会自动建表。
@Column注解用以标记数据库对应字段信息,包括字段名称、字段长度、是否为notNull,若不使用@Column注解则该字段不会被映射到数据库中。
@PrimaryKey注解用以标记数据库表中的主键。
@AutoIncrement注解用以标记数据库表中的自增长字段。
定时任务往往是为了解决一些周期性、自动化、被动式的业务流程,JLight框架提供了两种定时任务的实现方法,注解式和声明式。因为JLight框架中的定时任务处理机制是基于JDK自带的定时服务工具包实现的,因此无需额外引入其它依赖即可使用。
/**
* 定时任务处理类
* 基于JDK自带的定时任务处理机制实现
* 可选择定时执行与延迟执行两种方式
* 执行频率可选择秒级、分钟级、小时级、日级
* Component注解用于将此对象交给JFaster的IOC容器管理
* Schedule注解用于启动定时任务
*/
public class ScheduleTest {
/**
* 该方法从2021-06-17 14:00:00开始执行,每5秒执行一次
*/
@Cron(startTime = "2021-06-17 14:00:00",period = 5,unit = ScheduleUnit.SECOND)
public void method1(){
System.out.println("方法一:定时任务开始执行。。。");
}
/**
* 该方法3小时后开始执行,每1小时执行一次
*/
@Cron(initialDelay = 3,period = 1,unit = ScheduleUnit.HOUR)
public void method2(){
System.out.println("方法二:定时任务开始执行。。。");
}
}
在此测试案例中,定义了两种不同类型的定时任务,一种是指定时间执行,另外一种是延迟执行。其中period频率参数和unit执行时间单位参数为必选项。指定时间方法需要传入开始执行时间参数,而延迟执行方法需要传入延迟时间参数。
/**
* JLight定制的轻量级定时任务
*/
public class Schedule {
/**
* JDK自带的定时服务,并声明10个线程并发执行
*/
private static final ScheduledExecutorService service = Executors.newScheduledThreadPool(10);
/**
* 自定义定时服务
* @param task 定时任务
* @param initialDelay 起始时间
* @param period 执行频率
* @param timeUnit 执行频率枚举
*/
public static void doTask(Runnable task,long initialDelay,long period,TimeUnit timeUnit){
service.scheduleAtFixedRate(task,initialDelay,period,timeUnit);
}
/**
* 自定义定时服务
* @param task 定时任务
* @param startTime 起始时间
* @param period 执行频率
* @param timeUnit 执行频率枚举
*/
public static void doTask(Runnable task,String startTime,long period,TimeUnit timeUnit,DateUnit dateUnit){
long betweenStart = DateUtil.between(DateUtil.date(), DateUtil.parse(startTime), dateUnit);
service.scheduleAtFixedRate(task,betweenStart,period,timeUnit);
}
/**
* 按秒为单位执行定时任务
* @param task 线程任务
* @param initialDelay 开始时间
* @param period 执行频率
*/
public static void doBySeconds(Runnable task,long initialDelay,long period){
service.scheduleAtFixedRate(task,initialDelay,period,TimeUnit.SECONDS);
}
/**
* 按秒为单位执行定时任务
* @param task 线程任务
* @param startTime 开始时间
* @param period 执行频率
*/
public static void doBySeconds(Runnable task,String startTime,long period){
long betweenStart = DateUtil.between(DateUtil.date(), DateUtil.parse(startTime), DateUnit.SECOND);
service.scheduleAtFixedRate(task,betweenStart,period,TimeUnit.SECONDS);
}
/**
* 按分钟为单位执行定时任务
* @param task 线程任务
* @param initialDelay 开始时间
* @param period 执行频率
*/
public static void doByMinutes(Runnable task,long initialDelay,long period){
service.scheduleAtFixedRate(task,initialDelay,period,TimeUnit.MINUTES);
}
/**
* 按分钟为单位执行定时任务
* @param task 线程任务
* @param startTime 开始时间
* @param period 执行频率
*/
public static void doByMinutes(Runnable task,String startTime,long period){
long betweenStart = DateUtil.between(DateUtil.date(), DateUtil.parse(startTime), DateUnit.DAY);
service.scheduleAtFixedRate(task,betweenStart,period,TimeUnit.MINUTES);
}
/**
* 按小时为单位执行定时任务
* @param task 线程任务
* @param initialDelay 开始时间
* @param period 执行频率
*/
public static void doByHours(Runnable task,long initialDelay,long period){
service.scheduleAtFixedRate(task,initialDelay,period,TimeUnit.HOURS);
}
/**
* 按小时为单位执行定时任务
* @param task 线程任务
* @param startTime 开始时间
* @param period 执行频率
*/
public static void doByHours(Runnable task,String startTime,long period){
long betweenStart = DateUtil.between(DateUtil.date(), DateUtil.parse(startTime), DateUnit.HOUR);
service.scheduleAtFixedRate(task,betweenStart,period,TimeUnit.HOURS);
}
/**
* 按天为单位执行定时任务
* @param task 线程任务
* @param initialDelay 开始时间
* @param period 执行频率
*/
public static void doByDays(Runnable task,long initialDelay,long period){
service.scheduleAtFixedRate(task,initialDelay,period,TimeUnit.DAYS);
}
/**
* 按天为单位执行定时任务
* @param task 线程任务
* @param startTime 开始时间
* @param period 执行频率
*/
public static void doByDays(Runnable task,String startTime,long period){
long betweenStart = DateUtil.between(DateUtil.date(), DateUtil.parse(startTime), DateUnit.DAY);
service.scheduleAtFixedRate(task,betweenStart,period,TimeUnit.DAYS);
}
}
通过源码可知,JLight框架中的Schedule类提供了一系列静态方法供使用者定义定时任务,同样也分为指定时间执行方法和延迟执行方法两种,使用者可以通过**Schedule.doxxx()**等方法直接声明需要定时执行的任务,其中Task为JLight定义的任务类,其继承自线程接口,以此实现多线程多任务并发执行。