Apache Felix 快速入门与集成示例

Apache Felix 快速入门与集成示例:从环境准备到首个 OSGi bundle 的编写、MANIFEST 要点与部署,并演示以嵌入式 Felix(FrameworkFactory API)启动框架的代码与输出。

Apache FelixApache Felix2026/10/12

Apache Felix 快速入门与集成示例

本文以 Apache Felix Framework 7.0.5 为例,先跑通「启动框架 → 观察 bundle」的最小场景,再编写并部署第一个 OSGi bundle,最后演示在自有 Java 应用中嵌入 Felix 的两种方式。所有回显均来自真实运行。

一、快速入门

1.1 环境准备

项目要求
JDKJDK 11 及以上(示例 bundle 声明 osgi.ee=JavaSE 11)
FelixApache Felix Framework 7.0.5 发行包
Maven3.5+(用于构建示例 bundle)
运行账号root

安装 JDK(以 Ubuntu 22.04 为例):

apt-get update
apt-get install -y openjdk-17-jdk unzip
java -version
openjdk version "17.0.20.1" 2026-08-18

下载并解压 Felix:

mkdir -p /opt/felix
cd /tmp
curl -sL -o felix.zip \
  https://archive.apache.org/dist/felix/org.apache.felix.main.distribution-7.0.5.zip
unzip -q felix.zip -d /opt/felix
ls /opt/felix
felix-framework-7.0.5

1.2 首例:启动框架

cd /opt/felix/felix-framework-7.0.5
java -Xms256m -Xmx512m -jar bin/felix.jar

进入 Gogo shell 后执行 lb:

Welcome to Apache Felix Gogo

g! lb
START LEVEL 1
   ID|State      |Level|Name
    0|Active     |    0|System Bundle (7.0.5)|7.0.5
    1|Active     |    1|jansi (1.18.0)|1.18.0
    2|Active     |    1|JLine Bundle (3.13.2)|3.13.2
    3|Active     |    1|Apache Felix Bundle Repository (2.0.10)|2.0.10
    4|Active     |    1|Apache Felix Gogo Command (1.1.2)|1.1.2
    5|Active     |    1|Apache Felix Gogo JLine Shell (1.1.8)|1.1.8
    6|Active     |    1|Apache Felix Gogo Runtime (1.1.4)|1.1.4

1.3 解读

  • ID 0 是 System Bundle,代表框架自身,版本 7.0.5。
  • ID 1–6 是发行包 bundle/ 目录下 Gogo 技术支持相关的 bundle,由 felix.auto.deploy.action=install,start 默认自动安装并启动。
  • State 一列显示 bundle 运行状态,Active 表示已启动。
  • 该启动方式即「独立发行包」形态,shell 中即可完成安装与调试。

二、编写你的第一个 OSGi bundle

2.1 工程结构

hello-osgi/
├── pom.xml
└── src/main/java/com/example/hello/Activator.java

2.2 pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.example</groupId>
  <artifactId>hello-osgi</artifactId>
  <version>1.0.0</version>
  <packaging>bundle</packaging>

  <properties>
    <maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>11</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.osgi</groupId>
      <artifactId>org.osgi.core</artifactId>
      <version>6.0.0</version>
      <scope>provided</scope>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.felix</groupId>
        <artifactId>maven-bundle-plugin</artifactId>
        <version>5.1.9</version>
        <extensions>true</extensions>
        <configuration>
          <instructions>
            <Bundle-SymbolicName>com.example.hello</Bundle-SymbolicName>
            <Bundle-Version>1.0.0</Bundle-Version>
            <Bundle-Activator>com.example.hello.Activator</Bundle-Activator>
            <Export-Package>com.example.hello</Export-Package>
          </instructions>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

<packaging>bundle</packaging> 配合 maven-bundle-plugin 才会生成 OSGi 响应头。

2.3 Activator 实现

package com.example.hello;

import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;

public class Activator implements BundleActivator {

    public void start(BundleContext ctx) {
        System.out.println("[hello-osgi] Bundle started: " + ctx.getBundle().getSymbolicName());
    }

    public void stop(BundleContext ctx) {
        System.out.println("[hello-osgi] Bundle stopped: " + ctx.getBundle().getSymbolicName());
    }
}

2.4 MANIFEST 要点解读

构建后产物 target/hello-osgi-1.0.0.jar 的响应头关键项:

Bundle-ManifestVersion: 2
Bundle-SymbolicName: com.example.hello
Bundle-Version: 1.0.0
Bundle-Activator: com.example.hello.Activator
Export-Package: com.example.hello;uses:="org.osgi.framework";version="1.0.0"
Import-Package: org.osgi.framework;version="[1.8,2)"
Require-Capability: osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=11))"
  • Bundle-SymbolicName + Bundle-Version 是 bundle 的唯一标识。
  • Bundle-Activator 指定框架在 start/stop 时回调的类。
  • Import-Package 声明对 OSGi 核心包的依赖,由 Felix 在解析时从 System Bundle 满足。
  • Require-Capability 中的 osgi.ee=JavaSE 11 要求运行环境至少 JDK 11。

2.5 构建

cd /opt/felix-bundle
mvn -q -B package
ls target/hello-osgi-1.0.0.jar
target/hello-osgi-1.0.0.jar

三、集成示例一:部署到 Felix

在运行中的 Felix Gogo shell 里安装并启动:

g! install file:/opt/felix-bundle/target/hello-osgi-1.0.0.jar
Bundle ID: 7
g! start 7
[hello-osgi] Bundle started: com.example.hello
g! lb
    7|Active     |    1|hello-osgi (1.0.0)|1.0.0

启动后状态为 Active,Activator#start 输出日志。停止与卸载:

g! stop 7
[hello-osgi] Bundle stopped: com.example.hello
g! uninstall 7
g!

若希望框架启动时自动装载,可在 conf/config.properties 中加入:

felix.auto.start.1=file:/opt/felix-bundle/target/hello-osgi-1.0.0.jar

四、集成示例二:嵌入式 Felix(FrameworkFactory API)

4.1 依赖

嵌入式启动只需把发行包的 bin/felix.jar 加入 classpath,无需额外依赖。

4.2 示例代码

EmbedFelix.java:

import java.util.HashMap;
import java.util.Map;
import java.util.ServiceLoader;

import org.osgi.framework.Bundle;
import org.osgi.framework.FrameworkFactory;
import org.osgi.framework.launch.Framework;

public class EmbedFelix {
    public static void main(String[] args) throws Exception {
        FrameworkFactory factory = ServiceLoader.load(FrameworkFactory.class).iterator().next();

        Map<String, String> config = new HashMap<String, String>();
        config.put("org.osgi.framework.storage", "/tmp/embedded-felix-cache");
        config.put("felix.log.level", "1");

        Framework framework = factory.newFramework(config);
        framework.start();
        System.out.println("[embed] framework state = " + framework.getState());

        Bundle bundle = framework.getBundleContext()
                .installBundle("file:/opt/felix-bundle/target/hello-osgi-1.0.0.jar");
        bundle.start();
        System.out.println("[embed] bundle " + bundle.getSymbolicName()
                + " state = " + bundle.getState());

        bundle.stop();
        framework.stop();
        framework.waitForStop(0);
        System.out.println("[embed] framework stopped");
    }
}

FrameworkFactory 由 felix.jar 内的 META-INF/services 注册,通过 ServiceLoader 发现。

4.3 编译与运行

cd /root/embed
javac -cp /opt/felix/felix-framework-7.0.5/bin/felix.jar EmbedFelix.java
java -cp .:/opt/felix/felix-framework-7.0.5/bin/felix.jar EmbedFelix

真实输出:

[embed] framework state = 32
[hello-osgi] Bundle started: com.example.hello
[embed] bundle com.example.hello state = 32
[hello-osgi] Bundle stopped: com.example.hello
[embed] framework stopped

state = 32 即 OSGi Bundle.ACTIVE 常量值(0x00000020)。

五、集成示例三:直接实例化 Felix 类

除 FrameworkFactory 外,也可直接使用框架实现类 org.apache.felix.framework.Felix,它实现了 Framework 接口:

import java.util.HashMap;
import java.util.Map;
import org.apache.felix.framework.Felix;
import org.osgi.framework.launch.Framework;

Map<String, String> config = new HashMap<String, String>();
config.put("org.osgi.framework.storage", "/tmp/embedded-felix-cache");

Framework framework = new Felix(config);
framework.start();
// ... 使用 framework.getBundleContext() 安装/启动 bundle
framework.stop();
framework.waitForStop(0);

该写法与 FrameworkFactory 方式行为一致,区别仅在于直接依赖 Felix 实现类,牺牲了可移植性(FrameworkFactory 是 OSGi 标准发现方式,便于切换实现)。

六、常见问题

Q1:mvn package 生成的 jar 缺 OSGi 响应头?

检查 <packaging>bundle</packaging> 是否设置、maven-bundle-plugin 是否配置 <extensions>true</extensions>。

Q2:bundle.start() 抛 BundleException?

多为 osgi.ee 或包导入未满足。用 inspect req osgi.wiring.package <id> 定位缺失依赖,确认 JDK 版本满足 Require-Capability。

Q3:嵌入式启动后缓存目录在哪?

由配置 Map 中的 org.osgi.framework.storage 决定,示例为 /tmp/embedded-felix-cache。

Q4:如何限制嵌入式框架内存?

在启动 JVM 时指定 -Xms256m -Xmx512m,框架本身不管理堆大小。

Q5:ServiceLoader.load(FrameworkFactory.class) 取不到工厂?

确认运行 classpath 中包含发行包的 bin/felix.jar。