前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >JLight——JavaWeb的轻量级开发框架

JLight——JavaWeb的轻量级开发框架

作者头像
易兮科技
发布2021-12-08 11:48:57
1K0
发布2021-12-08 11:48:57
举报
文章被收录于专栏:CSDN博客专栏CSDN博客专栏

JLight——JavaWeb的轻量级开发框架

JLight源码与库文件下载链接: 提取码:5by4

一、项目结构

  • annotation——注解层
    1. @Api注解:控制层中用以标记请求路径
    2. @AntoIncrement:持久层自动建库中的自增字段标记
    3. @Column:持久层中的字段名
    4. @Config:全局配置注解
    5. @Cron:定时任务注解
    6. @DataBase:数据库配置注解
    7. @PrimaryKey:持久层中主键标记
    8. @Table:持久层自动建库中的表名标记
  • constance——常量包
    1. ConfigConstance:全局配置常量
    2. ControllerConstance:控制层常量
    3. DataBaseConstance:数据库配置常量
    4. TomcatConstance:Tomcat配置常量
  • controller——控制层
    1. ControllerHandler:控制层注解处理类
    2. ControllerHelper:控制层逻辑帮助类
    3. DispatcherServlet:核心请求分发器
    4. GlobalFilter:全局过滤器
    5. MappingContainer:请求映射路径容器
    6. RequestHandler:核心请求处理器
    7. ReturnBody:JSON格式的返回体
  • core——核心模块
    1. GlobalConfig:全局配置信息
    2. JLightApplication:核心应用启动类
    3. ModuleHandler:注解处理接口
    4. TomcatStarter:Tomcat启动器
  • dao——持久层
    1. BaseDao:持久层基类接口
    2. BaseDaoImpl:持久层基类实现类
    3. TableHandler:持久层自动建表处理类
  • entity——实体类
    1. Ajax:Ajax实体类
    2. MappingBean:存储映射对象的实体类
  • security——权限校验模块
    1. PermissionAble:权限接口
    2. PermissionValidator:权限校验工具
    3. TokenMaker:Token生成器
  • task——定时任务模块
    1. Schedule:JFaster定制的轻量级定时任务
    2. ScheduleHandler:定时任务注解处理类
    3. ScheduleUnit:定时任务执行频率单位
    4. Task:JFaster线程任务
  • utils——工具包
    1. ConnectionUtil:数据库连接对象
    2. JdbcUtil:获取数据源对象工具类
    3. MailUtil:邮件发送与收取工具类
    4. ScannerUtil:包扫描工具类

二、框架体验

  • 第一步:建立主类,调用JLight.start()系列方法。可以使用全局配置对象进行配置
代码语言:javascript
复制
public class ApplicationTest {

    public static void main(String[] args) {
        GlobalConfig.getConfig().setDatabaseName("test").setRecreate(false);
        JLightApplication.start();
    }
}

也可以使用注解进行配置

代码语言:javascript
复制
@Config(port = 8089)
public class ApplicationTest {

    public static void main(String[] args) {
        JLightApplication.start();
    }
}
  • 第二步:建立ControllerTest,编写API接口,将请求方法限制为仅允许GET请求
代码语言:javascript
复制
@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");
    }
}

三、核心方法

1.全局配置

  • 注解配置方式
代码语言:javascript
复制
@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框架获取注解信息。

  • 全局配置对象方式
代码语言:javascript
复制
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启动前后执行对应任务。

代码语言:javascript
复制
public class ApplicationTest {

    public static void main(String[] args) {
        JLightApplication.startWithTask(
                () -> System.out.println("启动前任务!"), 
                () -> System.out.println("启动后任务!"));
    }
}

2.控制层开发

代码语言:javascript
复制
@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类的源码。

代码语言:javascript
复制
/**
 * 控制层请求帮助类
 * 用以提取参数、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);
    }
}

3.持久层开发

代码语言:javascript
复制
public interface BeanTestDao {

    boolean save(BeanTest beanTest);

    BeanTest findByID(int id);
}
代码语言:javascript
复制
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中的四个核心方法,接口源码如下:

代码语言:javascript
复制
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()方法实现,分别获取查询结果中的第一个数据与查询结果的结果集。

4.自动建表

此模块非开发必需流程,使用者可以自行建表也可以通过配置实体类注解自动建表

代码语言:javascript
复制
@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注解用以标记数据库表中的自增长字段。

3.定时任务

定时任务往往是为了解决一些周期性、自动化、被动式的业务流程,JLight框架提供了两种定时任务的实现方法,注解式和声明式。因为JLight框架中的定时任务处理机制是基于JDK自带的定时服务工具包实现的,因此无需额外引入其它依赖即可使用。

代码语言:javascript
复制
/**
 * 定时任务处理类
 * 基于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执行时间单位参数为必选项。指定时间方法需要传入开始执行时间参数,而延迟执行方法需要传入延迟时间参数。

代码语言:javascript
复制
/**
 * 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定义的任务类,其继承自线程接口,以此实现多线程多任务并发执行。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2021-09-09 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • JLight——JavaWeb的轻量级开发框架
    • 一、项目结构
      • 二、框架体验
        • 三、核心方法
          • 1.全局配置
          • 2.控制层开发
          • 3.持久层开发
          • 4.自动建表
          • 3.定时任务
      相关产品与服务
      数据库
      云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档