Solon 是一個相似Springboot的微型開發框架,也是一個不基於Servlet的開發框架。項目從2018年啓動以來,參考過大量前人做品;歷時兩年,3500屢次的commit;內核保持0.1m的身材,超高的Web跑分,良好的使用體驗。java
Solon 強調:剋制 + 簡潔 + 開放的原則;力求:更小、更快、更自由的體驗。git
內核0.1m,最小Web開發單位0.2m(相比Springboot項目包,小到能夠乎略不計了)。github
具用戶反映,某些項目切換到Solon後,能夠縮減到原來10%的包大小。web
本機helloworld測試,啓動最快可達0.09s,Qps可達12萬之多。可參考:《helloworld_wrk_test》。spring
// 除了注入模式以外,還能夠按需手動 // //手動獲取配置 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應用。api
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 | 數據訪問類 |
@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; } }
@Controller public class DemoController{ @Db BaseMapper<UserModel> userService; @Tran @Mapping("/user/update") public void udpUser(long user_id, UserModel user){ userService.updateById(user); } }
@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"; } }
@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); } }
// // 一個數據主從庫的示例 // @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; } }
//示例:定製統一輸出控制基類,並統一開啓驗證 // @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)); } } } }
@Configuration public class DemoConfiguration implements ServletContainerInitializer{ @Override public void onStartup(Set<Class<?>> set, ServletContext servletContext) throws ServletException { //... } }
@WebFilter("/demo/*") public class DemoFilter implements Filter { @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain filterChain) throws IOException, ServletException { res.getWriter().write("Hello,我把你過濾了"); } }
//[服務端] @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"));
//[服務端] @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); } }
//[定義接口],通常狀況下不須要加任何註解 // 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"; } }
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); }); } }
src/main/resources/META-INF/solon/solon.extend.aspect.properties
solon.plugin=org.noear.solon.extend.aspect.XPluginImp
//[收集異常] 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);