首页 > 其他分享 >solon架构(spring-boot未来最有效的竞争力)

solon架构(spring-boot未来最有效的竞争力)

时间:2023-05-12 11:34:19浏览次数:47  
标签:solon String spring boot context ApiException return public

  一、现在基本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.0Springboot 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));
    }


}

  四、开发的几个重要部分,简单搞了一下,其他的都有对应策略,这里不细讲了。

  官网地址:https://solon.noear.org/

  五、总体来说这套框架还是非常优秀的。但是还是存在很多bug吧,个人准备弄着玩。

  demo地址:https://gitee.com/lilin409546297/solon-demo

 

标签:solon,String,spring,boot,context,ApiException,return,public
From: https://www.cnblogs.com/ll409546297/p/17393578.html

相关文章

  • 基于can总线的dsp280049c升级方案 提供bootloader源代码,上位机,上位机
    基于can总线的dsp280049c升级方案提供bootloader源代码,上位机,上位机源码,使用说明。已经通过项目验证。ID:473000679427129206......
  • DSP28035串口升级方案 带bootloader源码,测试app工程源码,
    DSP28035串口升级方案带bootloader源码,测试app工程源码,上位机源码,说明文档。上位机采用vs2013开发,c#。工程采用ccs10.3.1开发。ID:612000682777940502......
  • spring出现依赖关系形成循环问题,The dependencies of some of the beans in the appli
    出现这个问题大多使用的springboot都是在2.6.x以上,springboot在2.6.x就将依赖循环禁用了,解决方式有以下几种:解决方式:1、第一种解决方式:可以优化自己程序的逻辑,优化bean的依赖关系,只要不形成一个环状就不会出该问题了 2、第二种解决方式:可以使用@Lazy注解(懒加载)和@Autowired注......
  • dsp28335串口升级方案 提供bootloader源代码,用户工程源代码,上位
    dsp28335串口升级方案提供bootloader源代码,用户工程源代码,上位机以及上位机源代码。提供使用说明,通信协议。ID:441000666865117012......
  • 【Spring 事务】【一】 Spring 事务简介
    1 前言本节我们开始来看看Spring事务哈,大家看之前首先要看过IOC、AOP、甚至代理哈,如果这些你不知道原理,你看任何东西都会很费劲,比如Bean的生命周期、AOP的切入时机、什么时候创建代理以及执行时机,这些不知道的话,你就看事务的话,会很懵,当然前提是大家是带着思考看的哈,单纯看不......
  • springboot 大文件切片上传
    1.前端(vueelementui&原生)初始变量声明: currentFile:{},//当前上传的文件bigFileSliceCount:20,//大文件切片后的子文件数量(也可使用其它限定方式,如按照文件大小,每10MB切一片,此处采用的是固定切片的子文件数量的方式倒推切片大小) 接口:切片上传图片&合并......
  • java基于springboot+html的学生就业管理系统的设计与实现,附源码+数据库+文档,包安装调
    1、项目介绍本系统是利用现代化的计算机网络技术将传统信息宣传方式整合,按照实践过程设计完成的。同时完善服务,初步设计一个学生就业管理系统平台以利于相关的事务操作。为了使系统在各项管理中发挥更大的作用,实现计算机信息化高效的管理,现将开发目标功能需求介绍如下:(1)管理员模......
  • 存下吧!Spring高频面试题总结
    Spring是什么?Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架。Spring的优点通过控制反转和依赖注入实现松耦合。支持面向切面的编程,并且把应用业务逻辑和系统服务分开。通过切面和模板减少样板式代码。声明式事务的支持。可以从单调繁冗的事务管理代码中解脱......
  • SpringBoot中单元测试如何对包含AopContext.currentProxy()的方法进行测试
    今天在工作中遇到一个问题,一个Service类中有一个方法,其中使用了AopContext.currentProxy()去访问自身的函数,例如intresult=((OrderServiceImpl)AopContext.currentProxy()).save();单元测试方法如下:@InjectMocksprivateOrderServiceImplorderServiceUnderTest;@Tes......
  • SpringBoot3.x中spring.factories SPI 服务发现机制的改变
    目录一、基础背景二、服务发现接口spring.factories三、服务发现机制调用1.启动SpringApplication2.加载SpringApplication.run1.SpringApplication.createApplicationContext2.SpringApplication.prepareContext3.SpringApplication.refreshContext4.AutoConfigurationImportSele......