JOxygen輕量級(jí) Java 框架
JOxygen
輕量級(jí)Java框架
介紹
一個(gè)輕量級(jí)Java框架
oxygen-core 核心部分
基于cglib的aop實(shí)現(xiàn)
提供緩存管理和基于注解的緩存,內(nèi)置LocalCache和Ehcache實(shí)現(xiàn),可擴(kuò)展
配置管理,支持${attrs.key:defaultValue}表達(dá)式獲取配置
加解密管理,提供加解密服務(wù)內(nèi)置基礎(chǔ)加密實(shí)現(xiàn),例如SHA-1、SHA-256、MD5
異常管理,提供異常包裝,統(tǒng)一異常編碼,便于國(guó)際化
提供基于構(gòu)造器注入的ioc(原因:依賴鏈清晰,并可任意切換ioc實(shí)現(xiàn))
定時(shí)任務(wù)服務(wù),內(nèi)置提供了基于注解的定時(shí)任務(wù)服務(wù)
├─ src/main │─ java/.../core //oxygen-core代碼目錄 │ │- aop //aop實(shí)現(xiàn)目錄 │ │- cache //緩存實(shí)現(xiàn)目錄 │ │- config //配置實(shí)現(xiàn)目錄 │ │- constant //常量目錄 │ │- convert //類型轉(zhuǎn)換實(shí)現(xiàn)目錄 │ │- crypto //密碼加密目錄 │ │- domain //基礎(chǔ)實(shí)體目錄 │ │- exception //異常管理目錄 │ │- io //io讀寫目錄 │ │- ioc //ioc實(shí)現(xiàn)目錄 │ │- job //定時(shí)任務(wù)實(shí)現(xiàn)目錄 │ │- scan //類掃描實(shí)現(xiàn)目錄 │ │- util //工具類目錄 │ │- Bootstrap.java //框架啟動(dòng)引導(dǎo)類 │ └- Plugin.java //插件接口 └─ resources/META-INF/services └- ...core.Plugin //Plugin服務(wù)實(shí)現(xiàn)配置文件
oxygen-jdbc jdbc實(shí)現(xiàn)
小巧簡(jiǎn)單的jdbc實(shí)現(xiàn),純jdk實(shí)現(xiàn),無第三方j(luò)ar
支持多數(shù)據(jù)源
基于sql進(jìn)行crud,不提供類似Hibernate的鏈?zhǔn)椒椒ǎㄔ颍簊ql作為數(shù)據(jù)庫領(lǐng)域的DSL,已經(jīng)很自然優(yōu)雅,Less is more)
├─ src/main │─ java/.../jdbc //oxygen-jdbc代碼目錄 │ │- config //配置數(shù)據(jù)源目錄 │ │- handler //處理器目錄,包括結(jié)果集處理 行處理 列處理 │ │- interceptor //攔截器目錄,攔截sql執(zhí)行前后及異常 │ │- record //基礎(chǔ)crud │ │- Jdbc.java //Jdbc核心操作類,提供crud操作 │ │- JdbcException.java //jdbc異常封裝 │ └- JdbcPlugin.java //jdbc插件,與oxygen-core配套使用 └─ resources/META-INF/services │- ...handler.ColumnHandler //列處理服務(wù)配置文件 └- ...core.Plugin //增加jdbcPlugin服務(wù)實(shí)現(xiàn),與oxygen-core配套使用
oxygen-web
項(xiàng)目使用了Servlet3.0的
ServletContainerInitializer接口在servlet中可自動(dòng)加載,不需要在web.xml中配置
使用
@Router標(biāo)記路由文件使用
@Mapping標(biāo)記請(qǐng)求路徑處理方法參數(shù)綁定的取值域使用
@Param,@HeaderParam,@CookieParam,@PathParam指定,默認(rèn)為@Param參數(shù)綁定支持簡(jiǎn)單類型 + Map<String,Object> + 用戶實(shí)體類
支持返回Json、視圖或自定義實(shí)現(xiàn)(文件下載等)
├─ src/main │─ java/.../web //oxygen-web代碼目錄 │ │- handler //參數(shù)綁定處理 │ │- http //http請(qǐng)求解析 │ │- mapping //url映射,參數(shù)映射相關(guān)注解和實(shí)體 │ │- router //一個(gè)示例路由(獲取服務(wù)器時(shí)間) │ │- server //內(nèi)置server接口和啟動(dòng)類 │ │- view //視圖解析 │ │- DefaultWebAppInitializer.java //默認(rèn)初始化實(shí)現(xiàn) │ │- DispatcherServlet.java //路由分發(fā)器 │ │- WebAppInitializer.java //web自動(dòng)初始化接口,提供給用戶自定義使用 │ │- WebContainerInitializer.java //容器自動(dòng)初始化 │ │- WebConf.java //web配置 │ └- WebPlugin.java //web插件 └─ resources/META-INF/services │- ...ServletContainerIntializer //servlet3.0規(guī)范 │- ...core.Plugin //增加web插件 │- ...ParamHandler //參數(shù)處理服務(wù) │- ...RequestParse //請(qǐng)求解析服務(wù) └- ...ViewResolver //視圖解析服務(wù)
特性
輕量級(jí),注釋完善,使用簡(jiǎn)單
使用ServiceLoader加載插件,易于擴(kuò)展
安裝
添加依賴到你的 pom.xml:
<!-- 核心包 包含aop ioc 異常處理 緩存 定時(shí)任務(wù)等 -->
<dependency>
<groupId>vip.justlive</groupId>
<artifactId>oxygen-core</artifactId>
<version>${oxygen.version}</version>
</dependency>
<!-- jdbc實(shí)現(xiàn) 可單獨(dú)使用 -->
<dependency>
<groupId>vip.justlive</groupId>
<artifactId>oxygen-jdbc</artifactId>
<version>${oxygen.version}</version>
</dependency>
<!-- web實(shí)現(xiàn) 已依賴了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>
快速開始
基礎(chǔ)返回
使用 Resp 作為返回
// 成功返回 code 00000 Resp.success(Object obj); // 錯(cuò)誤返回 默認(rèn)code 99999 Resp.error(String msg); // 錯(cuò)誤返回 自定義code Resp.error(String code, String msg);
異常處理
使用 Exceptions 拋出異常
// 創(chuàng)建 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); // 拋出業(yè)務(wù)異常 不含堆棧信息 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;
}
...
}
// 運(yùn)行時(shí)獲取bean
Inter inter = BeanStore.getBean("depBean", Inter.class);
AOP
通過注解使用AOP
// 定義使用了Log注解的方法aop處理
@Before(annotation = Log.class)
public void log(Invocation invocation) {
...
}
// 目標(biāo)方法添加注解
@Log
public void print() {
...
}
定時(shí)任務(wù)
使用注解 @Scheduled 標(biāo)記一個(gè)方法需要作為定時(shí)任務(wù)
onApplicationStart(), cron(), fixedDelay(), or fixedRate() 必須配置其中一個(gè)
// 固定延遲任務(wù) 任務(wù)結(jié)束時(shí)間-下一個(gè)開始時(shí)間間隔固定
@Scheduled(fixedDelay = "500")
public void run1() {
...
}
// 固定周期任務(wù) 任務(wù)開始時(shí)間-下一個(gè)開始時(shí)間固定
@Scheduled(fixedRate = "600")
public void run2() {
...
}
// cron任務(wù),并且程序啟動(dòng)后異步執(zhí)行一次
@Scheduled(cron = "0/5 * * * * ?", onApplicationStart = true, async = true)
public void run3() {
...
}
緩存
使用緩存有兩種方式:
JCache.cache()獲取緩存然后調(diào)用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
基礎(chǔ)使用
可配置多數(shù)據(jù)源
使用Jdbc進(jìn)行crud
使用ResultSetHandler 進(jìn)行自定義類型轉(zhuǎn)換處理
開啟關(guān)閉事務(wù)
// 單獨(dú)使用 需要自己創(chuàng)建并添加數(shù)據(jù)源 ... // 添加主數(shù)據(jù)源 Jdbc.addPrimaryDataSource(DataSource dataSource) // 添加多數(shù)據(jù)源 Jdbc.addDataSource(String name, DataSource dataSource) // crud T Jdbc.query(String sql, Class<T> clazz, Object... params) List<T> Jdbc.queryForList(String sql, Class<T> clazz, Object... params) Map<String, Object> Jdbc.queryForMap(String sql, Object... params) List<Map<String, Object>> Jdbc.queryForMapList(String sql, Object... params) // 可自定義返回處理 T Jdbc.query(String sql, ResultSetHandler<T> handler, Object... params) int Jdbc.update(String sql, Object... params) // 開啟主數(shù)據(jù)源的事務(wù) Jdbc.startTx() // 開啟指定數(shù)據(jù)源的事務(wù) Jdbc.startTx(String dataSourceName) // 關(guān)閉主數(shù)據(jù)源的事務(wù) Jdbc.closeTx() // 關(guān)閉指定數(shù)據(jù)源的事務(wù) Jdbc.closeTx(String dataSourceName) // 回滾事務(wù) Jdbc.rollbackTx() // 回滾指定數(shù)據(jù)源的事務(wù) Jdbc.rollbackTx(String dataSourceName) // 基礎(chǔ)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使用, 只需在配置文件中配置數(shù)據(jù)源即可自動(dòng)裝載 // 多數(shù)據(jù)源名稱 datasource.multi=a // 主數(shù)據(jù)源 datasource.logSql=true datasource.driverClassName=org.h2.Driver datasource.url=jdbc:h2:mem:test;DB_CLOSE_DELAY=-1 datasource.username=sa datasource.password=sa // 數(shù)據(jù)源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
自定義列類型轉(zhuǎn)換
實(shí)現(xiàn)
ColumnHandler接口進(jìn)行自定義處理增加
META-INF/services/vip.justlive.oxygen.jdbc.handler.ColumnHandler文件,添加實(shí)現(xiàn)類的類名
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攔截
實(shí)現(xiàn)
JdbcInterceptor接口調(diào)用
Jdbc.addJdbcInterceptor添加攔截
// 內(nèi)置的sql打印攔截
@Slf4j
public class LogSqlJdbcInterceptor implements JdbcInterceptor {
@Override
public void before(String sql, List<Object> params) {
if (log.isDebugEnabled()) {
log.debug("execute sql: {} -> params: {}", sql, params);
}
}
}
// 添加攔截器
Jdbc.addJdbcInterceptor(JdbcInterceptor interceptor)
web
基礎(chǔ)使用
使用
@Router @Mapping @Param...等注解進(jìn)行定義路由類和請(qǐng)求方法以及綁定參數(shù)使用
View進(jìn)行視圖跳轉(zhuǎn),非void,View的返回值默認(rèn)使用json處理線程內(nèi)使用
Request.current(),Response.current()獲取請(qǐng)求和返回
// 使用 @Router標(biāo)記路由
@Router("/common")
public class CommonRouter {
// 標(biāo)記請(qǐng)求路徑和請(qǐng)求方式,默認(rèn)支持所有請(qǐng)求 當(dāng)返回值不是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();
// 相對(duì)路徑為容器內(nèi)跳轉(zhuǎn) 使用http://xxx則絕對(duì)跳轉(zhuǎn)
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());
}
}
// 獲取當(dāng)前線程的Request Response
Request.current()
Response.current()
// 配置404錯(cuò)誤頁面跳轉(zhuǎn)
web.error.404.page=
// 配置404自定義處理
web.error.404.handler=
// 配置500錯(cuò)誤頁面跳轉(zhuǎn)
web.error.500.page=
// 配置500自定義處理
web.error.500.handler=
自定義視圖解析
實(shí)現(xiàn)
ViewResolver接口通過
ServiceLoader或WebPlugin.addViewResolver的方式進(jìn)行添加自定義視圖解析
// 內(nèi)置的重定向和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啟動(dòng)執(zhí)行類
只需實(shí)現(xiàn)WebAppInitializer接口,容器會(huì)自動(dòng)加載
public class MyWebAppInitializer implements WebAppInitializer {
@Override
public void onStartup(ServletContext context) {
...
}
@Override
public int order() {
...
}
}
使用內(nèi)置容器啟動(dòng)
依賴
oxygen-web-tomcat在main中使用
Server.start啟動(dòng)
<!-- 已依賴了web 并提供了embeded tomcat -->
<dependency>
<groupId>vip.justlive</groupId>
<artifactId>oxygen-web-tomcat</artifactId>
<version>${oxygen.version}</version>
</dependency>
public static void main(String[] args) {
// 啟動(dòng)容器
Server.start();
...
// 關(guān)閉容器
Server.stop();
}