cxl
Published on 2025-08-04 / 8 Visits
0
0

Solon框架-代码初步实践

本文将采用最新的Solon 3.4.2 版本(截至2025年8月),通过一个完整的渐进式项目示例,跟我一起从零开始构建基于Solon框架的JavaWeb应用。我们将从基础项目搭建开始,逐步添加Web模块、数据访问层、freemarker动态模板等核心功能,最终形成一个功能完整的现代化应用,将粗略展示Solon框架在实际开发中的入门实践。

环境准备与项目初始化

在开始Solon项目开发前,我们需要完成开发环境的准备和基础项目的创建。Solon 3.4.2 支持Java 8至Java 24的所有版本,推荐使用Java 17 LTS以获得最佳性能和特性支持。

环境要求

  • JDK 17+

  • Maven 3.9+

  • IDE(推荐IntelliJ IDEA 2025或VS Code with Java Pack)

创建demo项目

使用官网下的项目生成器勾选最小依赖组合点击生成按钮下载项目,然后通过IDEA导入,我选的是带jetty的Solon Web Servlet

查看pom.xml

  <?xml version="1.0" encoding="UTF-8"?>
  <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>
  
    <!-- 继承自Solon框架的父POM,简化了子项目的依赖和插件管理,避免了重复配置 -->
      <parent>
          <groupId>org.noear</groupId>
          <artifactId>solon-parent</artifactId>
          <version>3.4.2</version>
          <relativePath />
      </parent>
  
      <groupId>com.example</groupId>
      <artifactId>demo</artifactId>
      <version>1.0</version>
      <packaging>jar</packaging>
      <description>Demo project for Solon</description>
  
      <properties>
          <java.version>17</java.version>
      </properties>
  
      <dependencies>
          <!--  
              Solon框架的核心库,提供IoC容器、AOP支持、基础工具类等核心功能,是其他模块的基础;
              solon-lib(基础功能库)是个快捷组合包,自己没有代码,而是组合最基础的日常插件。
           -->
          <dependency>
              <groupId>org.noear</groupId>
              <artifactId>solon-lib</artifactId>
          </dependency>
          <!--  集成Jetty嵌入式Web服务器,无需额外配置即可运行Web应用,适合快速开发和部署 -->
          <dependency>
              <groupId>org.noear</groupId>
              <artifactId>solon-boot-jetty</artifactId>
          </dependency>
          <!--  Solon的JSON序列化/反序列化插件,基于Snack3实现,用于处理HTTP请求和响应的JSON数据转换 -->
          <dependency>
              <groupId>org.noear</groupId>
              <artifactId>solon-serialization-snack3</artifactId>
          </dependency>
          <!--  静态资源处理模块,支持自动映射/static目录下的静态文件(如HTML、CSS、JS) -->
          <dependency>
              <groupId>org.noear</groupId>
              <artifactId>solon-web-staticfiles</artifactId>
          </dependency>
          <!--  跨域资源共享(CORS)支持模块,简化前后端分离开发中的跨域配置 -->
          <dependency>
              <groupId>org.noear</groupId>
              <artifactId>solon-web-cors</artifactId>
          </dependency>
          <!--  提供参数校验和安全验证功能,如注解驱动的字段校验(类似Spring Validation) -->
          <dependency>
              <groupId>org.noear</groupId>
              <artifactId>solon-security-validation</artifactId>
          </dependency>
          <!--  集成FreeMarker模板引擎,用于服务端渲染动态页面(如HTML模板) -->
          <dependency>
              <groupId>org.noear</groupId>
              <artifactId>solon-view-freemarker</artifactId>
          </dependency>
          <!--  logback日志框架 -->
          <dependency>
              <groupId>org.noear</groupId>
              <artifactId>solon-logging-logback</artifactId>
          </dependency>
          <dependency>
              <groupId>org.projectlombok</groupId>
              <artifactId>lombok</artifactId>
              <scope>provided</scope>
          </dependency>
          <!--  Solon的测试支持模块 -->
          <dependency>
              <groupId>org.noear</groupId>
              <artifactId>solon-test</artifactId>
              <scope>test</scope>
          </dependency>
      </dependencies>
      <build>
          <finalName>${project.artifactId}</finalName>
          <plugins>
              <plugin>
                  <groupId>org.noear</groupId>
                  <artifactId>solon-maven-plugin</artifactId>
              </plugin>
          </plugins>
      </build>
      <repositories>		
          <repository>
  			<id>tencent</id>
  			<url>https://mirrors.cloud.tencent.com/nexus/repository/maven-public/</url>
  			<snapshots>
  				<enabled>false</enabled>
  			</snapshots>
  		</repository>
      </repositories>
  </project>

打开主启动类App.java,熟练SpringBoot的开发想必已经对这种模板似的代码不陌生了,这里就不解释了

  import org.noear.solon.Solon;
  import org.noear.solon.annotation.SolonMain;
  
  @SolonMain
  public class App {
      public static void main(String[] args) {
          Solon.start(App.class, args);
      }
  }

打开默认的接口类DemoController.java:

  import org.noear.solon.annotation.Controller;
  import org.noear.solon.annotation.Mapping;
  import org.noear.solon.annotation.Param;
  
  @Controller
  public class DemoController {
      @Mapping("/hello")
      public String hello(@Param(defaultValue = "world") String name) {
          return String.format("Hello %s!", name);
      }
  }

验证项目结构​:
运行主类后,控制台应显示类似以下日志,表明Solon已成功启动,接着访问http://localhost:8080/hello能正常返回结果:

  App: Start loading
  App: Plugin starting
  Render mapping: @json=StringSerializerRender#snack3-json
  Render mapping: @type_json=StringSerializerRender#snack3-json
  View: load: FreemarkerRender
  View: load: org.noear.solon.view.freemarker.FreemarkerRender
  Render mapping: .ftl=FreemarkerRender
  App: Bean scanning
  Logging initialized @1082ms to org.eclipse.jetty.util.log.Slf4jLog
  jetty-9.4.57.v20241219; built: 2025-01-08T21:24:30.412Z; git: df524e6b29271c2e09ba9aea83c18dc9db464a31; jvm 17.0.15+6-LTS
  DefaultSessionIdManager workerName=node0
  No SessionScavenger set, using defaults
  node0 Scavenging every 600000ms
  Started o.e.j.s.ServletContextHandler@4d518b32{/,null,AVAILABLE}
  Started ServerConnector@1169afe1{HTTP/1.1, (http/1.1)}{0.0.0.0:8080}
  Started @1353ms
  Connector:main: jetty: Started ServerConnector@{HTTP/1.1,[http/1.1]}{http://localhost:8080}
  Server:main: jetty: Started (jetty 9.4/3.4.1) @284ms
  App: End loading elapsed=1142ms pid=2868 v=3.4.1

项目初始结构​:

  solon-demo/
  ├── src/
  │   ├── main/
  │   │   ├── java/
  │   │   │   └── com/
  │   │   │       └── example/
  │   │   │           └── demo/
  │   │   │               └── App.java    # 主类
  │   │   └── resources/
  │   │       ├── app.yml       # 主配置文件
  │   └── test/
  │       └── java/
  │           └── features/
  │               └── HelloTest.java                    # 测试代码
  ├── target/
  └── pom.xml

基础配置​:
app.yml中添加基本配置:

  server.port: 8080
  
  solon.app:
    name: 'demo-app'
    group: 'demo'
  
  solon.logging:
    appender:
      console:
        level: INFO
      file:
        level: INFO

至此,我们已经完成了一个最基本的Solon Web项目搭建,接下来将逐步添加各功能模块。

REST API开发

上一步我们已经为项目添加Web支持,构建符合现代开发实践的RESTful API。Solon的Web模块设计简洁而强大,支持同步和异步处理模式。

新增第一个Controller​:
在上面的DemoController.java新增json请求方法:

  @Get
  @Mapping("/json")
  public Map<String, Object> jsonExample() {
      return Map.of(
          "timestamp", System.currentTimeMillis(),
          "message", "This is a JSON response",
          "data", List.of(1, 2, 3)
      );
  }

验证API​:
启动应用后,访问以下端点:

  • http://localhost:8080/json → 返回JSON响应

增强配置​:
app.yml中添加Web相关配置:

server:
  port: 8080
  contextPath: "/api"  
  # 连接器配置
  connector:
    maxHttpPostSize: 2MB    # 最大POST大小
    idleTimeout: 30s        # 空闲超时
    requestTimeout: 10s      # 请求超时
    
  # 响应压缩
  compression:
    enable: true
    mime-types: "text/html,text/plain,text/xml,text/css,text/javascript,application/javascript,application/json"
    min-size: 512
    
  # CORS配置
  cors:
    enable: true
    allowed-origins: "*"
    allowed-methods: "GET,POST,PUT,DELETE,OPTIONS"
    allowed-headers: "Content-Type,Authorization"
    max-age: 3600

数据访问层集成

现代应用离不开数据持久化,现在来试试如何在Solon中集成MyBatis-Plus作为ORM框架,并实现事务管理、缓存等企业级特性。

添加数据访问依赖

修改pom.xml:

  <dependencies>
      <!-- 数据访问核心 -->
      <dependency>
          <groupId>org.noear</groupId>
          <artifactId>solon-data</artifactId>
      </dependency>
      
      <!-- MyBatis-Plus集成 -->
      <dependency>
          <groupId>org.noear</groupId>
          <artifactId>solon-data-mybatis-plus</artifactId>
      </dependency>
      
      <!-- 数据库驱动 (以Mysql为例) -->
      <dependency>
        <groupId>com.mysql</groupId>
        <artifactId>mysql-connector-j</artifactId>
        <version>9.2.0</version>
        <scope>runtime</scope>
      </dependency>
        
      <!-- 连接池 -->
      <dependency>
        <groupId>com.zaxxer</groupId>
        <artifactId>HikariCP</artifactId>
      </dependency>

  </dependencies>

数据库配置

app.yml中添加:

  solon:
    dataSources:
      db1:
        class: "com.zaxxer.hikari.HikariDataSource"
        jdbcUrl: "jdbc:mysql://127.0.0.1:3366/demo?useUnicode=true&characterEncoding=utf-8"
        driverClassName: "com.mysql.cj.jdbc.Driver"
        username: root
        password: root
  #  db2:  可设置多个数据源

  mybatis:
    db1:
      typeAliases:    #支持包名 或 类名 //支持 ** 和 *
        - "com.cxl.entity"
      mappers:        #支持包名 或 类名 或 xml(.xml结尾)//支持 ** 和 *
        - "com.cxl.mapper"
        - "classpath:mapper/*.xml"
  #  db2:  对应数据源配置

初始化数据库,这里就沿用之前Micronaut文章中的数据表Person吧。

创建实体类

Person.java

  package com.cxl.entity;

  import com.baomidou.mybatisplus.annotation.IdType;
  import com.baomidou.mybatisplus.annotation.TableId;
  import com.baomidou.mybatisplus.annotation.TableName;
  import lombok.Data;

  @Data
  @TableName("person")
  public class Person {
  
      @TableId(value = "id", type = IdType.AUTO)
      private Long id;
      private String name;
      private String birth;
      private String status;
  }

创建Mapper接口

PersonMapper.java

  package com.cxl.mapper;
  
  import com.baomidou.mybatisplus.core.mapper.BaseMapper;
  import com.cxl.entity.Person;
  import org.apache.ibatis.annotations.Mapper;
  
  @Mapper
  public interface PersonMapper extends BaseMapper<Person> {
  }

创建Service层

IPersonService.java接口与实现类PersonService.java

  package com.cxl.service;
  
  import com.baomidou.mybatisplus.extension.service.IService;
  import com.cxl.entity.Person;
  
  public interface IPersonService extends IService<Person> {
  }

  package com.cxl.service.impl;

  import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
  import com.cxl.entity.Person;
  import com.cxl.mapper.PersonMapper;
  import com.cxl.service.IPersonService;
  import org.noear.solon.annotation.Component;
  
  @Component
  public class PersonService extends ServiceImpl<PersonMapper, Person> implements IPersonService {
  }
  

创建Controller

PersonApi.java

  package com.cxl.api;
  
  import com.cxl.entity.Person;
  import com.cxl.service.IPersonService;
  import org.noear.solon.annotation.*;
  import java.util.List;
  
  @Controller
  @Mapping("/person")
  public class PersonApi {
  
      @Inject
      private IPersonService personService;
  
      @Get
      @Mapping
      public List<Person> list() {
          return personService.list();
      }
  
      @Get
      @Mapping("/{id}")
      public Person getById(@Param long id) {
          return personService.getById(id);
      }
  
      @Post
      @Mapping
      public long save(@Body Person person) {
          personService.saveOrUpdate(person);
          return person.getId();
      }
  }
  

验证数据访问

  1. 启动应用,检查数据库初始化日志

  2. 测试API端点:

    • GET /person/1 → 获取ID为1的用户

    • POST /person → 创建新用户

    • GET /person → 获取用户列表

注意

可能出现的异常:

  1. Missing mapper registration, please check the mappers configuration. 需检查配置文件中是否缺少mybatis下db的mappers选项

  2. org.noear.solon.core.exception.InjectionException: Field injection failed: 'baseMapper'. 注入失败,检查数据源配置,可参考上面配置

数据​缓存支持

  1. 添加依赖:上一步已经添加了 solon-data 依赖,其中已经包含对应缓存注解,这里直接使用本地内存,也无须添加其它类似redis依赖,如 solon-cache-jedis

  2. 配置缓存类型:

      # app.yml 文件
      demo.cache1:
        driverType: "local"
      # 同时打开mybatis日志输出sql,用来验证是否使用了缓存
      mybatis:
        db1:
          configuration:
            logImpl: org.apache.ibatis.logging.stdout.StdOutImpl
  3. 配置缓存类 Demo1Config.java

      @Configuration
      public class Demo1Config {
        @Bean
        public CacheService cahce1(@Inject("${demo.cache1}") CacheServiceSupplier supplier) {
            return supplier.get();
        }
      }
  4. 在controller中使用@Cache缓存:

      @Get
      @Cache
      @Mapping
      public List<Person> list() {
        return personService.list();
      }
  5. 访问接口验证 localhost:8080/person,第一次访问控制台会打印出完整sql,后面再访问则不会打印,说明已经缓存成功

动态模板

我们一开始创建项目时就引入了 solon-view-freemarker 模板引擎依赖,现在我们便从Person类着手创建对应的列表视图吧!

solon项目约定了 resources/templates 目录为视图模板文件根目录、resources/static目录为静态文件根目录,我们首先在templates目录(没有则手动创建)下新建一个person.ftl文件:

  <#-- person-list.ftl -->
  <!DOCTYPE html>
  <html>
  <head>
      <title>人员列表</title>
      <meta charset="UTF-8">
      <style>
          table {
              border-collapse: collapse;
              width: 100%;
          }
          th, td {
              border: 1px solid #ddd;
              padding: 8px;
              text-align: left;
          }
          th {
              background-color: #f2f2f2;
          }
      </style>
  </head>
  <body>
      <h1>人员列表</h1>
      <table>
          <thead>
              <tr>
                  <th>ID</th>
                  <th>姓名</th>
                  <th>出生日期</th>
                  <th>状态</th>
              </tr>
          </thead>
          <tbody>
              <#list personList as person>
                  <tr>
                      <td>${person.id!}</td>
                      <td>${person.name!}</td>
                      <td>${person.birth!}</td>
                      <td>${person.status!}</td>
                  </tr>
              </#list>
          </tbody>
      </table>
  </body>
  </html>

如果不懂freemarker模板语法的话可以查看相关文档,我这里是直接通过通义灵码AI生成的

接着创建Controller请求数据后渲染模板: PersonController.java

  @Controller
  @Mapping("view")
  public class PersonController {
      @Inject
      private IPersonService personService;
  
      @Mapping("person")
      public Object personView(){
          ModelAndView vm = new ModelAndView("person.ftl"); //注意,带后缀
          vm.put("personList",personService.list());
          return vm;
      }
  }

最终效果:

通过这个小小的实践项目,我们粗略地体验了Solon框架的开发流程和核心功能。整个项目一步步操作下来,个人感觉上手还是挺容易的,主要还是因为文档比较全,而且还有相应的示例代码,是真正的Open,是我在国产框架里看到最有希望的一款了。Solon以其独特的轻量级设计和强大的扩展能力,为Java应用开发提供了全新的选择,特别适合追求性能和开发效率的现代应用场景。无论是初创项目还是企业级应用,Solon都值得考虑作为基础框架。


Comment