一、現在基本WEB的開發都是用spring-boot來做開發了,但是springboot存在很多弊端。體量大,啓動慢等。優點就是生態比較完整,文檔說明也比較多。
二、solon架構,是我學習其他框架兼容時瞭解的,說說其區別之處。
1)solon目前已經有一定規模了,有自己的生態圈了吧
2)solon算的上新生態的開發框架了。
優點:更快、更小、更簡單。(主要體現在啓動很快,目前基本在5S(不絕對,官網說5~10倍)以內,Qps10萬+。打包更小,官方說小1/2~1/10。內存更小。簡單主要是在開發上面)
缺點:文檔相對較少,雖然後官方實例,但是要完整的用起來還是得自己研究。且存在BUG。
3)與springboot的區別
a、與 Springboot 的常用註解比較
Solon 2.2.0 | Springboot 2.7.8 | 說明 |
---|---|---|
@Inject * | @Autowired | 注入Bean(by type) |
@Inject("name") | @Qualifier+@Autowired | 注入Bean(by name) |
@Inject("${name}") | @Value("${name}") + @ConfigurationProperties(prefix="name") | 注入配置 |
@Singleton | @Scope(“singleton”) | 單例(Solon 默認是單例) |
@Singleton(false) | @Scope(“prototype”) | 非單例 |
@Import | @Import + @ComponentScan | 配置組件導入或掃描(一般加在啓動類上) |
@PropertySource | @PropertySource | 配置屬性源(一般加在啓動類上) |
@Configuration | @Configuration | 配置類 |
@Bean | @Bean | 配置Bean |
@Condition | @ConditionalOnClass + @ConditionalOnProperty | 配置條件 |
@Controller | @Controller,@RestController | 控制器類 |
@Remoting | 遠程控制器類(即 Rpc 服務端) | |
@Mapping ... | @RequestMapping,@GetMapping... | 映射 |
@Param | @RequestParam | 請求參數 |
@Header | @RequestHeader | 請求頭 |
@Body | @RequestBody | 請求體 |
@Cookie | @CookieValue | 請求Cookie |
@Component | @Component | 普通託管組件 |
@ProxyComponent | @Service,@Dao,@Repository | 代理託管組件 |
@TestPropertySource | @TestPropertySource | 配置測試屬性源 |
@TestRollback | @TestRollback | 執行測試回滾 |
LifecycleBean | InitializingBean + DisposableBean | 組件初始化和銷燬 |
Solon 2.2.0 | Java EE(Java 11 後更名爲 Jakarta) | |
LifecycleBean::start | @PostConstruct | 組件構造完成並注入後的初始化 |
LifecycleBean::stop | @PreDestroy | 組件銷燬 |
b、重要的區別,Solon 不是基於 Servlet 的開發框架
c、其他區別也比較多。這裏不一一說明。參考地址:https://solon.noear.org/article/compare-springboot
三、開發部分說明:(這裏只有cloud相關的,單體的相對簡單)
1)集羣註冊與發現,以nacos爲例。
<dependency> <groupId>org.noear</groupId> <artifactId>nacos-solon-cloud-plugin</artifactId> </dependency>
2)配置app.yml(測試只能在resources下),具體配置:https://solon.noear.org/article/148
solon: app: name: demo group: xbd namespace: public cloud: nacos: server: 127.0.0.1:8848
3)rpc適用簡單,優點類似dubbo的調用方式,抽公共的接口就行。
<dependency> <groupId>org.noear</groupId> <artifactId>solon-rpc</artifactId> </dependency>
server
@Service @Mapping("/user") @Remoting public class UserServiceImpl implements IUserService { @Tran public List<String> listUser() { List<String> users = new ArrayList<String>(); for (int i = 0; i < 5; i++) { users.add("user" + i); } return users; } }
client
@NamiClient(name = "demo", path = "/user", headers = ContentTypes.PROTOBUF) private IUserService userService;
4)認證(jwt)
<dependency> <groupId>org.noear</groupId> <artifactId>solon.auth</artifactId> </dependency> <dependency> <groupId>org.noear</groupId> <artifactId>solon.sessionstate.jwt</artifactId> </dependency>
配置
server: port: 8673 session: #超時配置。單位秒(可不配,默認:7200) timeout: 86400 state: #可共享域配置(可不配,默認當前服務域名;多系統共享時要配置) domain: "xbd.auth.com" jwt: #Jwt 令牌變量名;(可不配,默認:TOKEN) name: "Authorization" #Jwt 密鑰(使用 JwtUtils.createKey() 生成);(可不配,默認:xxx) secret: "QQRsCNRmPqZQtAo5ANqZ9OgG5N2mOqZl5D3i1VSDvJs=" #Jwt 令牌前綴(可不配,默認:空) prefix: "Bearer" #Jwt 允許超時;(可不配,默認:true);false,則token一直有效 allowExpire: true #Jwt 允許自動輸出;(可不配,默認:true);flase,則不向header 或 cookie 設置值(由用戶手動控制) allowAutoIssue: false #Jwt 允許使用Header傳遞;(可不配,默認:使用 Cookie 傳遞);true,則使用 header 傳遞 allowUseHeader: true
@Controller @Mapping("/auth") public class AuthController { @Inject private IUserService userService; @Get @Mapping("/test") public Result test(Context context) { return Result.succeed(context.session("userId")); } @Post @Mapping("/login") public Result login(Context context, @Body JSONObject jsonObject) { String username = jsonObject.getString("username"); String password = jsonObject.getString("password"); boolean result = userService.checkUser(context, username, password); return result ? Result.succeed() : Result.failure("密碼錯誤!"); } }
@Service public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService { @Override public boolean checkUser(Context context, String username, String password) { QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.eq("username", username); User user = baseMapper.selectOne(wrapper); if (user == null) { throw new ApiException("用戶不存在"); } boolean check = DigestUtil.bcryptCheck(password, user.getPassword()); if (check) { context.sessionSet("userId", user.getId()); context.sessionSet("username", user.getUsername()); context.cookieSet("TOKEN", context.sessionState().sessionToken()); } return check; } }
@Configuration public class AuthConfiguration { @Bean public AuthAdapter authAdapter() { return new AuthAdapter() // .loginUrl("/login") //設定登錄地址,未登錄時自動跳轉(如果不設定,則輸出401錯誤) .addRule(r -> r.include("/**").exclude("/auth/login").verifyLogined()) //添加規則 .processor(new AuthProcessor() { @Override public boolean verifyIp(String ip) { return false; } @Override public boolean verifyLogined() { Context context = ContextUtil.current(); if (context.session("userId") != null) { return true; } return false; } @Override public boolean verifyPath(String path, String method) { return false; } @Override public boolean verifyPermissions(String[] permissions, Logical logical) { return false; } @Override public boolean verifyRoles(String[] roles, Logical logical) { return false; } }).failure((ctx, rst) -> { System.out.println(rst); ctx.output(JSONObject.toJSONString(rst)); }); } }
參考地址:https://solon.noear.org/article/59 https://solon.noear.org/article/132
說實話這個認證太簡單了,二次校驗等等問題,相對於security真的太輕量級了。在結合redis緩存,基本可以最到登陸和登出的一些判定了。
5)多數據源:地址:https://solon.noear.org/article/231
<dependency> <groupId>org.noear</groupId> <artifactId>mybatis-plus-extension-solon-plugin</artifactId> </dependency>
@Configuration public class DataSourceConfiguration { @Bean(name = "default", typed = true) public DataSource dataSource(@Inject("${datasource.default}") HikariDataSource dataSource){ return dataSource; } }
不多說,應用就會了。
6)網關(官網推薦nginx等三方產品):說實話這個就真的比較一般了。數據轉發出現亂碼的情況,改用其他的就正常。
<dependency> <groupId>org.noear</groupId> <artifactId>solon-api</artifactId> </dependency> <!-- 適用httputils亂碼 --> <dependency> <groupId>org.noear</groupId> <artifactId>solon.cloud.httputils</artifactId> </dependency>
@Slf4j @Component @Mapping("/**") public class RouterConfiguration extends Gateway { @Override protected void register() { //添加個前置處理 before(ctx -> { String token = ctx.sessionState().sessionToken(); System.out.println(token); }); //添加缺省處理 add(Any.class); } public static class Any { @Mapping public String any(Context ctx) throws Throwable { //檢測請求,並嘗試獲取二級接口服務名 String serviceName = ctx.pathMap("/{serviceName}/**").get("serviceName"); if (serviceName == null) { log.error(MessageFormat.format("{0} service not found", serviceName)); throw new ApiException(500, "服務異常!"); } //轉發請求 String method = ctx.method(); String server = LoadBalance.get(serviceName).getServer(); if (server == null) { log.error(MessageFormat.format("{0} service not exception", serviceName)); throw new ApiException(500, "服務異常!"); } HttpRequest request = HttpUtil.createRequest(Method.valueOf(method), server + ctx.path().substring(StrUtil.indexOf(ctx.path(), '/', 2))); request.addHeaders(ctx.headerMap()); request.header(CloudClient.trace().HEADER_TRACE_ID_NAME(), CloudClient.trace().getTraceId()); request.body(ctx.body()); HttpResponse response = request.execute(); if (200 == response.getStatus()) { return response.body(); } else { throw new ApiException(response.getStatus(), response.body()); } } } @Override public void render(Object obj, Context context) throws Throwable { if (context.getRendered()) { return; } context.setRendered(true); Object result; if (obj instanceof ApiException) { ApiException exception = (ApiException) obj; result = exception.data(); } else { //處理java bean數據(爲擴展新的) result = obj; } context.result = result; //如果想對輸出時間點做控制,可以不在這裏渲染(由後置處理進行渲染) context.render(context.result); } }
public class ApiException extends DataThrowable { public ApiException() { super.data(Result.failure()); } public ApiException(int code, String message) { super.data(Result.failure(code, message)); } public ApiException(Throwable cause) { super(cause); super.data(Result.failure(cause.getMessage())); } public ApiException(String message) { super(message); super.data(Result.failure(message)); } public ApiException(String message, Throwable cause) { super(message, cause); super.data(Result.failure(message)); } }
四、開發的幾個重要部分,簡單搞了一下,其他的都有對應策略,這裏不細講了。
五、總體來說這套框架還是非常優秀的。但是還是存在很多bug吧,個人準備弄着玩。
demo地址:https://gitee.com/lilin409546297/solon-demo