惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

The Cloudflare Blog
Microsoft Security Blog
Microsoft Security Blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
L
LangChain Blog
W
WeLiveSecurity
P
Proofpoint News Feed
月光博客
月光博客
NISL@THU
NISL@THU
L
LINUX DO - 最新话题
Webroot Blog
Webroot Blog
T
Threatpost
Y
Y Combinator Blog
www.infosecurity-magazine.com
www.infosecurity-magazine.com
T
Threat Research - Cisco Blogs
Vercel News
Vercel News
Jina AI
Jina AI
阮一峰的网络日志
阮一峰的网络日志
S
Schneier on Security
J
Java Code Geeks
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
小众软件
小众软件
MyScale Blog
MyScale Blog
N
News and Events Feed by Topic
Stack Overflow Blog
Stack Overflow Blog
有赞技术团队
有赞技术团队
The Hacker News
The Hacker News
Schneier on Security
Schneier on Security
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Help Net Security
Help Net Security
Recent Announcements
Recent Announcements
S
Security @ Cisco Blogs
C
CXSECURITY Database RSS Feed - CXSecurity.com
S
Securelist
T
The Exploit Database - CXSecurity.com
云风的 BLOG
云风的 BLOG
C
Cisco Blogs
雷峰网
雷峰网
量子位
Google DeepMind News
Google DeepMind News
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Spread Privacy
Spread Privacy
L
Lohrmann on Cybersecurity
I
Intezer
T
The Blog of Author Tim Ferriss
G
GRAHAM CLULEY
D
DataBreaches.Net
V
Vulnerabilities – Threatpost
P
Privacy & Cybersecurity Law Blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
罗磊的独立博客

青空之蓝

[青空之蓝-2023] - 色彩 | 青空之蓝 [青空之蓝-2022] - 平静 | 青空之蓝 [青空之蓝-2021] - 远望 | 青空之蓝 浅谈垃圾回收 | 青空之蓝 浅谈泛型擦除 | 青空之蓝 浅谈单点登录 | 青空之蓝 使用 Kotlin 编写 Spring 测试 | 青空之蓝 设计模式系列文章 | 青空之蓝 从零实现一个 Java 微框架 - IoC | 青空之蓝 浅谈 JVM:类加载 | 青空之蓝 浅谈 IO | 青空之蓝 浅谈并发:synchronized & ReentrantLock | 青空之蓝 浅谈并发:CAS & AQS | 青空之蓝 浅谈并发:ThreadLocal | 青空之蓝 浅谈并发:三大特性 | 青空之蓝 浅谈组合注解 & 注解别名 | 青空之蓝 [青空之蓝-2020]-迷茫 | 青空之蓝 Java 系列文章 | 青空之蓝 HTTP 系列文章 | 青空之蓝 浅谈 EatWhatYouKill | 青空之蓝 浅谈可扩展线程池 | 青空之蓝 聊聊写框架 | 青空之蓝 聊聊现状-[2020-09] | 青空之蓝 浅谈并发:锁 | 青空之蓝 浅谈并发:基础 | 青空之蓝 浅谈缓存 | 青空之蓝 无须定义类,Spring 快速注入 Json 参数 | 青空之蓝 浅谈 Proxy 和 Aop | 青空之蓝 从零实现一个 PHP 微框架 - 初始化请求 | 青空之蓝 为 Vue3 添加一个简单的 Store | 青空之蓝 从零实现一个 PHP 微框架 - 服务提供者 | 青空之蓝 WSL2 踩坑记录 | 青空之蓝 浅谈浏览器Event Loop [更新] | 青空之蓝 从零实现一个 PHP 微框架 - Bootstrap 启动加载 | 青空之蓝 从零实现一个 PHP 微框架 - IoC 容器 | 青空之蓝 从零实现一个 PHP 微框架 - PSR & Composer | 青空之蓝 从零实现一个 PHP 微框架 - 前言 | 青空之蓝 MVVM 简单实现 | 青空之蓝 浅谈 DI 和 IoC | 青空之蓝 中间件实现 [PHP] | 青空之蓝 告别 Windows 终端的难看难用,打造好用的 PowerShell | 青空之蓝 VSCode Java输出中文乱码问题解决[更新] | 青空之蓝 浅谈浏览器渲染 | 青空之蓝 Vue-Cli@2 项目迁移日志 | 青空之蓝 Laragon & Scoop 集成踩坑记录 | 青空之蓝 「一行代码」优雅管理 Windows 软件 | 青空之蓝 [青空之蓝-2019]-年度总结 | 青空之蓝 为Vue添加简单的Store | 青空之蓝 为React添加简单的Store | 青空之蓝 为Vuex添加同步Action | 青空之蓝 浅谈B+树 | 青空之蓝 浅谈跳表 | 青空之蓝 浅谈数据库索引 | 青空之蓝 MySQL事务隔离 | 青空之蓝 算法复杂度分析(1) | 青空之蓝 一年来的经验总结 | 青空之蓝 Acrylic - VSCode Extension | 青空之蓝 ace编辑器设置惯性滚动 | 青空之蓝 Java二叉树实现 | 青空之蓝 Java图实现 | 青空之蓝 为apt方式安装的nginx重新编译增加WebDAV | 青空之蓝 XK-Editor - 一个支持富文本和Markdown的编辑器 | 青空之蓝 JS生成列表树 | 青空之蓝 Laravel生成目录树 | 青空之蓝 XK-Note - 集各种神奇功能的云笔记 | 青空之蓝 PHP GD生成验证码 | 青空之蓝 PHP GD图片处理[转换格式-水印-缩略图] | 青空之蓝 Origami - 简洁轻快的WordPress主题 | 青空之蓝 为WordPress启用WorkBox | 青空之蓝 [青空之蓝-2018]-年度总结 | 青空之蓝 VSCode Java手动导入jar和源码包 | 青空之蓝 Windows IP变化自动发送邮件 | 青空之蓝 C链表实现重制版 | 青空之蓝 C 结构体的定义和使用 | 青空之蓝 图的搜索(遍历) - BFS & DFS | 青空之蓝 Java链表实现 | 青空之蓝 C 快速排序 | 青空之蓝 C 插入排序 | 青空之蓝 C 归并排序 | 青空之蓝 C语言链表实现 | 青空之蓝 VSCode配置Java调试环境[Windows] | 青空之蓝 C 选择排序 | 青空之蓝 C 冒泡排序 | 青空之蓝 VSCode配置PHP调试环境[Windows] | 青空之蓝 VSCode配置C/C++ GDB调试环境[Windows] | 青空之蓝 WordPress友情链接模板 | 青空之蓝 Intel Optane 傲腾内存体验 | 青空之蓝 Mysql双机热备实战 | 青空之蓝 博客一年记录 | 青空之蓝 为WordPress启用Service Worker | 青空之蓝 Bing每日一图API | 青空之蓝 iframe延迟加载 | 青空之蓝 写在2018年高考前 | 青空之蓝 The Fox主题汉化分享 | 青空之蓝 [青空之蓝-2017]-崭新 | 青空之蓝 本博客评论规则 | 青空之蓝 世界,您好! | 青空之蓝
从零实现一个 Java 微框架 - 前言 | 青空之蓝
Otstar Lin · 2021-04-08 · via 青空之蓝

前言

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

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

目录

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

项目地址

系列文章讲解的代码都会通过注解标记到代码的第一行,由于文章编写的时候框架应该还会继续开发,所以请尽量按链接访问指定 commit 的代码,避免代码不同的情况。

整体结构

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

框架成果

@Controller
@RequestMapping("/test")
public class TestController {

    private static final Logger log = LoggerFactory.getLogger(
        TestController.class
    );

    @Autowired
    List<Advice> list;

    @Autowired
    Advice[] advice;

    @Autowired
    List<Advice>[] lists;

    @Autowired
    ObjectProvider<Advice> adviceObjectProvider;

    @Autowired
    ObjectProvider<String> notFoundObjectProvider;

    @Autowired(value = "name", required = false)
    private String name;

    @GetMapping("/{id}")
    public String test(final int id) {
        return "test";
    }

    @GetMapping("/get-a")
    public int getAnnotation(@QueryValue final int id) {
        return id;
    }

    @PostMapping("/post-a")
    public int postAnnotation(@BodyValue final int id) {
        return id;
    }

    @CrossOrigin
    @GetMapping("/header-a")
    public String headerAnnotation(@HeaderValue final String host) {
        return host;
    }

    @PostMapping("/post")
    @ResponseStatus
    // @VerifyCsrf
    public String post(
        @BodyValue final JsonNode user,
        @DataBind(name = "user") final User user2,
        @WebBind(
            name = "name",
            type = Type.PATH,
            converter = TestConverter.class
        ) final String name,
        @WebBind(name = "user3") final User2 user3,
        @Valid @DataBind(name = "user4") final User2 user4,
        // 如果不传入这两个其中一个参数,则会抛出异常
        final ValidGroup validGroup,
        final ValidResult<User2> validResult
    ) {
        return "post";
    }

    @PostMapping("/body")
    public String body(
        @DataBind(name = "&body") final JsonNode body,
        @DataBind final User2 user2,
        @DataBind(name = "request") final HttpServletRequest request
    ) {
        return "body";
    }

    @GetMapping("/case")
    public String getCase(final String userName) {
        return userName;
    }

    @GetMapping("/session-context")
    public String sessionContext(final SessionTest sessionTest) {
        if (sessionTest.getName() == null) {
            sessionTest.setName("Otstar Lin");
            return sessionTest.getName();
        } else {
            return sessionTest.getName() + "-Copy";
        }
    }

    @GetMapping("/result-empty")
    public String resultEmpty() {
        return "empty:";
    }

    @GetMapping("/result-html")
    public String resultHtml() {
        return "html:<h1>Html Result</h1>";
    }

    @GetMapping("/result-json")
    public String resultJson() {
        return "json:{\"type\": \"Json Result\"}";
    }

    @GetMapping("/result-redirect")
    public String resultRedirect() {
        return "redirect:/result-json";
    }

    @GetMapping("/result-text")
    public String resultText() {
        return "text:Text Result";
    }

    @GetMapping("/result-view")
    public String resultView(final Model model) {
        model.addAttribute("name", "View Result");
        return "view:index";
    }

    @GetMapping("/result-no-match")
    public String resultNoMatch() {
        return "No Match Result";
    }

    @GetMapping("/result-ignore")
    public String resultIgnore() {
        return ":empty:";
    }

    @GetMapping("/result-status")
    public String resultStatus(final Model model) {
        model.status(HttpStatus.BAD_REQUEST);
        return "text:Status Result";
    }

    @GetMapping("/default")
    public String defaultValue(
        @QueryValue(name = "name", defaultValue = "default") final String name
    ) {
        return name;
    }

    @GetMapping("/stream")
    public StreamResult stream() throws FileNotFoundException {
        return Result.stream(
            "video/mp4",
            IoUtil.toStream(
                ResourceUtils.getFile(
                    "file:/E:/Data/Videos/天气之子/天气之子.mp4"
                )
            )
        );
    }

    @GetMapping("/file")
    public File file() {
        return FileUtil.file("file:/E:/Data/Videos/天气之子/天气之子.mp4");
    }

    @GetMapping("/async")
    public Callable<String> async() {
        return () -> {
            log.info("Callable");
            return "result";
        };
    }

    @GetMapping("/async-task")
    public WebAsyncTask<String> asyncTask() {
        final WebAsyncTask<String> asyncTask = new WebAsyncTask<>(
            () -> {
                log.info("WebAsyncTask");
                return "result";
            }
        );
        asyncTask.onCompletion(
            () -> {
                log.info("WebAsyncTask completion");
            }
        );
        return asyncTask;
    }

    @GetMapping("/async-result")
    public AsyncResult<String> asyncResult() {
        return context -> {
            Result
                .file("file:/E:/Data/Videos/天气之子/天气之子.mp4")
                .toResponse(context.request(), context.response(), null);
            return null;
        };
    }

    @GetMapping("/deferred")
    public WebDeferredTask<String> deferred() {
        final WebDeferredTask<String> deferredTask = new WebDeferredTask<>();
        new Thread(
            () -> {
                try {
                    Thread.sleep(1000L);
                } catch (final InterruptedException e) {
                    log.error("Deferred sleep error", e);
                }
                log.info("Deferred set result");
                deferredTask.result("deferred");
            }
        )
            .start();
        return deferredTask;
    }

    @GetMapping("/async-timeout")
    public WebAsyncTask<String> asyncTimeout() {
        final WebAsyncTask<String> asyncTask = new WebAsyncTask<>(
            () -> {
                log.info("Async timeout sleep");
                try {
                    Thread.sleep(3000L);
                } catch (final Exception e) {
                    log.error("Async sleep error", e);
                }
                return "result";
            }
        );
        asyncTask.timeout(1000L);
        asyncTask.onTimeout(
            () -> {
                log.info("Timeout");
                return "timeout";
            }
        );
        return asyncTask;
    }

    @GetMapping("/async-pool")
    @WebAsync("testAsyncExecutor")
    public Callable<String> asyncPool() {
        return () -> {
            log.info("Callable");
            return "result";
        };
    }

    @GetMapping("/reactive-mono")
    public Mono<String> reactiveMono() {
        return Mono.just("mono");
    }

    @GetMapping("/reactive-flux")
    public Flux<String> reactiveFlux(final Response response) {
        response.contentType("text/event-stream");
        response.header("Cache-Control", "no-cache");
        return Flux
            .interval(Duration.ofSeconds(1))
            .map(
                seq ->
                    String.format("id:%d\nevent:random\ndata:%d\n\n", seq, seq)
            )
            .doOnNext(System.out::println);
    }

    @Bean
    public static ExecutorService testAsyncExecutor() {
        return new ThreadPoolExecutor(
            3,
            3,
            0L,
            TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(),
            ThreadUtil.newNamedThreadFactory("testAsyncExecutor-", true)
        );
    }

    @InitBinder
    public void binder(final WebDataBinder binder) {
        final User2 user2 = new User2();
        user2.setName("user3");
        user2.setAge(17);
        binder.addDefault("user3", user2);
        binder.addDefault("user4.name", "user4");
    }

    @PreDestroy
    public void destroy() {
        System.out.println("TestController destroy");
    }

    public static class TestConverter implements Converter {

        @Override
        public Object before(
            final Object object,
            final String name,
            final TypeWrapper<?> type,
            final MergedAnnotation annotation,
            final Container container
        ) {
            return "test-converter";
        }
    }
}
@Controller
@RequestMapping("/test")
public class TestController {

    private static final Logger log = LoggerFactory.getLogger(
        TestController.class
    );

    @Autowired
    List<Advice> list;

    @Autowired
    Advice[] advice;

    @Autowired
    List<Advice>[] lists;

    @Autowired
    ObjectProvider<Advice> adviceObjectProvider;

    @Autowired
    ObjectProvider<String> notFoundObjectProvider;

    @Autowired(value = "name", required = false)
    private String name;

    @GetMapping("/{id}")
    public String test(final int id) {
        return "test";
    }

    @GetMapping("/get-a")
    public int getAnnotation(@QueryValue final int id) {
        return id;
    }

    @PostMapping("/post-a")
    public int postAnnotation(@BodyValue final int id) {
        return id;
    }

    @CrossOrigin
    @GetMapping("/header-a")
    public String headerAnnotation(@HeaderValue final String host) {
        return host;
    }

    @PostMapping("/post")
    @ResponseStatus
    // @VerifyCsrf
    public String post(
        @BodyValue final JsonNode user,
        @DataBind(name = "user") final User user2,
        @WebBind(
            name = "name",
            type = Type.PATH,
            converter = TestConverter.class
        ) final String name,
        @WebBind(name = "user3") final User2 user3,
        @Valid @DataBind(name = "user4") final User2 user4,
        // 如果不传入这两个其中一个参数,则会抛出异常
        final ValidGroup validGroup,
        final ValidResult<User2> validResult
    ) {
        return "post";
    }

    @PostMapping("/body")
    public String body(
        @DataBind(name = "&body") final JsonNode body,
        @DataBind final User2 user2,
        @DataBind(name = "request") final HttpServletRequest request
    ) {
        return "body";
    }

    @GetMapping("/case")
    public String getCase(final String userName) {
        return userName;
    }

    @GetMapping("/session-context")
    public String sessionContext(final SessionTest sessionTest) {
        if (sessionTest.getName() == null) {
            sessionTest.setName("Otstar Lin");
            return sessionTest.getName();
        } else {
            return sessionTest.getName() + "-Copy";
        }
    }

    @GetMapping("/result-empty")
    public String resultEmpty() {
        return "empty:";
    }

    @GetMapping("/result-html")
    public String resultHtml() {
        return "html:<h1>Html Result</h1>";
    }

    @GetMapping("/result-json")
    public String resultJson() {
        return "json:{\"type\": \"Json Result\"}";
    }

    @GetMapping("/result-redirect")
    public String resultRedirect() {
        return "redirect:/result-json";
    }

    @GetMapping("/result-text")
    public String resultText() {
        return "text:Text Result";
    }

    @GetMapping("/result-view")
    public String resultView(final Model model) {
        model.addAttribute("name", "View Result");
        return "view:index";
    }

    @GetMapping("/result-no-match")
    public String resultNoMatch() {
        return "No Match Result";
    }

    @GetMapping("/result-ignore")
    public String resultIgnore() {
        return ":empty:";
    }

    @GetMapping("/result-status")
    public String resultStatus(final Model model) {
        model.status(HttpStatus.BAD_REQUEST);
        return "text:Status Result";
    }

    @GetMapping("/default")
    public String defaultValue(
        @QueryValue(name = "name", defaultValue = "default") final String name
    ) {
        return name;
    }

    @GetMapping("/stream")
    public StreamResult stream() throws FileNotFoundException {
        return Result.stream(
            "video/mp4",
            IoUtil.toStream(
                ResourceUtils.getFile(
                    "file:/E:/Data/Videos/天气之子/天气之子.mp4"
                )
            )
        );
    }

    @GetMapping("/file")
    public File file() {
        return FileUtil.file("file:/E:/Data/Videos/天气之子/天气之子.mp4");
    }

    @GetMapping("/async")
    public Callable<String> async() {
        return () -> {
            log.info("Callable");
            return "result";
        };
    }

    @GetMapping("/async-task")
    public WebAsyncTask<String> asyncTask() {
        final WebAsyncTask<String> asyncTask = new WebAsyncTask<>(
            () -> {
                log.info("WebAsyncTask");
                return "result";
            }
        );
        asyncTask.onCompletion(
            () -> {
                log.info("WebAsyncTask completion");
            }
        );
        return asyncTask;
    }

    @GetMapping("/async-result")
    public AsyncResult<String> asyncResult() {
        return context -> {
            Result
                .file("file:/E:/Data/Videos/天气之子/天气之子.mp4")
                .toResponse(context.request(), context.response(), null);
            return null;
        };
    }

    @GetMapping("/deferred")
    public WebDeferredTask<String> deferred() {
        final WebDeferredTask<String> deferredTask = new WebDeferredTask<>();
        new Thread(
            () -> {
                try {
                    Thread.sleep(1000L);
                } catch (final InterruptedException e) {
                    log.error("Deferred sleep error", e);
                }
                log.info("Deferred set result");
                deferredTask.result("deferred");
            }
        )
            .start();
        return deferredTask;
    }

    @GetMapping("/async-timeout")
    public WebAsyncTask<String> asyncTimeout() {
        final WebAsyncTask<String> asyncTask = new WebAsyncTask<>(
            () -> {
                log.info("Async timeout sleep");
                try {
                    Thread.sleep(3000L);
                } catch (final Exception e) {
                    log.error("Async sleep error", e);
                }
                return "result";
            }
        );
        asyncTask.timeout(1000L);
        asyncTask.onTimeout(
            () -> {
                log.info("Timeout");
                return "timeout";
            }
        );
        return asyncTask;
    }

    @GetMapping("/async-pool")
    @WebAsync("testAsyncExecutor")
    public Callable<String> asyncPool() {
        return () -> {
            log.info("Callable");
            return "result";
        };
    }

    @GetMapping("/reactive-mono")
    public Mono<String> reactiveMono() {
        return Mono.just("mono");
    }

    @GetMapping("/reactive-flux")
    public Flux<String> reactiveFlux(final Response response) {
        response.contentType("text/event-stream");
        response.header("Cache-Control", "no-cache");
        return Flux
            .interval(Duration.ofSeconds(1))
            .map(
                seq ->
                    String.format("id:%d\nevent:random\ndata:%d\n\n", seq, seq)
            )
            .doOnNext(System.out::println);
    }

    @Bean
    public static ExecutorService testAsyncExecutor() {
        return new ThreadPoolExecutor(
            3,
            3,
            0L,
            TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(),
            ThreadUtil.newNamedThreadFactory("testAsyncExecutor-", true)
        );
    }

    @InitBinder
    public void binder(final WebDataBinder binder) {
        final User2 user2 = new User2();
        user2.setName("user3");
        user2.setAge(17);
        binder.addDefault("user3", user2);
        binder.addDefault("user4.name", "user4");
    }

    @PreDestroy
    public void destroy() {
        System.out.println("TestController destroy");
    }

    public static class TestConverter implements Converter {

        @Override
        public Object before(
            final Object object,
            final String name,
            final TypeWrapper<?> type,
            final MergedAnnotation annotation,
            final Container container
        ) {
            return "test-converter";
        }
    }
}

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

结语

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

XK-Java 参考了以下框架:

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

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

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

    BY-NC-SA

  • 本文作者

    Otstar Lin

  • 发布于

    2021/04/08

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

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