JOxygen - 轻量级 Java 框架


Apache
跨平台
Java

软件简介

JOxygen

轻量级Java框架

介绍

一个轻量级Java框架

oxygen-core 核心部分

  • 基于cglib的aop实现

  • 提供缓存管理和基于注解的缓存,内置LocalCache和Ehcache实现,可扩展

  • 配置管理,支持${attrs.key:defaultValue}表达式获取配置

  • 加解密管理,提供加解密服务内置基础加密实现,例如SHA-1、SHA-256、MD5

  • 异常管理,提供异常包装,统一异常编码,便于国际化

  • 提供基于构造器注入的ioc(原因:依赖链清晰,并可任意切换ioc实现)

  • 定时任务服务,内置提供了基于注解的定时任务服务

    ├─ src/main
      │─ java/…/core  //oxygen-core代码目录
      │  │- aop //aop实现目录
      │  │- cache //缓存实现目录
      │  │- config  //配置实现目录
      │  │- constant  //常量目录
      │  │- convert  //类型转换实现目录
      │  │- crypto  //密码加密目录
      │  │- domain  //基础实体目录
      │  │- exception  //异常管理目录
      │  │- io  //io读写目录
      │  │- ioc  //ioc实现目录
      │  │- job  //定时任务实现目录
      │  │- scan  //类扫描实现目录
      │  │- util  //工具类目录
      │  │- Bootstrap.java  //框架启动引导类
      │  └- Plugin.java   //插件接口
      └─ resources/META-INF/services
         └- …core.Plugin  //Plugin服务实现配置文件

oxygen-jdbc jdbc实现

  • 小巧简单的jdbc实现,纯jdk实现,无第三方jar

  • 支持多数据源

  • 基于sql进行crud,不提供类似Hibernate的链式方法(原因:sql作为数据库领域的DSL,已经很自然优雅,Less is more)

    ├─ src/main
      │─ java/…/jdbc  //oxygen-jdbc代码目录
      │  │- config  //配置数据源目录
      │  │- handler  //处理器目录,包括结果集处理 行处理 列处理
      │  │- interceptor  //拦截器目录,拦截sql执行前后及异常
      │  │- record  //基础crud
      │  │- Jdbc.java  //Jdbc核心操作类,提供crud操作
      │  │- JdbcException.java  //jdbc异常封装
      │  └- JdbcPlugin.java   //jdbc插件,与oxygen-core配套使用
      └─ resources/META-INF/services
         │- …handler.ColumnHandler //列处理服务配置文件
         └- …core.Plugin  //增加jdbcPlugin服务实现,与oxygen-core配套使用

oxygen-web

  • 项目使用了Servlet3.0的ServletContainerInitializer接口

  • 在servlet中可自动加载,不需要在web.xml中配置

  • 使用@Router标记路由文件

  • 使用@Mapping标记请求路径处理方法

  • 参数绑定的取值域使用@Param,@HeaderParam,@CookieParam,@PathParam指定,默认为@Param

  • 参数绑定支持简单类型 + Map + 用户实体类

  • 支持返回Json、视图或自定义实现(文件下载等)

    ├─ src/main
      │─ java/…/web  //oxygen-web代码目录
      │  │- handler //参数绑定处理
      │  │- http //http请求解析
      │  │- mapping  //url映射,参数映射相关注解和实体
      │  │- router  //一个示例路由(获取服务器时间)
      │  │- server  //内置server接口和启动类
      │  │- view  //视图解析
      │  │- DefaultWebAppInitializer.java  //默认初始化实现
      │  │- DispatcherServlet.java  //路由分发器
      │  │- WebAppInitializer.java  //web自动初始化接口,提供给用户自定义使用
      │  │- WebContainerInitializer.java  //容器自动初始化
      │  │- WebConf.java  //web配置
      │  └- WebPlugin.java  //web插件
      └─ resources/META-INF/services
          │- …ServletContainerIntializer //servlet3.0规范
          │- …core.Plugin  //增加web插件
          │- …ParamHandler //参数处理服务
          │- …RequestParse //请求解析服务
          └- …ViewResolver //视图解析服务

特性

  • 轻量级,注释完善,使用简单

  • 使用ServiceLoader加载插件,易于扩展

安装

添加依赖到你的 pom.xml:

<!-- 核心包 包含aop ioc 异常处理 缓存 定时任务等 -->
<dependency>
    <groupId>vip.justlive</groupId>
    <artifactId>oxygen-core</artifactId>
    <version>${oxygen.version}</version>
</dependency>

<!-- jdbc实现 可单独使用 -->
<dependency>
    <groupId>vip.justlive</groupId>
    <artifactId>oxygen-jdbc</artifactId>
    <version>${oxygen.version}</version>
</dependency>

<!-- web实现 已依赖了core -->
<dependency>
    <groupId>vip.justlive</groupId>
    <artifactId>oxygen-web</artifactId>
    <version>${oxygen.version}</version>
</dependency>

<!-- 已依赖了web 并提供了embeded tomcat -->
<dependency>
    <groupId>vip.justlive</groupId>
    <artifactId>oxygen-web-tomcat</artifactId>
    <version>${oxygen.version}</version>
</dependency>

快速开始

基础返回

使用 Resp 作为返回

// 成功返回 code 00000
Resp.success(Object obj);

// 错误返回 默认code 99999
Resp.error(String msg);

// 错误返回 自定义code
Resp.error(String code, String msg);

异常处理

使用 Exceptions 抛出异常

// 创建 ErrorCode
ErrorCode err = Exceptions.errorCode(String module, String code);
ErrorCode err = Exceptions.errorMessage(String module, String code, String message);

// 抛出unchecked异常
throw Exceptions.wrap(Throwable e);
throw Exceptions.wrap(Throwable e, String code, String message);
throw Exceptions.wrap(Throwable e, ErrorCode errorCode, Object... arguments);

// 抛出业务异常 不含堆栈信息
throw Exceptions.fail(ErrorCode errCode, Object... params);
throw Exceptions.fail(String code, String message, Object... params);

// 抛出故障异常 包含堆栈信息
throw Exceptions.fault(ErrorCode errCode, Object... params);
throw Exceptions.fault(String code, String message, Object... params);
throw Exceptions.fault(Throwable e, ErrorCode errCode, Object... params);
throw Exceptions.fault(Throwable e, String code, String message, Object... params)

IOC

通过注解使用IOC容器

// 在配置文件中添加扫包路径
main.class.scan=com.xxx.xxx,com.aaa.bbb

// 使用 @Configuration 和 @Bean
@Configuration
public class Conf {
 
  @Bean
  Inter noDepBean() {
    return new NoDepBean();
  }
}

// 使用 @Bean 和 @Inject
@Bean("depBean")
public class DepBean implements Inter {

  private final NoDepBean noDepBean;

  @Inject
  public DepBean(NoDepBean noDepBean) {
    this.noDepBean = noDepBean;
  }
  
  ...
}

// 运行时获取bean
Inter inter = BeanStore.getBean("depBean", Inter.class);

AOP

通过注解使用AOP

// 定义使用了Log注解的方法aop处理
@Before(annotation = Log.class)
public void log(Invocation invocation) {
  ...
}

// 目标方法添加注解
@Log
public void print() {
  ...
}

定时任务

使用注解 @Scheduled 标记一个方法需要作为定时任务

onApplicationStart(), cron(), fixedDelay(), or fixedRate() 必须配置其中一个

// 固定延迟任务 任务结束时间-下一个开始时间间隔固定
@Scheduled(fixedDelay = "500")
public void run1() {
  ...
}

// 固定周期任务 任务开始时间-下一个开始时间固定
@Scheduled(fixedRate = "600")
public void run2() {
  ...
}

// cron任务,并且程序启动后异步执行一次
@Scheduled(cron = "0/5 * * * * ?", onApplicationStart = true, async = true)
public void run3() {
  ...
}

缓存

使用缓存有两种方式:

  • JCache.cache() 获取缓存然后调用api

  • 使用 @Cacheable 注解给方法添加缓存

    // 使用缓存api 
    Cache cache = JCache.cache(cacheName);
    T value = cache.get(key, clazz);
    cache.set(key, value, duration, timeUnit);

    // 使用注解
    @Cacheable
    public Object method() {
      …
    }

    @Cacheable(key = “args[0]”, duration = 10, timeUnit = TimeUnit.MINUTES)
    public Object method(Object arg0, Object arg1) {
      …
    }

Jdbc

基础使用

  • 可配置多数据源

  • 使用Jdbc进行crud

  • 使用ResultSetHandler 进行自定义类型转换处理

  • 开启关闭事务

    // 单独使用 需要自己创建并添加数据源

    // 添加主数据源
    Jdbc.addPrimaryDataSource(DataSource dataSource)
    // 添加多数据源
    Jdbc.addDataSource(String name, DataSource dataSource)

    // crud
    T Jdbc.query(String sql, Class clazz, Object… params)
    List Jdbc.queryForList(String sql, Class clazz, Object… params)
    Map Jdbc.queryForMap(String sql, Object… params)
    List> Jdbc.queryForMapList(String sql, Object… params)
    // 可自定义返回处理
    T Jdbc.query(String sql, ResultSetHandler handler, Object… params)

    int Jdbc.update(String sql, Object… params)

    // 开启主数据源的事务
    Jdbc.startTx()
    // 开启指定数据源的事务
    Jdbc.startTx(String dataSourceName)

    // 关闭主数据源的事务
    Jdbc.closeTx()
    // 关闭指定数据源的事务
    Jdbc.closeTx(String dataSourceName)

    // 回滚事务
    Jdbc.rollbackTx()
    // 回滚指定数据源的事务
    Jdbc.rollbackTx(String dataSourceName)

    // 基础crud
    Option opt = new Option()

    Record.insert(opt)
    Record.findById(Option.class, 1)
    Record.find(opt)
    Record.update(opt)
    Record.deleteById(Option.class, 1)
    Record.delete(opt);

    // 配合oxygen-core使用, 只需在配置文件中配置数据源即可自动装载
    // 多数据源名称
    datasource.multi=a
    // 主数据源
    datasource.logSql=true
    datasource.driverClassName=org.h2.Driver
    datasource.url=jdbc:h2:mem:test;DB_CLOSE_DELAY=-1
    datasource.username=sa
    datasource.password=sa

    // 数据源a
    datasource.a.driverClassName=org.h2.Driver
    datasource.a.url=jdbc:h2:mem:a;DB_CLOSE_DELAY=-1
    datasource.a.username=sa
    datasource.a.password=sa

自定义列类型转换

  • 实现 ColumnHandler 接口进行自定义处理

  • 增加 META-INF/services/vip.justlive.oxygen.jdbc.handler.ColumnHandler 文件,添加实现类的类名

    public class MyColumnHandler implements ColumnHandler {

    @Override
      public boolean supported(Class<?> type) {
        …
      }

    @Override
      public Object fetch(ResultSet rs, int index) throws SQLException {
        …
      }
    }

    // 新增或修改 META-INF/services/vip.justlive.oxygen.jdbc.handler.ColumnHandler 添加自定义类名
    xxx.xxx.MyColumnHandler

增加jdbc拦截

  • 实现 JdbcInterceptor 接口

  • 调用 Jdbc.addJdbcInterceptor 添加拦截

    // 内置的sql打印拦截
    @Slf4j
    public class LogSqlJdbcInterceptor implements JdbcInterceptor {

    @Override
      public void before(String sql, List params) {
        if (log.isDebugEnabled()) {
          log.debug(“execute sql: {} -> params: {}”, sql, params);
        }
      }
    }

    // 添加拦截器
    Jdbc.addJdbcInterceptor(JdbcInterceptor interceptor)

    web

    基础使用

    • 使用@Router @Mapping @Param...等注解进行定义路由类和请求方法以及绑定参数

    • 使用View进行视图跳转,非void,View的返回值默认使用json处理

    • 线程内使用Request.current(),Response.current()获取请求和返回

      // 使用 @Router标记路由
      @Router(“/common”)
      public class CommonRouter {

      // 标记请求路径和请求方式,默认支持所有请求 当返回值不是void且非View则为返回json
        @Mapping(value = ”/localDate”,method = HttpMethod.GET)
        public Resp localDate() {
          return Resp.success(
                  LocalDate.now().plusDays(offset).atStartOfDay(ZoneOffset.systemDefault()).toInstant()
                      .toEpochMilli());
        }

      // 页面渲染 需要返回View
        @Mapping(“/index”)
        public View index() {
          View view = new View();
          view.setPath(“/index.jsp”);
          return view;
        }

        // 重定向
        @Mapping(“/view”)
        public View index() {
          View view = new View();
          // 相对路径为容器内跳转 使用http://xxx则绝对跳转
          view.setPath(“/index”);
          view.setRedirect(true);
          return view;
        }

        // 文件下载
        @Mapping(“download”)
        public void download(HttpServletResponse resp) throws IOException {
          resp.setCharacterEncoding(“utf-8”);
          resp.setContentType(“application/octet-stream;charset=utf-8”);
          resp.setHeader(“Content-disposition”, “attachment;filename=xx.txt”);
          Files.copy(new File(“xxx.txt”), resp.getOutputStream());
        }
      }

      // 获取当前线程的Request Response
      Request.current()
      Response.current()

      // 配置404错误页面跳转
      web.error.404.page=
      // 配置404自定义处理
      web.error.404.handler=
      // 配置500错误页面跳转
      web.error.500.page=
      // 配置500自定义处理
      web.error.500.handler=

    自定义视图解析

    • 实现ViewResolver接口

    • 通过ServiceLoaderWebPlugin.addViewResolver的方式进行添加自定义视图解析

      // 内置的重定向和json解析器
      public class DefaultViewResolver implements ViewResolver {

      @Override
        public boolean supported(Object data) {
          // 重定向
          if (data != null && data.getClass() == View.class && ((View) data).isRedirect()) {
            return true;
          }
          // null 或者 非view 返回json
          return data == null || data.getClass() != View.class;
        }

      @Override
        public void resolveView(HttpServletRequest request, HttpServletResponse response, Object data) {
          try {
            if (data != null && data.getClass() == View.class) {
              View view = (View) data;
              String redirectUrl = view.getPath();
              if (!redirectUrl.startsWith(Constants.HTTP_PREFIX) && !redirectUrl
                  .startsWith(Constants.HTTPS_PREFIX)) {
                redirectUrl = request.getContextPath() + view.getPath();
              }
              response.sendRedirect(redirectUrl);
            } else {
              response.getWriter().print(JSON.toJSONString(data));
            }
          } catch (IOException e) {
            throw Exceptions.wrap(e);
          }
        }
      }

    添加web启动执行类

    只需实现WebAppInitializer接口,容器会自动加载

    public class MyWebAppInitializer implements WebAppInitializer {
      @Override
      public void onStartup(ServletContext context) {
        ...
      }
      @Override
      public int order() {
        ...
      }
    }
    

    使用内置容器启动

    • 依赖 oxygen-web-tomcat

    • 在main中使用 Server.start启动



          vip.justlive
          oxygen-web-tomcat
          ${oxygen.version}

      public static void main(String[] args) {
        // 启动容器
        Server.start();
        …
        // 关闭容器
        Server.stop();
      }