站点图标

从零实现一个 Java 微框架 - 前言

2021-04-08折腾记录Java / XK-Java / 框架
本文最后更新于 607 天前,文中所描述的信息可能已发生改变

前言

自制的 JavaWeb 框架 XK-Java 至今也开发了快一年的时间了,如果算上第一个 commit 之前的准备时间差不多就一年了,最近应该也不会再有大的结构更新了,所以就打算和之前 PHP 框架系列一样,写一个系列文章。也算是对这些技术的复盘吧,顺便如果可以的话也可以连 Spring 一起分析下。

观前提示:XK-Java 并不是 Spring 的缩小版,大部分模块都是由我自行实现的,所以原理也大不相同(当然有一些是 copy 自 Spring 的就另外说,逃),如果你是想通过本框架来学习 Spring 的话基本可以绕道了。

目录

  1. 从零实现一个 Java 微框架 - 前言
  2. 从零实现一个 Java 微框架 - IoC

项目地址

整体结构

63a92cf8 d4b7 4875 88ca 6a4bf491dd2c

这张图只展示了框架中包含的架构和各模块之间的关系,模块中的东西会在后续的文章中说明。

框架成果


_304
@Controller
_304
@RequestMapping("/test")
_304
public class TestController {
_304
_304
private static final Logger log = LoggerFactory.getLogger(
_304
TestController.class
_304
);
_304
_304
@Autowired
_304
List<Advice> list;
_304
_304
@Autowired
_304
Advice[] advice;
_304
_304
@Autowired
_304
List<Advice>[] lists;
_304
_304
@Autowired
_304
ObjectProvider<Advice> adviceObjectProvider;
_304
_304
@Autowired
_304
ObjectProvider<String> notFoundObjectProvider;
_304
_304
@Autowired(value = "name", required = false)
_304
private String name;
_304
_304
@GetMapping("/{id}")
_304
public String test(final int id) {
_304
return "test";
_304
}
_304
_304
@GetMapping("/get-a")
_304
public int getAnnotation(@QueryValue final int id) {
_304
return id;
_304
}
_304
_304
@PostMapping("/post-a")
_304
public int postAnnotation(@BodyValue final int id) {
_304
return id;
_304
}
_304
_304
@CrossOrigin
_304
@GetMapping("/header-a")
_304
public String headerAnnotation(@HeaderValue final String host) {
_304
return host;
_304
}
_304
_304
@PostMapping("/post")
_304
@ResponseStatus
_304
// @VerifyCsrf
_304
public String post(
_304
@BodyValue final JsonNode user,
_304
@DataBind(name = "user") final User user2,
_304
@WebBind(
_304
name = "name",
_304
type = Type.PATH,
_304
converter = TestConverter.class
_304
) final String name,
_304
@WebBind(name = "user3") final User2 user3,
_304
@Valid @DataBind(name = "user4") final User2 user4,
_304
// 如果不传入这两个其中一个参数,则会抛出异常
_304
final ValidGroup validGroup,
_304
final ValidResult<User2> validResult
_304
) {
_304
return "post";
_304
}
_304
_304
@PostMapping("/body")
_304
public String body(
_304
@DataBind(name = "&body") final JsonNode body,
_304
@DataBind final User2 user2,
_304
@DataBind(name = "request") final HttpServletRequest request
_304
) {
_304
return "body";
_304
}
_304
_304
@GetMapping("/case")
_304
public String getCase(final String userName) {
_304
return userName;
_304
}
_304
_304
@GetMapping("/session-context")
_304
public String sessionContext(final SessionTest sessionTest) {
_304
if (sessionTest.getName() == null) {
_304
sessionTest.setName("Otstar Lin");
_304
return sessionTest.getName();
_304
} else {
_304
return sessionTest.getName() + "-Copy";
_304
}
_304
}
_304
_304
@GetMapping("/result-empty")
_304
public String resultEmpty() {
_304
return "empty:";
_304
}
_304
_304
@GetMapping("/result-html")
_304
public String resultHtml() {
_304
return "html:<h1>Html Result</h1>";
_304
}
_304
_304
@GetMapping("/result-json")
_304
public String resultJson() {
_304
return "json:{\"type\": \"Json Result\"}";
_304
}
_304
_304
@GetMapping("/result-redirect")
_304
public String resultRedirect() {
_304
return "redirect:/result-json";
_304
}
_304
_304
@GetMapping("/result-text")
_304
public String resultText() {
_304
return "text:Text Result";
_304
}
_304
_304
@GetMapping("/result-view")
_304
public String resultView(final Model model) {
_304
model.addAttribute("name", "View Result");
_304
return "view:index";
_304
}
_304
_304
@GetMapping("/result-no-match")
_304
public String resultNoMatch() {
_304
return "No Match Result";
_304
}
_304
_304
@GetMapping("/result-ignore")
_304
public String resultIgnore() {
_304
return ":empty:";
_304
}
_304
_304
@GetMapping("/result-status")
_304
public String resultStatus(final Model model) {
_304
model.status(HttpStatus.BAD_REQUEST);
_304
return "text:Status Result";
_304
}
_304
_304
@GetMapping("/default")
_304
public String defaultValue(
_304
@QueryValue(name = "name", defaultValue = "default") final String name
_304
) {
_304
return name;
_304
}
_304
_304
@GetMapping("/stream")
_304
public StreamResult stream() throws FileNotFoundException {
_304
return Result.stream(
_304
"video/mp4",
_304
IoUtil.toStream(
_304
ResourceUtils.getFile(
_304
"file:/E:/Data/Videos/天气之子/天气之子.mp4"
_304
)
_304
)
_304
);
_304
}
_304
_304
@GetMapping("/file")
_304
public File file() {
_304
return FileUtil.file("file:/E:/Data/Videos/天气之子/天气之子.mp4");
_304
}
_304
_304
@GetMapping("/async")
_304
public Callable<String> async() {
_304
return () -> {
_304
log.info("Callable");
_304
return "result";
_304
};
_304
}
_304
_304
@GetMapping("/async-task")
_304
public WebAsyncTask<String> asyncTask() {
_304
final WebAsyncTask<String> asyncTask = new WebAsyncTask<>(
_304
() -> {
_304
log.info("WebAsyncTask");
_304
return "result";
_304
}
_304
);
_304
asyncTask.onCompletion(
_304
() -> {
_304
log.info("WebAsyncTask completion");
_304
}
_304
);
_304
return asyncTask;
_304
}
_304
_304
@GetMapping("/async-result")
_304
public AsyncResult<String> asyncResult() {
_304
return context -> {
_304
Result
_304
.file("file:/E:/Data/Videos/天气之子/天气之子.mp4")
_304
.toResponse(context.request(), context.response(), null);
_304
return null;
_304
};
_304
}
_304
_304
@GetMapping("/deferred")
_304
public WebDeferredTask<String> deferred() {
_304
final WebDeferredTask<String> deferredTask = new WebDeferredTask<>();
_304
new Thread(
_304
() -> {
_304
try {
_304
Thread.sleep(1000L);
_304
} catch (final InterruptedException e) {
_304
log.error("Deferred sleep error", e);
_304
}
_304
log.info("Deferred set result");
_304
deferredTask.result("deferred");
_304
}
_304
)
_304
.start();
_304
return deferredTask;
_304
}
_304
_304
@GetMapping("/async-timeout")
_304
public WebAsyncTask<String> asyncTimeout() {
_304
final WebAsyncTask<String> asyncTask = new WebAsyncTask<>(
_304
() -> {
_304
log.info("Async timeout sleep");
_304
try {
_304
Thread.sleep(3000L);
_304
} catch (final Exception e) {
_304
log.error("Async sleep error", e);
_304
}
_304
return "result";
_304
}
_304
);
_304
asyncTask.timeout(1000L);
_304
asyncTask.onTimeout(
_304
() -> {
_304
log.info("Timeout");
_304
return "timeout";
_304
}
_304
);
_304
return asyncTask;
_304
}
_304
_304
@GetMapping("/async-pool")
_304
@WebAsync("testAsyncExecutor")
_304
public Callable<String> asyncPool() {
_304
return () -> {
_304
log.info("Callable");
_304
return "result";
_304
};
_304
}
_304
_304
@GetMapping("/reactive-mono")
_304
public Mono<String> reactiveMono() {
_304
return Mono.just("mono");
_304
}
_304
_304
@GetMapping("/reactive-flux")
_304
public Flux<String> reactiveFlux(final Response response) {
_304
response.contentType("text/event-stream");
_304
response.header("Cache-Control", "no-cache");
_304
return Flux
_304
.interval(Duration.ofSeconds(1))
_304
.map(
_304
seq ->
_304
String.format("id:%d\nevent:random\ndata:%d\n\n", seq, seq)
_304
)
_304
.doOnNext(System.out::println);
_304
}
_304
_304
@Bean
_304
public static ExecutorService testAsyncExecutor() {
_304
return new ThreadPoolExecutor(
_304
3,
_304
3,
_304
0L,
_304
TimeUnit.SECONDS,
_304
new LinkedBlockingQueue<>(),
_304
ThreadUtil.newNamedThreadFactory("testAsyncExecutor-", true)
_304
);
_304
}
_304
_304
@InitBinder
_304
public void binder(final WebDataBinder binder) {
_304
final User2 user2 = new User2();
_304
user2.setName("user3");
_304
user2.setAge(17);
_304
binder.addDefault("user3", user2);
_304
binder.addDefault("user4.name", "user4");
_304
}
_304
_304
@PreDestroy
_304
public void destroy() {
_304
System.out.println("TestController destroy");
_304
}
_304
_304
public static class TestConverter implements Converter {
_304
_304
@Override
_304
public Object before(
_304
final Object object,
_304
final String name,
_304
final TypeWrapper<?> type,
_304
final MergedAnnotation annotation,
_304
final Container container
_304
) {
_304
return "test-converter";
_304
}
_304
}
_304
}

更多的样例可以到 xkjava-app 模块里查看。

结语

本篇文章只是一个开篇,所以是一篇水文 2333。

XK-Java 参考了以下框架:

感谢这些框架为我提供了实现和学习的思路。

从零实现一个 Java 微框架 - 前言

https://blog.ixk.me/post/implement-a-java-microframework-from-zero-1
  • 许可协议

    BY-NC-SA

  • 发布于

    2021-04-08

  • 本文作者

    Otstar Lin

转载或引用本文时请遵守许可协议,注明出处、不得用于商业用途!

从零实现一个 Java 微框架 - IoC浅谈 JVM:类加载