一. 前言
Solon 是 国产的 Java 企业级应用开发框架 ,算是国内在体系和生态上都比较全面的框架了。
这个框架在 内存占用 \ 启动速度上都很亮眼, 据官方数据 ,整体性能对比 Spring 提高了多倍。
这一篇就来感受一下这个框架 ,并且从源码的角度上 ,来感受一下这个框架的种种,对比一下 Spring ,它又优化了什么。
二. 基础使用
Maven 依赖
            
            
              xml
              
              
            
          
          <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>org.noear</groupId>
    <artifactId>solon-parent</artifactId>
    <version>3.2.0</version>
  </parent>
  <groupId>org.example</groupId>
  <artifactId>solonSimpleDemo</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>jar</packaging>
  <name>solonSimpleDemo</name>
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>
  <dependencies>
    <dependency>
      <groupId>org.noear</groupId>
      <artifactId>solon-web</artifactId>
    </dependency>
  </dependencies>
  <build>
    <finalName>${project.artifactId}</finalName>
    <plugins>
      <plugin>
        <!-- 引入打包插件 -->
        <groupId>org.noear</groupId>
        <artifactId>solon-maven-plugin</artifactId>
      </plugin>
    </plugins>
  </build>
</project>
        启动类
            
            
              java
              
              
            
          
          import org.noear.solon.Solon;
public class App {
    public static void main(String[] args) {
        Solon.start(App.class, args);
    }
}
        Rest 接口
            
            
              java
              
              
            
          
          @Controller
public class HelloController {
    /**
     * 这是直接返回值
     * */
    @Mapping("/")
    public String hello() {
        return "Hello world!";
    }
    /**
     * 这是返回个对象(以json形式)
     * */
    @Mapping("/json")
    public Map hello_json() {
        Map<String,Object> map = new HashMap<>(); //实体也ok
        map.put("message", "Hello world!");
        return map;
    }
}
        阶段总结 :
- 从最简单的使用层面 ,可以看到和Spring区别不大 ,或者说是沿用统一的规范
 - 启动时间确实更快了 ,调用接口等功能也没有太大的问题
 
三. 深入原理
3.1 Rest 接口的使用
我们知道 ,Spring 的 MVC 功能是基于 Servlet 的 ,其根本的特性是通过 DispatchServlet 接收来自于外部的各种请求。
而 Solon 里面要简单很多 ,其默认核心的依赖是 solon-boot-smarthttp ,而从官方的依赖列表里面 ,Solon 的 HTTP 层面还支持 : jdkhttp , jetty 和 undertow.
我们来简单学习一下这四个框架 :
| 框架 | 实现原理 | 主要特性 | 性能指标(线程模型 & 吞吐) | 适用场景 | 
|---|---|---|---|---|
| SmartHttp (SmartBoot) | 基于 Java NIO 异步非阻塞,事件驱动,利用 Epoll/kqueue | - 纯异步 NIO 架构 - 轻量高性能 - 灵活的协议扩展 - 支持全链路异步处理 | 高效纯异步线程模型 支持大量并发连接 低延迟,高吞吐(百万级 TPS,视环境不同) | 高并发微服务、游戏、实时通信 | 
| JDK HttpServer | Java 自带,基于线程池的阻塞 IO 实现,简单 HTTP 服务器 | - 内置 JDK,无需额外依赖 - 简单易用 - 仅支持基础 HTTP 功能 | 线程池阻塞模型 低并发适用 吞吐和延迟一般,不适合高并发 | 简单应用、快速开发、测试调试 | 
| Jetty | 基于 Java NIO 异步非阻塞,Servlet 容器,多线程队列 | - 完整 Servlet 规范支持 - 轻量灵活 - 支持 HTTP/2, WebSocket - 丰富扩展和生态整合 | 异步线程池+请求队列 线程复用良好 性能优异,百万级 TPS(硬件及配置相关) | 传统中大型 Web 应用,Servlet 生态 | 
| Undertow | 基于 Java NIO,借助 XNIO 框架实现轻量异步非阻塞 | - 极简轻量 - 支持 Servlet 3.1 异步 API - HTTP/2 支持 - 内置 WebSocket | 高效事件驱动异步 线程数少,减少上下文切换 吞吐量高,性能与 Jetty 相似或略优 | 微服务、轻量 Web 服务及嵌入式服务器 | 
solon HTTP 流程的核心流程处理 :

这是 Spring MVC 的核心流程 :

阶段总结 :
Spring 默认带的 Tomcat 有问题吗 ? 一点问题没有 , Tomcat 的性能是够的 , 这一点已经有大量案例了 。
但是从上面流程可以对比出来 ,Spring 的 Web 处理太完备了 ,以至于一个简单的 HTTP 请求链过长 ,其中可能多做了50% 的无意义操作.
Solon 的处理很原生 ,主要在最底层的框架上面做了一些必要的封装。简单请求里面 ,从SmartHttp 透传请求 ,到业务方接收到请求 ,整体的处理栈差不多在10层左右。
3.2 容器的启动 ( = ApplicationContext)
Solon 的启动是从 Solon.class 的 start 方法开始。重点是在 SolonApp 中 ,其中主要可以分为3步 :
- S1 : 创建一个 SolonApp 对象用于加载容器等信息
 - S2 : SolonApp.init(initialize) 进行内部的初始化 ,主要是配置
- 配置就像Spring里面自动装配的内容 ,也就是类似于 Spring-mybatis-starter 这样,用来导入插件
 - 比如 Solon 里面就是 mybatis-sqlhelper-solon-plugin
 
 - S3 : SolonApp.run 加载插件和 Bean
 
Plugin 的扫描处理 :
            
            
              java
              
              
            
          
          protected void pluginScan(List<ClassLoader> classLoaders) {
    for (ClassLoader classLoader : classLoaders) {
        //扫描配置
        PluginUtil.scanPlugins(classLoader, pluginExcludeds, plugins::add);
    }
    //扫描主配置
    PluginUtil.findPlugins(AppClassLoader.global(), this, pluginExcludeds, plugins::add);
    //插件排序
    Collections.sort(plugins);
}
        Run 方法中对 Bean 的扫描起点
            
            
              java
              
              
            
          
          //2.1.通过注解导入bean(一般是些配置器)
beanImportTry();
//2.2.通过源扫描bean
if (source() != null && enableScanning()) {
    context().beanScan(source());
}
        3.2 Bean 的管理 (= IOC)
Bean 真正扫描的起点在于 :AppContext # beanScan 部分 :

阶段总结 :
3.3 AOP 部分
这部分有点意思 ,Solon 对 AOP 进行了很直接的简化 ,简化为了两个注解 ,使用上反而会复杂点 :
- @Addition : 局部对 Context 进行过滤 , 基于 Filter 进行代理
- 更多是 "前置" 或 "后置" 逻辑
 - 无法通过它来控制目标方法的执行和修改返回值
 - 用于快速加挂"额外操作(Addition) "
 
 - @Around : 局部对 Bean 进行拦截 ,基于 MethodInterceptor
- 以在执行前后插入逻辑,还可以决定是否执行目标方法、修改返回值或参数
 
 
所以 ,其实 @Around 更像之前 Spring 的 AOP 流程。由于 Solon 相当于省略了整个 AOP 的自动化处理 ,更接近于对象代理 ,所以配置起来会更复杂点 :
            
            
              java
              
              
            
          
          // S1 : 准备一个注解 
@Target({ElementType.METHOD, ElementType.TYPE}) //支持加在类或方法上
@Retention(RetentionPolicy.RUNTIME)
public @interface AopDemo {
}
// S2 : 准备拦截器
@Slf4j
public class LogInterceptor  implements Interceptor {
    @Override
    public Object doIntercept(Invocation inv) throws Throwable {
        System.out.println("拦截器执行了");
        return inv.invoke();
    }
}
// S3 : 启动类里面注入
public static void main(String[] args) {
    Solon.start(App.class, args, app->{
        app.context().beanInterceptorAdd(AopDemo.class, new LogInterceptor());
    });
}
        源码层面 :
核心也是分为两个部分 :
- S1 : 创建和注入Bean的时候 ,提供的是一个 BeanInvocationHandler 代理对象
 - S2 : 外部调用 AOP 方法时候 ,首先调用的是代理对象 ,再由代理对象调用到方法里面
 
            
            
              java
              
              
            
          
          // S1 : 创建代理对象 
// C- AppContext
if (tryProxy) {
    //是否需要自动代理
    enableProxy = enableProxy || beanInterceptorHas(bw.clz());
    if (enableProxy) {
        ProxyBinder.global().binding(bw);
    }
}
// S2 : 调用代理的方法
C- BeanInvocationHandler
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  
    // 判断是否存在自定义处理器,如果没有则调用默认的执行流程  
    if (this.handler == null) {  
         // 允许访问私有方法  
         method.setAccessible(true);
         // 通过上下文获取对应类的对应方法的 MethodWrap(或类似封装)  
         // bw.context() 表示当前的上下文环境  
         // bw.rawClz() 获取原始类(目标类)  
         // methodGet 方法根据类和方法反射信息获取相应的包装MethodWrap对象  
         // invokeByAspect 表示通过AOP切面逻辑来执行这个方法,传入 this.bean 作为目标实例,args 作为参数 
         Object result = this.bw.context()  
                              .methodGet(this.bw.rawClz(), method)  
                              .invokeByAspect(this.bean, args);  
         return result;  
    }
}
        Spring AOP 做了什么 ?

- 这是之前 Spring AOP 的主流程梳理 ,流程相对而言长了太多
 
总结
时间精力有限 ,整体代码没有太深入的看了。整体使用下来 ,如果项目比较小 ,资源也紧张 ,Solon 是一个很值得考虑的框架。
- 使用上 : 不适合改造旧项目 ,但是新项目使用没什么压力 ,文档丰富,使用不需要太大的难度
 - 深度问题 : 由于对整个流程做了极大的简化 ,所以出问题基本上可以通过读源码解决
 - 性能和内存占用现在看到的会有明显的提升 ,但是改造大的生产项目太难了 ,没办法进行比较
 
个人的一点思路 :
Spring 是全面可靠的 ,这点无容置疑 , 所以生产级的大项目 ,相信还是会以 Spring 为主流。
但是个人的一些小 Demo ,和一些资源更少的场景 ,就可以考虑使用 Solon了 ,不用自己造轮子 ,也不会浪费 Spring 的性能。
关于设计的思想 :
核心源码大致走了一遍 ,Solon 本身设计上是有一些明确的思路的 :
- AOP 更偏向于显示 的声明(没试过能不能通过包扫描的方式)
- 其实写了这么多年代码 ,
Spring AOP 的使用率并不高,更多的是Spring自己在用 ,业务上用到也主要是日志级别的处理 
 - 其实写了这么多年代码 ,
 - Spring 的容器确实过于复杂了 ,各种 
Listener,Aware,PostProcessor, 很全面 ,但是也太难了、- 容器的处理 Solon 只有两个核心 : 
Plugin (组件) + Bean 加载 
 - 容器的处理 Solon 只有两个核心 : 
 - IOC 层面也简单很多 ,这里就不细说了 ,看过 Spring 源码的都能懂
- 没看过的推荐我自己的这个专栏 @ juejin.cn/column/6961...
 
 
最后的最后 ❤️❤️❤️👇👇👇
- 👈 欢迎关注 ,超200篇优质文章,未来持续高质量输出 🎉🎉
 - 🔥🔥🔥 系列文章集合,高并发,源码应有尽有 👍👍
 - 走过路过不要错过 ,知识无价还不收钱 ❗❗