Skip to content

开始使用

This content is not available in your language yet.

Feat Cloud 提供了一种面向企业级应用开发的解决方案,它的定位有点像 SpringBoot。

设计方面:Feat Cloud 尽最大可能保留了 SpringBoot 的使用习惯,以此降低开发者的学习成本。

技术方面:Feat Cloud 在编译期对代码进行静态分析,最大化提升服务运行性能,降低资源消耗。

创建一个 Maven 项目,在 pom.xml 文件中添加以下依赖:

pom.xml
<dependency>
<groupId>tech.smartboot.feat</groupId>
<artifactId>feat-cloud-starter</artifactId>
<version>${feat.version}</version>
</dependency>

调整 IDEA 编译配置,以启用 Feat Cloud 的静态优化功能,否则请求将无法正常路由

设置路径为:Preferences -> Build, Execution, Deployment -> Build Tools -> Maven -> Runner, 勾选Delegate IDE build/run actions to Maven 并点击 OK 保存配置。

hello world

在 Maven 工程中创建一个 FeatCloudDemo.java 文件,添加以下代码:

FeatCloudDemo.java
@Controller
public class FeatCloudDemo {
@RequestMapping("/cloud")
public String helloWorld() {
return "hello Feat Cloud";
}
public static void main(String[] args) {
Feat.cloudServer().listen();
}
}

启动程序,打开浏览器访问 http://localhost:8080/cloud

hello world

从上面的代码可以看出,Feat Cloud 是基于注解的方式进行服务开发的。 通常来说,框架会在运行时解析注解并生成对应的服务。

但是,Feat Cloud 却是在编译时对代码进行静态分析,生成对应的服务。 其核心原理是运用了 APT(Annotation Processing Tool)技术,并结合 ServiceLoader 实现了 0 反射的服务加载机制

以 FeatCloudDemo 为例,开发人员编写的源代码在编译时经过一次静态转码,生成了一个新的 java 文件 FeatCloudDemoBeanAptLoader.java

FeatCloudDemo.java
@Controller
public class FeatCloudDemo {
@RequestMapping("/cloud")
public String helloWorld() {
return "hello Feat Cloud";
}
public static void main(String[] args) {
Feat.cloudServer().listen();
}
}

可以从静态转码后的代码中看出,对于 bean 的实例化,是通过 new 关键字进行的,而不是通过反射。

对于路由的配置,也是通过调用 Feat Server 中的 Router 方法进行的,也不是通过反射。

因此,Feat Cloud 可以在提供优雅的开发体验的同时,也极大地保留了 Feat Server 框架的性能优势。


Controller 在编译时完成转码后,下一步便是需要在程序启动后能够被正确加载,此处需要用到的技术便是 java.util.ServiceLoader

Controller 静态转码所生成的类文件默认实现了 tech.smartboot.feat.cloud.service.CloudService 接口,同时会自动生成一个 service 文件:

META-INF/services/tech.smartboot.feat.cloud.service.CloudService

当调用 ApplicationContext@start 方法时:

  1. 首先通过 ServiceLoader.load(CloudService.class) 加载所有实现了 CloudService 接口的类。并根据 isIgnore 规则过滤出有效的服务。
  2. 遍历所有服务,调用其 loadBean 方法,完成 bean 的实例化。
  3. 遍历所有服务,调用其 autowired 方法,完成各实例的依赖注入。
  4. 遍历所有服务,调用其 postConstruct 方法,完成各实例的初始化。
  5. 遍历所有服务,调用其 router 方法,完成 Controller 路由的配置。
ApplicationContext.java
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);
}
}
...
}

在这个步骤中,由于 ApplicationContext@start 中已经完成了所有服务的实例化、依赖注入、初始化、路由配置等工作,因此,最后一步便是将 Router 实例设置到 HttpServer 中,启动服务。

Feat.java
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;
}

在 Feat Cloud 中对于托管的实例的生命周期管理可分为:创建 -> 属性填充 -> 初始化 -> 使用 -> 销毁 几个核心阶段。下图直观展示了 Bean 的完整生命周期流程:

Feat Cloud 生命周期

Feat Cloud 通过 @Bean@Controller 注解来定义实例对象。

在某个 class 上添加注解 @Bean。Feat Cloud 启动时会自动创建该 Bean,bean 的名称默认为类名首字母小写。

@Bean
public class BeanDemo{
}

如果需要自定义 bean 的名称,可以通过 value 属性来指定。例如:@Bean(“beanDemo2”)

@Bean("beanDemo2")
public class BeanDemo{
}

@Controller 是一种特殊的 Bean,它没有 bean 名称的概念。 我们认为一个 Controller 的核心功能就是提供 HTTP 服务,而不是成一个可被外部依赖的 bean 对象。

@Controller
public class ControllerDemo{
}

在 Bean/Controller 的中通过 @Bean 注解定义 Bean 方法。 若未在 @Bean 中指定 bean 的名称,则默认为方法名。

@Bean
public 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 中生效。

该注解用于填充 Bean 或者 Controller 的属性,类似于 Spring 中的 @Autowired

注意:因为技术方面的原因,现阶段需要显示提供属性的 setter 方法。

@Bean
public 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;
}
}

同 Spring 中的 @PostConstruct 注解,在 Bean 完成实例化和属性填充后被调用。

@Bean
public 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;
}
}

在 Feat Cloud 中,可以通过 @Autowired 注解来注入和使用其他 Bean。Bean 之间可以相互依赖,形成依赖关系图。

@Bean
public class BeanDemo {
@Autowired
private OtherBean otherBean;
public void useOtherBean() {
// 使用注入的 otherBean
otherBean.doSomething();
}
public void setOtherBean(OtherBean otherBean) {
this.otherBean = otherBean;
}
}
@Bean
public class OtherBean {
public void doSomething() {
System.out.println("OtherBean is doing something");
}
}

Feat Cloud 支持 Bean 之间的循环依赖。当两个或多个 Bean 相互依赖时,Feat Cloud 能够正确处理这种情况,确保所有 Bean 都能正确初始化。

@Bean
public class BeanA {
@Autowired
private BeanB beanB;
public void setBeaB(BeanB beanB) {
this.beanB = beanB;
}
}
@Bean
public class BeanB {
@Autowired
private BeanA beanA;
public void setBeanA(BeanA beanA) {
this.beanA = beanA;
}
}

目前 Feat Cloud 中的 Bean 默认都是单例的,即在整个应用中只有一个实例。这意味着无论在哪里注入这个 Bean,获取到的都是同一个实例。

@PreDestroy 作用于 Bean 或者 Controller 内部 public 方法上,类似于 Spring 中的 @PreDestroy。当应用关闭时,Feat Cloud 会调用标记了 @PreDestroy 注解的方法,以便 Bean 可以释放资源或执行其他清理操作。

@Bean
public 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 注解的方法会被调用:

  1. 应用正常关闭时(如调用 System.exit()
  2. 通过 JVM 的 shutdown hook 关闭应用时
  3. 在 Feat Cloud 中,当调用 server.shutdown() 方法时