Solon 特性简集,相较于 Springboot 有什么区别?
Solon 是一个类似Springboot的微型开发框架,也是一个不基于Servlet的开发框架。项目从2018年启动以来,参考过大量前人作品;历时两年,3500多次的commit;内核保持0.1m的身材,超高的Web跑分,良好的使用体验。
Solon 强调:克制 + 简洁 + 开放的原则;力求:更小、更快、更自由的体验。
所谓更小:
内核0.1m,最小Web开发单位0.2m(相比Springboot项目包,小到可以乎略不计了)。
具用户反映,某些项目切换到Solon后,可以缩减到原来10%的包大小。
所谓更快:
本机helloworld测试,启动最快可达0.09s,Qps可达12万之多。可参考:《helloworld_wrk_test》。
所谓更自由:
- 代码操控自由:
// 除了注入模式之外,还可以按需手动
//
//手动获取配置
String userName = Solon.cfg().get("user.name");
Properties dbcfg = Solon.cfg().getProp("db");
//手动获取容器里的Bean
UserService userService = Aop.get(UserService.class);
//手动监听http post请求
Solon.global().post("/user/update", x-> userService.updateById(x.paramMap()));
- 框架选择自由:
可以用solon-web这样的快速开发集成包。也可以按项目需要选择不同的插件组装,比如:为非Solon项目添加solon.boot.jlhttp,0.2m即可让项目实现http+rpc开发;还可以用MVC开发Socket应用。
特性简集:
1、与Springboot的常用注解比较
| Solon 1.2.12 | Springboot 2.3.3 | 说明 |
|---|---|---|
| @Inject * | @Autowired | 注入Bean(by type) |
| @Inject("name") | @Qualifier+@Autowired | 注入Bean(by name) |
| @Inject("${name}") | @Value("${name}") | 注入配置 |
| @Component | @Component | 托管组件 |
| @Singleton | @Scope(“singleton”) | 单例(Solon 默认是单例) |
| @Singleton(false) | @Scope(“prototype”) | 非单例 |
| @Init * | @PostConstruct | 构造完成并注入后的初始化 |
| @Configuration | @Configuration | 配置类 |
| @Bean | @Bean | 配置组件 |
| @Mapping | @RequestMapping,@GetMapping... | 映射 |
| @Param | @RequestParam | 请求参数 |
| @Controller | @Controller,@RestController | 控制器类 |
| @Service | @Service | 服务类 |
| @Dao | @Dao | 数据访问类 |
- Solon 的 @Inject 算是: Spring 的@Value、@Autowired、@Qualifier 三者的结合,但又不完全等价
- Solon 托管的 Bean 初始化顺序:new() - > @Inject - > @Init
- 注1:@Inject 的参数注入,只在Method@Bean上有效
- 注2:@Inject 的类型注入,只在@Configuration类上有效
2、重要的区别,Solon不是基于Servlet的开发框架
- 与Springboot相似的体验,但使用Context包装请求上下文。Helloworld效果如下:
@Controller
public class App{
public static void main(String[] args){
Solon.start(App.class, args);
}
@Inject("${app.name}")
String appName;
@Mapping("/")
public Object home(Context c, @Param(defaultValue="noear") String name){
return appName + ": Hello " + name;
}
}
3、与Springboot相似的事务支持@Tran
- 采用Springboot相同的事件传播机制及隔离级别
@Controller
public class DemoController{
@Db
BaseMapper<UserModel> userService;
@Tran
@Mapping("/user/update")
public void udpUser(long user_id, UserModel user){
userService.updateById(user);
}
}
4、与Springboot不同的较验方案@Valid
- Solon 的方案更侧重较验参数(及批量较验),且强调可见性(即与处理函数在一起)
@Valid
@Controller
public class DemoController {
@NoRepeatSubmit
@NotNull({"name", "icon", "mobile"})
@Mapping("/valid")
public String test(String name, String icon, @Pattern("13\\d{9}") String mobile) {
return "OK";
}
@Whitelist
@Mapping("/valid/test2")
public String test2() {
return "OK";
}
}
5、基于标签管理的缓存支持@Cache,与Springboot略有不同
- 基于标签管理,避免不必要的KEY冲突
@Controller
public class DemoController{
@Db
BaseMapper<UserModel> userService;
@CacheRemove(tags = "user_${user_id}")
@Mapping("/user/update")
public void udpUser(int user_id, UserModel user){
userService.updateById(user);
}
@Cache(tags = "user_${user_id}")
public UserModel getUser(int user_id){
return userService.selectById(user_id);
}
}
6、具备语义特性的Bean定义,实现更多可能性
- 通过语义特性,为Bean增加特性描述;从而实现一些附加的能力
//
// 一个数据主从库的示例
//
@Configuration
public class Config {
//申明 db2 是 db1 为的从库
@Bean(value = "db1", attrs = { "slaves=db2" })
public DataSource db1(@Inject("${test.db1}") HikariDataSource dataSource) {
return dataSource;
}
@Bean("db2")
public DataSource db2(@Inject("${test.db2}") HikariDataSource dataSource) {
return dataSource;
}
}
7、支持数据渲染(或输出格式化)的自我控制支持
- 定制特定场景的控制器基类,负责统一格式化输出
//示例:定制统一输出控制基类,并统一开启验证
//
@Valid
public class ControllerBase implements Render {
@Override
public void render(Object obj, Context ctx) throws Throwable {
if (obj == null) {
return;
}
if (obj instanceof String) {
ctx.output((String) obj);
} else {
if (obj instanceof ONode) {
ctx.outputAsJson(((ONode) obj).toJson());
} else {
if (obj instanceof UapiCode) {
//此处是重点,把一些特别的类型进行标准化转换
//
UapiCode err = (UapiCode) obj;
obj = Result.failure(err.getCode(), UapiCodes.getDescription(err));
}
if (obj instanceof Throwable) {
//此处是重点,把异常进行标准化转换
//
Throwable err = (Throwable) obj;
obj = Result.failure(err.getMessage());
}
ctx.outputAsJson(ONode.stringify(obj));
}
}
}
}
8、不基于Servlet,却很有 Servlet 亲和度。当使用servlet相关的组件时(也支持jsp + tld)
- 支持 ServletContainerInitializer 配置
@Configuration
public class DemoConfiguration implements ServletContainerInitializer{
@Override
public void onStartup(Set<Class<?>> set, ServletContext servletContext) throws ServletException {
//...
}
}
- 支持 Servlet api 注解
@WebFilter("/demo/*")
public class DemoFilter implements Filter {
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain filterChain) throws IOException, ServletException {
res.getWriter().write("Hello,我把你过滤了");
}
}
9、为服务开发而生的SockeD组件,实现http,socket,websocket相同的信号处理。
- 支持MVC+RPC开发模式
//[服务端]
@Mapping(value = "/demoe/rpc", method = MethodType.SOCKET)
@Component(remoting = true)
public class HelloRpcServiceImpl implements HelloRpcService {
public String hello(String name) {
return "name=" + name;
}
}
//[客户端]
var rpc = SocketD.create("tcp://localhost:28080", HelloRpcService.class);
System.out.println("RPC result: " + rpc.hello("noear"));
- 支持单链接双向RPC开发模式(基于上例扩展)
//[服务端]
@Mapping(value = "/demoe/rpc", method = MethodType.SOCKET)
@Component(remoting = true)
public class HelloRpcServiceImpl implements HelloRpcService {
public String hello(String name) {
//
//[服务端] 调用 [客户端] 的 rpc,从而形成单链接双向RPC
//
NameRpcService rpc = SocketD.create(Context.current(), NameRpcService.class);
name = rpc.name(name);
return "name=" + name;
}
}
- 支持消息发送+监听开发模式
//[服务端]
@ServerEndpoint
public class ServerListener implements Listener {
@Override
public void onMessage(Session session, Message message) {
if(message.flag() == MessageFlag.heartbeat){
System.out.println("服务端:我收到心跳");
}else {
System.out.println("服务端:我收到:" + message);
//session.send(Message.wrapResponse(message, "我收到了"));
}
}
}
//[客户端]
var session = SocketD.createSession("tcp://localhost:28080");
session.send("noear");
//session.sendAndCallback("noear", (rst)->{}); //发送并异常回调
//var rst = session.sendAndResponse("noear"); //发送并等待响应
System.out.println(rst);
- 支持消息订阅开发模式
//[客户端]
@ClientEndpoint(uri = "tcp://localhost:28080")
public class ClientListener implements Listener {
@Override
public void onMessage(Session session, Message message) {
//之后,就等着收消息
System.out.println("客户端2:我收到了:" + message);
}
}
10、专属RPC客户端组件:Nami
- 类似于Springboot + Feign的关系,但Nami更简洁(Solon 也可以用Feign)
//[定义接口],一般情况下不需要加任何注解
//
public interface UserService {
UserModel getUser(Integer userId);
}
//[服务端] Component.remoting = true,即为组件开启远程服务
//
@Mappin("user")
@Component(remoting = true)
public class UserServiceImpl implements UserService{
public UserModel getUser(Integer userId){
return ...;
}
}
//[消费端]
//
@Mapping("demo")
@Controller
public class DemoController {
//直接指定服务端地址
@NamiClient("http://localhost:8080/user/")
UserService userService;
//使用负载
@NamiClient("local:/user/")
UserService userService2;
@Mapping("test")
public void test() {
UserModel user = userService.getUser(12);
System.out.println(user);
user = userService2.getUser(23);
System.out.println(user);
}
}
/**
* 定义一个负载器(可以对接发现服务)
* */
@Component("local")
public class RpcUpstream implements LoadBalance {
@Override
public String getServer() {
return "http://localhost:8080";
}
}
11、Solon的加强版SPI扩展机制 - 以增加注解为例
- 1.新建个模块,实现Plugin接口(以增加@Service注解支持为例)
public class XPluginImp implements Plugin {
@Override
public void start(SolonApp app) {
Aop.context().beanBuilderAdd(Service.class, (clz, bw, anno) -> {
bw.proxySet(BeanProxyImp.global());
Aop.context().beanRegister(bw, "", true);
});
}
}
- 2.增加配置文件
src/main/resources/META-INF/solon/solon.extend.aspect.properties
- 3.增加配置内容,打包发布即可
solon.plugin=org.noear.solon.extend.aspect.XPluginImp
12、Solon内部的事件总线EventBus的妙用
- 通过事件总线收集异常
//[收集异常]
EventBus.push(err);
//[订阅异常]
EventBus.subscribe(Throwable.class,(event)->{
event.printStackTrace();
});
//或通过SolonApp订阅
app.onEvent(Throwable.class, (err)->{
err.printStackTrace();
});
//或通过组件订阅
@Component
public class ErrorListener implements EventListener<Throwable> {
@Override
public void onEvent(Throwable err) {
err.printStackTrace();
}
}
- 通过事件总线扩展配置对象
//
// 插件开发时,较常见
//
SqlManagerBuilder builder = new SqlManagerBuilder(ds);
EventBus.push(builder);
附:Solon项目地址
- gitee: https://gitee.com/noear/solon
- github: https://github.com/noear/solon
Solon 特性简集,相较于 Springboot 有什么区别?的更多相关文章
- 简析 Tomcat 、Nginx 与 Apache 的区别
简析 Tomcat .Nginx 与 Apache 的区别 本文讲的是简析 Tomcat .Nginx 与Apache的区别, 经常在用 apache 和 tomcat 等这些服务器,可是总感觉还是不 ...
- Springboot和SpringMVC区别
Springboot和SpringMVC区别----http://www.cnblogs.com/xdyixia/p/9279644.html
- Redis的高可用详解:Redis哨兵、复制、集群的设计原理,以及区别
谈到Redis服务器的高可用,如何保证备份的机器是原始服务器的完整备份呢?这时候就需要哨兵和复制. 哨兵(Sentinel):可以管理多个Redis服务器,它提供了监控,提醒以及自动的故障转移的功能. ...
- Redis哨兵、复制、集群的设计原理,以及区别
广西SEO:谈到Redis服务器的高可用,如何保证备份的机器是原始服务器的完整备份呢?这时候就需要哨兵和复制. **哨兵(Sentinel):**可以管理多个Redis服务器,它提供了监控,提醒以及自 ...
- Redis3.2.5 集群搭建以及Spring-boot测试
1:集群中的机器信息 IP PORT 192.168.3.10 7000,7001,7002 192.168.3.11 7004,7005,7006 2:安装Redis 分别在10与11机器上面安装R ...
- activemq的高级特性:集群实战
高级特性实战需求 当消费端是多个集群,集群A又包含多个服务. 当每个集群都要接受相同的一批消息,而集群内的每个服务都去分摊消息. 解决办法一:级联 增加一个中转者.但是不是特别的优化,而且性能也不是特 ...
- docker compose搭建redis7.0.4高可用一主二从三哨兵集群并整合SpringBoot【图文完整版】
一.前言 redis在我们企业级开发中是很常见的,但是单个redis不能保证我们的稳定使用,所以我们要建立一个集群. redis有两种高可用的方案: High availability with Re ...
- css3新特性合集
转自:https://www.cnblogs.com/xiaoxie2016/p/5964694.html (若原作者对此转载有疑问,联系删除,谢谢!) animation IE10 anima ...
- [转]利用Jenkins的Pipeline实现集群自动化部署SpringBoot项目
环境准备 Git: 安装部署使用略. Jenkins: 2.46.2版本安装部署略(修改jenkins执行用户为root,省得配置权限) JDK: 安装部署略. Maven: 安装部署略. 服务器免密 ...
随机推荐
- HEXO | 给博客添加RSS
Hexo是一个简洁.高效.易用的博客框架,同时它拥有十分丰富的主题环境,本次我们所谈到的是cards主题,但是该主题的原生环境里没有相关的rss配置,所以我们需要采用手动添加的方式,进而实现rss功能 ...
- ModelViewSet + ModelSerializer
ModelSerializer (封装好的序列化器,不需要我们写字段) from rest_framework import serializers from .models import * cl ...
- 「考试」联赛模拟36-39,noip晚间小测2-3
36.1 party(CF623D) 很是鸡贼的一道题 首先要明确一点,抓人是有策略,而不是随机的,可以认为等同于按一个给定的顺序猜人,那么这时猜中的概率就只是抓住这个人的概率了 对于每一次猜测,因为 ...
- 最新小样本学习综述 A Survey on Few-Shot Learning | 四大模型Multitask Learning、Embedding Learning、External Memory…
目录 原文链接: 小样本学习与智能前沿 01 Multitask Learning 01.1 Parameter Sharing 01.2 Parameter Tying. 02 Embedding ...
- PyQt学习随笔:Model/View中设置视图数据项可编辑的方法
在视图对象中调用setEditTriggers方法可以设置视图对象中的数据项是否可编辑以及编辑的触发方法. setEditTriggers方法是QAbstractItemView的方法,语法如下: s ...
- KafKa简介和利用docker配置kafka集群及开发环境
KafKa的基本认识,写的很好的一篇博客:https://www.cnblogs.com/sujing/p/10960832.html 问题:1.kafka是什么?Kafka是一种高吞吐量的分布式发布 ...
- PR全套插件一键安装
PR全套插件一键安装,无需注册码软件也是我在别的地方搬来的,自己用着很好,决定分享出来! 我的PR版本是2019,用着没有任何问题.我没有安装其他版本PR,所以无法测试,不过应该是可以用的. 使用截图 ...
- hash相关
转译☞:https://www.cs.rice.edu/~as143/COMP441_Spring17/scribe/lect4.pdf 1 大规模图片检索问题 基于树模型的算法在分类跟聚类中很受欢迎 ...
- rsync全网备份low方法
要求: 1.基本备份要求已知3 台服务器主机名分别为web01.backup .nfs01,主机信息见下表:服务器说明外网IP(NAT) 内网IP(NAT) 主机名称nginx web 服务器10.0 ...
- Angular:自定义属性指令
①在命令行窗口下用 CLI 命令ng g directive创建指令类文件 ②将directives/light.directive.ts文件改造一番 import { Directive, Elem ...