开始使用
Feat Cloud 提供了一种面向企业级应用开发的解决方案,它的定位有点像 SpringBoot。
设计方面:Feat Cloud 尽最大可能保留了 SpringBoot 的使用习惯,以此降低开发者的学习成本。
技术方面:Feat Cloud 在编译期对代码进行静态分析,最大化提升服务运行性能,降低资源消耗。
引入 Maven 依赖
Section titled “引入 Maven 依赖”创建一个 Maven 项目,在 pom.xml
文件中添加以下依赖:
<dependency> <groupId>tech.smartboot.feat</groupId> <artifactId>feat-cloud-starter</artifactId> <version>${feat.version}</version></dependency>
配置 IDEA
Section titled “配置 IDEA”调整 IDEA 编译配置,以启用 Feat Cloud 的静态优化功能,否则请求将无法正常路由。
设置路径为:Preferences -> Build, Execution, Deployment -> Build Tools -> Maven -> Runner
,
勾选Delegate IDE build/run actions to Maven
并点击 OK
保存配置。

在 Maven 工程中创建一个 FeatCloudDemo.java
文件,添加以下代码:
@Controllerpublic class FeatCloudDemo { @RequestMapping("/cloud") public String helloWorld() { return "hello Feat Cloud"; }
public static void main(String[] args) { Feat.cloudServer().listen(); }}
启动程序,打开浏览器访问 http://localhost:8080/cloud
。

从上面的代码可以看出,Feat Cloud 是基于注解的方式进行服务开发的。 通常来说,框架会在运行时解析注解并生成对应的服务。
但是,Feat Cloud 却是在编译时对代码进行静态分析,生成对应的服务。 其核心原理是运用了 APT(Annotation Processing Tool)技术,并结合 ServiceLoader 实现了 0 反射的服务加载机制。
1. 静态转码
Section titled “1. 静态转码”以 FeatCloudDemo 为例,开发人员编写的源代码在编译时经过一次静态转码,生成了一个新的 java 文件 FeatCloudDemoBeanAptLoader.java
。
@Controllerpublic class FeatCloudDemo { @RequestMapping("/cloud") public String helloWorld() { return "hello Feat Cloud"; }
public static void main(String[] args) { Feat.cloudServer().listen(); }}
public class FeatCloudDemoBeanAptLoader extends AbstractServiceLoader { private FeatCloudDemo bean;
public void loadBean(ApplicationContext applicationContext) throws Throwable { bean = new FeatCloudDemo(); applicationContext.addBean("featCloudDemo", bean); }
public void autowired(ApplicationContext applicationContext) {
}
public void router(Router router) { router.route("/cloud", req -> { String rst = bean.helloWorld(); byte[] bytes = rst.getBytes("UTF-8"); req.getResponse().setContentLength(bytes.length); req.getResponse().write(bytes); }); }
public void destroy() throws Throwable { }
public void postConstruct(ApplicationContext applicationContext) throws Throwable {} }
可以从静态转码后的代码中看出,对于 bean 的实例化,是通过 new
关键字进行的,而不是通过反射。
对于路由的配置,也是通过调用 Feat Server 中的 Router 方法进行的,也不是通过反射。
因此,Feat Cloud 可以在提供优雅的开发体验的同时,也极大地保留了 Feat Server 框架的性能优势。
2. 服务载入
Section titled “2. 服务载入”Controller 在编译时完成转码后,下一步便是需要在程序启动后能够被正确加载,此处需要用到的技术便是 java.util.ServiceLoader
。
Controller 静态转码所生成的类文件默认实现了 tech.smartboot.feat.cloud.service.CloudService
接口,同时会自动生成一个 service 文件:
META-INF/services/tech.smartboot.feat.cloud.service.CloudService
当调用 ApplicationContext@start 方法时:
- 首先通过 ServiceLoader.load(CloudService.class) 加载所有实现了 CloudService 接口的类。并根据
isIgnore
规则过滤出有效的服务。 - 遍历所有服务,调用其
loadBean
方法,完成 bean 的实例化。 - 遍历所有服务,调用其
autowired
方法,完成各实例的依赖注入。 - 遍历所有服务,调用其
postConstruct
方法,完成各实例的初始化。 - 遍历所有服务,调用其
router
方法,完成 Controller 路由的配置。
public class ApplicationContext {
...
public void start() throws Throwable { for (CloudService service : ServiceLoader.load(CloudService.class)) { if (isIgnore(service)) { continue; } services.add(service); } for (CloudService service : services) { service.loadBean(this); }
for (CloudService service : services) { service.autowired(this); } for (CloudService service : services) { service.postConstruct(this); } for (CloudService service : services) { service.router(router); } }
...}
public interface CloudService { void loadBean(ApplicationContext context) throws Throwable;
void autowired(ApplicationContext context);
void postConstruct(ApplicationContext context) throws Throwable;
void destroy() throws Throwable;
void router(Router router);}
3. 启动服务
Section titled “3. 启动服务”在这个步骤中,由于 ApplicationContext@start
中已经完成了所有服务的实例化、依赖注入、初始化、路由配置等工作,因此,最后一步便是将 Router 实例设置到 HttpServer 中,启动服务。
public static HttpServer cloudServer(Consumer<CloudOptions> options) { CloudOptions opt = new CloudOptions(); options.accept(opt); opt.serverName("feat-cloud"); ApplicationContext application = new ApplicationContext(opt); opt.getExternalBeans().forEach(application::addBean); application.start();
HttpServer server = Feat.httpServer(opt); ... server.httpHandler(application.getRouter()); return server;}
4. 生命周期
Section titled “4. 生命周期”在 Feat Cloud 中对于托管的实例的生命周期管理可分为:创建 -> 属性填充 -> 初始化 -> 使用 -> 销毁 几个核心阶段。下图直观展示了 Bean 的完整生命周期流程:
Feat Cloud 通过 @Bean 和 @Controller 注解来定义实例对象。
在某个 class 上添加注解 @Bean
。Feat Cloud 启动时会自动创建该 Bean,bean 的名称默认为类名首字母小写。
@Beanpublic class BeanDemo{
}
如果需要自定义 bean 的名称,可以通过 value
属性来指定。例如:@Bean(“beanDemo2”)
@Bean("beanDemo2")public class BeanDemo{
}
@Controller 是一种特殊的 Bean,它没有 bean 名称的概念。 我们认为一个 Controller 的核心功能就是提供 HTTP 服务,而不是成一个可被外部依赖的 bean 对象。
@Controllerpublic class ControllerDemo{
}
在 Bean/Controller 的中通过 @Bean
注解定义 Bean 方法。
若未在 @Bean 中指定 bean 的名称,则默认为方法名。
@Beanpublic class BeanDemo{ @Bean public BeanDemo beanDemo2(){ return new BeanDemo(); }}
服务启动时通过addExternalBean
方法添加外部 Bean。
public class FeatCloudDemo {
public static void main(String[] args) { Feat.cloudServer(opts -> opts.addExternalBean("beanDemo", new BeanDemo())) .listen(); }}
注意:@Autowired、@PostConstruct、@PreDestroy 等注解的相关特性不在此类 Bean 中生效。
@Autowired:属性填充
Section titled “@Autowired:属性填充”该注解用于填充 Bean 或者 Controller 的属性,类似于 Spring 中的 @Autowired
。
注意:因为技术方面的原因,现阶段需要显示提供属性的 setter 方法。
@Beanpublic class FeatBeanDemo { @Autowired private String hello;
public static void main(String[] args) { Feat.cloudServer(opts -> opts.addExternalBean("hello", "你好~")).listen(); }
public void setHello(String hello) { this.hello = hello; }}
@PostConstruct:初始化
Section titled “@PostConstruct:初始化”同 Spring 中的 @PostConstruct
注解,在 Bean 完成实例化和属性填充后被调用。
@Beanpublic class FeatBeanDemo { @Autowired private String hello;
@PostConstruct public void init() { System.out.println(hello); }
public static void main(String[] args) { Feat.cloudServer(opts -> opts.addExternalBean("hello", "你好~")).listen(); }
public void setHello(String hello) { this.hello = hello; }}
使用 Bean
Section titled “使用 Bean”在 Feat Cloud 中,可以通过 @Autowired
注解来注入和使用其他 Bean。Bean 之间可以相互依赖,形成依赖关系图。
@Beanpublic class BeanDemo { @Autowired private OtherBean otherBean;
public void useOtherBean() { // 使用注入的 otherBean otherBean.doSomething(); }
public void setOtherBean(OtherBean otherBean) { this.otherBean = otherBean; }}
@Beanpublic class OtherBean { public void doSomething() { System.out.println("OtherBean is doing something"); }}
Feat Cloud 支持 Bean 之间的循环依赖。当两个或多个 Bean 相互依赖时,Feat Cloud 能够正确处理这种情况,确保所有 Bean 都能正确初始化。
@Beanpublic class BeanA { @Autowired private BeanB beanB;
public void setBeaB(BeanB beanB) { this.beanB = beanB; }}
@Beanpublic class BeanB { @Autowired private BeanA beanA;
public void setBeanA(BeanA beanA) { this.beanA = beanA; }}
Bean 的作用域
Section titled “Bean 的作用域”目前 Feat Cloud 中的 Bean 默认都是单例的,即在整个应用中只有一个实例。这意味着无论在哪里注入这个 Bean,获取到的都是同一个实例。
@PreDestroy:销毁
Section titled “@PreDestroy:销毁”@PreDestroy 作用于 Bean 或者 Controller 内部 public 方法上,类似于 Spring 中的 @PreDestroy
。当应用关闭时,Feat Cloud 会调用标记了 @PreDestroy 注解的方法,以便 Bean 可以释放资源或执行其他清理操作。
@Beanpublic class ResourceBean { private Resource resource;
@PostConstruct public void init() { // 初始化资源 resource = new Resource(); System.out.println("资源已初始化"); }
@PreDestroy public void destroy() { // 释放资源 if (resource != null) { resource.close(); System.out.println("资源已释放"); } }
// 资源类示例 private class Resource { public void close() { // 关闭资源的逻辑 } }}
当以下情况发生时,@PreDestroy 注解的方法会被调用:
- 应用正常关闭时(如调用
System.exit()
) - 通过 JVM 的 shutdown hook 关闭应用时
- 在 Feat Cloud 中,当调用
server.shutdown()
方法时