第03章 - 核心架构与模块设计

第03章 - 核心架构与模块设计

3.1 GeoTools 整体架构

3.1.1 分层架构设计

GeoTools 采用分层架构设计,从底层到高层依次为:

┌─────────────────────────────────────────────────────────────────────┐
│                           应用层                                     │
│    ┌─────────────┐  ┌─────────────┐  ┌─────────────┐               │
│    │  GeoServer  │  │    uDig     │  │  自定义应用  │               │
│    └─────────────┘  └─────────────┘  └─────────────┘               │
├─────────────────────────────────────────────────────────────────────┤
│                          服务层                                      │
│    ┌─────────────┐  ┌─────────────┐  ┌─────────────┐               │
│    │    WMS      │  │    WFS      │  │    WCS      │               │
│    │ gt-wms      │  │ gt-wfs-ng   │  │ gt-wcs      │               │
│    └─────────────┘  └─────────────┘  └─────────────┘               │
├─────────────────────────────────────────────────────────────────────┤
│                          表现层                                      │
│    ┌─────────────────────────────────────────────────────────┐     │
│    │                 渲染引擎 (gt-render)                      │     │
│    │    样式系统  │  符号化  │  标注  │  地图输出               │     │
│    └─────────────────────────────────────────────────────────┘     │
├─────────────────────────────────────────────────────────────────────┤
│                          数据层                                      │
│    ┌─────────────────────────────────────────────────────────┐     │
│    │              DataStore API (gt-main)                      │     │
│    ├───────────┬───────────┬───────────┬───────────┬─────────┤     │
│    │ Shapefile │ GeoJSON   │ PostGIS   │ GeoPackage│  WFS    │     │
│    └───────────┴───────────┴───────────┴───────────┴─────────┘     │
├─────────────────────────────────────────────────────────────────────┤
│                          模型层                                      │
│    ┌───────────────────┐  ┌───────────────────┐                    │
│    │   Feature Model   │  │    CRS/Transform   │                    │
│    │   要素模型         │  │    坐标系统        │                    │
│    └─────────┬─────────┘  └─────────┬─────────┘                    │
│              │                      │                               │
│    ┌─────────▼─────────────────────▼─────────┐                     │
│    │              JTS Geometry               │                     │
│    │              几何引擎                    │                     │
│    └─────────────────────────────────────────┘                     │
└─────────────────────────────────────────────────────────────────────┘

3.1.2 模块分类

GeoTools 的模块按功能分为以下几类:

核心库 (library)

模块 说明
gt-main 核心 API,Feature、DataStore 等基础接口
gt-api GeoTools API 定义
gt-metadata 元数据支持
gt-referencing 坐标参考系统
gt-coverage 栅格数据支持
gt-render 渲染引擎
gt-jdbc JDBC 数据访问基础

插件 (plugin)

模块 说明
gt-shapefile Shapefile 格式支持
gt-geojson GeoJSON 格式支持
gt-geopkg GeoPackage 格式支持
gt-geotiff GeoTIFF 格式支持
gt-jdbc-postgis PostGIS 数据库支持
gt-jdbc-h2gis H2GIS 数据库支持

扩展 (extension)

模块 说明
gt-wms WMS 客户端
gt-wfs-ng WFS 客户端
gt-wps WPS 客户端
gt-graph 图论/网络分析
gt-brewer ColorBrewer 配色方案
gt-validation 数据验证

3.1.3 依赖关系

                            gt-api
                              │
                              ▼
                           gt-main
                    ┌─────────┼─────────┐
                    │         │         │
                    ▼         ▼         ▼
             gt-referencing  gt-render  gt-coverage
                    │         │         │
                    ▼         │         │
              gt-epsg-hsql    │         │
                              │         │
                    ┌─────────┼─────────┘
                    │         │
                    ▼         ▼
              gt-shapefile   gt-geotiff
                    │
                    ▼
                 gt-jdbc
                    │
         ┌─────────┼─────────┐
         │         │         │
         ▼         ▼         ▼
    gt-jdbc-   gt-jdbc-   gt-jdbc-
    postgis    h2gis      oracle

3.2 核心接口设计

3.2.1 DataStore API

DataStore 是 GeoTools 数据访问的核心接口,提供统一的数据源访问方式。

/**
 * DataStore 接口层次结构
 */
public interface DataStore extends DataAccess<SimpleFeatureType, SimpleFeature> {
    
    // 获取类型名称列表
    String[] getTypeNames() throws IOException;
    
    // 获取要素类型
    SimpleFeatureType getSchema(String typeName) throws IOException;
    
    // 获取要素源(只读)
    SimpleFeatureSource getFeatureSource(String typeName) throws IOException;
    
    // 获取要素写入器
    FeatureWriter<SimpleFeatureType, SimpleFeature> getFeatureWriter(
        String typeName, Transaction transaction) throws IOException;
    
    // 创建要素类型
    void createSchema(SimpleFeatureType featureType) throws IOException;
    
    // 更新要素类型
    void updateSchema(String typeName, SimpleFeatureType featureType) throws IOException;
    
    // 删除要素类型
    void removeSchema(String typeName) throws IOException;
    
    // 释放资源
    void dispose();
}

DataStore 实现类

DataStore
    │
    ├── FileDataStore               # 文件数据源
    │   ├── ShapefileDataStore      # Shapefile
    │   ├── GeoJSONDataStore        # GeoJSON
    │   └── GeoPackageDataStore     # GeoPackage
    │
    ├── JDBCDataStore               # 数据库数据源
    │   ├── PostGISDataStore        # PostGIS
    │   ├── OracleDataStore         # Oracle Spatial
    │   └── H2GISDataStore          # H2GIS
    │
    └── ContentDataStore            # 通用内容数据源
        └── MemoryDataStore         # 内存数据源

使用示例

// 1. 文件数据源
File shapefile = new File("data/countries.shp");
FileDataStore store = FileDataStoreFinder.getDataStore(shapefile);

// 2. 数据库数据源
Map<String, Object> params = new HashMap<>();
params.put("dbtype", "postgis");
params.put("host", "localhost");
params.put("port", 5432);
params.put("database", "gisdb");
params.put("user", "postgres");
params.put("passwd", "password");
DataStore pgStore = DataStoreFinder.getDataStore(params);

// 3. 内存数据源
MemoryDataStore memStore = new MemoryDataStore();

3.2.2 FeatureSource / FeatureStore

FeatureSource 和 FeatureStore 是访问要素数据的核心接口:

/**
 * FeatureSource - 只读要素访问
 */
public interface SimpleFeatureSource {
    
    // 获取要素类型
    SimpleFeatureType getSchema();
    
    // 获取所有要素
    SimpleFeatureCollection getFeatures() throws IOException;
    
    // 按过滤条件获取要素
    SimpleFeatureCollection getFeatures(Filter filter) throws IOException;
    
    // 按查询条件获取要素
    SimpleFeatureCollection getFeatures(Query query) throws IOException;
    
    // 获取要素数量
    int getCount(Query query) throws IOException;
    
    // 获取边界范围
    ReferencedEnvelope getBounds() throws IOException;
    
    // 获取边界范围(按查询)
    ReferencedEnvelope getBounds(Query query) throws IOException;
    
    // 获取数据源
    DataStore getDataStore();
}

/**
 * FeatureStore - 可写要素访问(继承 FeatureSource)
 */
public interface SimpleFeatureStore extends SimpleFeatureSource {
    
    // 添加要素
    List<FeatureId> addFeatures(FeatureCollection<SimpleFeatureType, SimpleFeature> features)
        throws IOException;
    
    // 删除要素
    void removeFeatures(Filter filter) throws IOException;
    
    // 修改要素属性
    void modifyFeatures(Name[] attributeNames, Object[] attributeValues, Filter filter)
        throws IOException;
    
    // 设置事务
    void setTransaction(Transaction transaction);
    
    // 获取事务
    Transaction getTransaction();
}

使用示例

// 获取 FeatureSource
SimpleFeatureSource source = dataStore.getFeatureSource("countries");

// 检查是否支持写入
if (source instanceof SimpleFeatureStore) {
    SimpleFeatureStore featureStore = (SimpleFeatureStore) source;
    
    // 开启事务
    Transaction transaction = new DefaultTransaction("create");
    featureStore.setTransaction(transaction);
    
    try {
        // 添加要素
        featureStore.addFeatures(featureCollection);
        transaction.commit();
    } catch (Exception e) {
        transaction.rollback();
    } finally {
        transaction.close();
    }
}

3.2.3 Filter API

Filter API 用于构建查询过滤条件:

/**
 * Filter 接口层次
 */
Filter
    │
    ├── ComparisonOperator          # 比较操作符
    │   ├── PropertyIsEqualTo       # 等于
    │   ├── PropertyIsNotEqualTo    # 不等于
    │   ├── PropertyIsLessThan      # 小于
    │   ├── PropertyIsGreaterThan   # 大于
    │   ├── PropertyIsLike          # 模糊匹配
    │   └── PropertyIsBetween       # 区间
    │
    ├── SpatialOperator             # 空间操作符
    │   ├── Intersects              # 相交
    │   ├── Contains                # 包含
    │   ├── Within                  # 在内部
    │   ├── Touches                 # 相接
    │   ├── Crosses                 # 穿越
    │   ├── DWithin                 # 距离内
    │   └── BBOX                    # 边界框
    │
    └── LogicOperator               # 逻辑操作符
        ├── And                     # 与
        ├── Or                      # 或
        └── Not                     # 非

Filter 构建示例

import org.geotools.api.filter.Filter;
import org.geotools.api.filter.FilterFactory;
import org.geotools.factory.CommonFactoryFinder;

// 获取过滤器工厂
FilterFactory ff = CommonFactoryFinder.getFilterFactory();

// 1. 属性过滤
Filter nameFilter = ff.equals(
    ff.property("name"),
    ff.literal("Beijing")
);

// 2. 数值比较
Filter popFilter = ff.greater(
    ff.property("population"),
    ff.literal(1000000)
);

// 3. 模糊匹配
Filter likeFilter = ff.like(
    ff.property("name"),
    "*ing"  // 以 ing 结尾
);

// 4. 空间过滤 - BBOX
Filter bboxFilter = ff.bbox(
    ff.property("the_geom"),
    115.0, 39.0, 117.0, 41.0,  // minX, minY, maxX, maxY
    "EPSG:4326"
);

// 5. 空间过滤 - 相交
Geometry queryGeom = createQueryGeometry();
Filter intersectsFilter = ff.intersects(
    ff.property("the_geom"),
    ff.literal(queryGeom)
);

// 6. 组合过滤
Filter combinedFilter = ff.and(
    nameFilter,
    ff.or(popFilter, bboxFilter)
);

// 应用过滤器
SimpleFeatureCollection filtered = source.getFeatures(combinedFilter);

3.2.4 Style API

Style API 用于定义地图样式:

/**
 * 样式接口层次
 */
Style
    │
    ├── FeatureTypeStyle            # 要素类型样式
    │   └── Rule                    # 规则
    │       └── Symbolizer          # 符号化器
    │           ├── PointSymbolizer # 点符号
    │           ├── LineSymbolizer  # 线符号
    │           ├── PolygonSymbolizer # 面符号
    │           ├── TextSymbolizer  # 文本标注
    │           └── RasterSymbolizer # 栅格符号
    │
    └── StyledLayerDescriptor       # SLD 文档

Style 构建示例

import org.geotools.api.style.*;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.styling.SLD;

// 获取样式工厂
StyleFactory sf = CommonFactoryFinder.getStyleFactory();
FilterFactory ff = CommonFactoryFinder.getFilterFactory();

// 1. 简单方式创建样式
Style simpleStyle = SLD.createPolygonStyle(
    Color.BLUE,     // 边框颜色
    Color.CYAN,     // 填充颜色
    0.5f            // 透明度
);

// 2. 详细方式创建样式
// 创建填充
Fill fill = sf.createFill(
    ff.literal(Color.CYAN),
    ff.literal(0.5)
);

// 创建边框
Stroke stroke = sf.createStroke(
    ff.literal(Color.BLUE),
    ff.literal(1.0)
);

// 创建面符号化器
PolygonSymbolizer polygonSymbolizer = sf.createPolygonSymbolizer(stroke, fill, null);

// 创建规则
Rule rule = sf.createRule();
rule.symbolizers().add(polygonSymbolizer);

// 创建要素类型样式
FeatureTypeStyle fts = sf.createFeatureTypeStyle();
fts.rules().add(rule);

// 创建样式
Style style = sf.createStyle();
style.featureTypeStyles().add(fts);

// 3. 从 SLD 文件加载
StyleFactory styleFactory = CommonFactoryFinder.getStyleFactory();
SLDParser parser = new SLDParser(styleFactory, new File("style.sld"));
Style[] styles = parser.readXML();

3.3 工厂模式详解

3.3.1 CommonFactoryFinder

GeoTools 使用 CommonFactoryFinder 作为工厂定位器:

import org.geotools.factory.CommonFactoryFinder;
import org.geotools.util.factory.Hints;

// 获取各种工厂
FilterFactory filterFactory = CommonFactoryFinder.getFilterFactory();
FilterFactory2 filterFactory2 = CommonFactoryFinder.getFilterFactory2();
StyleFactory styleFactory = CommonFactoryFinder.getStyleFactory();
FeatureFactory featureFactory = CommonFactoryFinder.getFeatureFactory();
FunctionFactory functionFactory = CommonFactoryFinder.getFunctionFactory();

// 使用 Hints 定制工厂行为
Hints hints = new Hints();
hints.put(Hints.FILTER_FACTORY, FilterFactory2.class);
FilterFactory customFactory = CommonFactoryFinder.getFilterFactory(hints);

3.3.2 DataStoreFinder

数据源查找使用 DataStoreFinder

import org.geotools.api.data.DataStore;
import org.geotools.api.data.DataStoreFinder;
import org.geotools.api.data.FileDataStoreFinder;

// 1. 通过参数查找
Map<String, Object> params = new HashMap<>();
params.put("url", new File("data/countries.shp").toURI().toURL());
DataStore store = DataStoreFinder.getDataStore(params);

// 2. 文件数据源快捷方式
FileDataStore fileStore = FileDataStoreFinder.getDataStore(new File("data/countries.shp"));

// 3. 获取所有可用的 DataStoreFactory
Iterator<DataStoreFactorySpi> factories = DataStoreFinder.getAvailableDataStores();
while (factories.hasNext()) {
    DataStoreFactorySpi factory = factories.next();
    System.out.println(factory.getDisplayName() + ": " + factory.getDescription());
}

3.3.3 JTSFactoryFinder

几何工厂查找:

import org.geotools.geometry.jts.JTSFactoryFinder;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.PrecisionModel;

// 获取默认几何工厂
GeometryFactory gf = JTSFactoryFinder.getGeometryFactory();

// 自定义精度模型
PrecisionModel pm = new PrecisionModel(1000);  // 精确到小数点后3位
GeometryFactory preciseGF = JTSFactoryFinder.getGeometryFactory(
    new Hints(Hints.JTS_PRECISION_MODEL, pm)
);

// 指定 SRID
GeometryFactory sridGF = JTSFactoryFinder.getGeometryFactory(
    new Hints(Hints.JTS_SRID, 4326)
);

3.4 SPI 服务发现机制

3.4.1 SPI 概述

GeoTools 使用 Java SPI (Service Provider Interface) 机制实现插件化:

META-INF/services/
├── org.geotools.api.data.DataStoreFactorySpi
├── org.geotools.api.data.FileDataStoreFactorySpi
├── org.geotools.api.filter.FilterFactory
├── org.geotools.api.style.StyleFactory
└── org.geotools.referencing.operation.MathTransformProvider

3.4.2 DataStore SPI

/**
 * DataStoreFactorySpi 接口
 */
public interface DataStoreFactorySpi extends Factory {
    
    // 显示名称
    String getDisplayName();
    
    // 描述
    String getDescription();
    
    // 参数描述
    Param[] getParametersInfo();
    
    // 是否可用
    boolean isAvailable();
    
    // 能否处理给定参数
    boolean canProcess(Map<String, ?> params);
    
    // 创建 DataStore
    DataStore createDataStore(Map<String, ?> params) throws IOException;
    
    // 创建新的 DataStore
    DataStore createNewDataStore(Map<String, ?> params) throws IOException;
}

自定义 DataStore 工厂示例

public class MyDataStoreFactory implements DataStoreFactorySpi {
    
    public static final Param URL_PARAM = new Param(
        "url", URL.class, "数据文件 URL", true);
    
    @Override
    public String getDisplayName() {
        return "My DataStore";
    }
    
    @Override
    public String getDescription() {
        return "自定义数据格式支持";
    }
    
    @Override
    public Param[] getParametersInfo() {
        return new Param[] { URL_PARAM };
    }
    
    @Override
    public boolean canProcess(Map<String, ?> params) {
        if (params.containsKey("url")) {
            URL url = (URL) params.get("url");
            return url.toString().endsWith(".myformat");
        }
        return false;
    }
    
    @Override
    public DataStore createDataStore(Map<String, ?> params) throws IOException {
        URL url = (URL) params.get("url");
        return new MyDataStore(url);
    }
    
    @Override
    public DataStore createNewDataStore(Map<String, ?> params) throws IOException {
        return createDataStore(params);
    }
    
    @Override
    public boolean isAvailable() {
        return true;
    }
}

注册服务

创建文件 META-INF/services/org.geotools.api.data.DataStoreFactorySpi

com.example.MyDataStoreFactory

3.5 事务管理

3.5.1 Transaction 接口

/**
 * Transaction 接口
 */
public interface Transaction extends Closeable {
    
    // 提交事务
    void commit() throws IOException;
    
    // 回滚事务
    void rollback() throws IOException;
    
    // 关闭事务
    void close() throws IOException;
    
    // 获取/设置状态
    State getState(Object key);
    void putState(Object key, State state);
    
    // 添加事务监听器
    void addAuthorization(String authID) throws IOException;
}

3.5.2 事务使用示例

import org.geotools.api.data.Transaction;
import org.geotools.data.DefaultTransaction;

public class TransactionExample {
    
    public void addFeaturesWithTransaction(
            SimpleFeatureStore store, 
            SimpleFeatureCollection features) throws IOException {
        
        // 创建事务
        Transaction transaction = new DefaultTransaction("add-features");
        
        try {
            // 绑定事务
            store.setTransaction(transaction);
            
            // 执行操作
            store.addFeatures(features);
            
            // 提交事务
            transaction.commit();
            System.out.println("添加成功");
            
        } catch (Exception e) {
            // 回滚事务
            transaction.rollback();
            System.err.println("添加失败,已回滚: " + e.getMessage());
            throw new IOException(e);
            
        } finally {
            // 关闭事务
            transaction.close();
        }
    }
    
    public void batchUpdate(SimpleFeatureStore store, Filter filter, 
                           String attrName, Object newValue) throws IOException {
        
        Transaction transaction = new DefaultTransaction("batch-update");
        store.setTransaction(transaction);
        
        try {
            // 批量更新
            store.modifyFeatures(attrName, newValue, filter);
            transaction.commit();
            
        } catch (Exception e) {
            transaction.rollback();
            throw new IOException(e);
            
        } finally {
            transaction.close();
        }
    }
}

3.6 资源管理

3.6.1 资源释放原则

GeoTools 遵循以下资源管理原则:

  1. DataStore.dispose() - 释放数据源连接
  2. Transaction.close() - 关闭事务
  3. FeatureIterator.close() - 关闭迭代器
  4. MapContent.dispose() - 释放地图资源

3.6.2 使用 try-with-resources

// 1. 迭代器使用
try (SimpleFeatureIterator features = collection.features()) {
    while (features.hasNext()) {
        SimpleFeature feature = features.next();
        // 处理要素
    }
}

// 2. 数据源使用
FileDataStore store = null;
try {
    store = FileDataStoreFinder.getDataStore(file);
    SimpleFeatureSource source = store.getFeatureSource();
    // 使用数据源
} finally {
    if (store != null) {
        store.dispose();
    }
}

// 3. 事务使用
try (Transaction tx = new DefaultTransaction("operation")) {
    store.setTransaction(tx);
    store.addFeatures(collection);
    tx.commit();
} catch (Exception e) {
    // 异常时自动关闭事务
}

3.6.3 资源泄漏检测

// 启用资源泄漏检测(仅用于开发调试)
System.setProperty("org.geotools.referencing.forceXY", "true");

// 使用 Hints 配置
Hints hints = new Hints();
hints.put(Hints.FORCE_LONGITUDE_FIRST_AXIS_ORDER, Boolean.TRUE);

3.7 线程安全

3.7.1 线程安全级别

组件 线程安全 说明
GeometryFactory 可在多线程中共享
FilterFactory 可在多线程中共享
StyleFactory 可在多线程中共享
DataStore 可在多线程中共享
FeatureSource 可在多线程中共享
FeatureIterator 每个线程独立使用
Transaction 每个线程独立事务
MapContent UI 线程使用

3.7.2 多线程使用示例

public class MultiThreadExample {
    
    // 共享的只读资源
    private final DataStore dataStore;
    private final GeometryFactory geometryFactory;
    private final FilterFactory filterFactory;
    
    public MultiThreadExample(DataStore dataStore) {
        this.dataStore = dataStore;
        this.geometryFactory = JTSFactoryFinder.getGeometryFactory();
        this.filterFactory = CommonFactoryFinder.getFilterFactory();
    }
    
    public void parallelProcess(List<Geometry> queryGeometries) {
        // 并行处理
        queryGeometries.parallelStream().forEach(geom -> {
            try {
                processQuery(geom);
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
    }
    
    private void processQuery(Geometry queryGeom) throws IOException {
        // 每个线程获取独立的 FeatureSource
        SimpleFeatureSource source = dataStore.getFeatureSource("layer");
        
        // 构建过滤器(FilterFactory 线程安全)
        Filter filter = filterFactory.intersects(
            filterFactory.property("the_geom"),
            filterFactory.literal(queryGeom)
        );
        
        // 使用独立的迭代器
        try (SimpleFeatureIterator features = source.getFeatures(filter).features()) {
            while (features.hasNext()) {
                SimpleFeature feature = features.next();
                // 处理结果
            }
        }
    }
}

3.8 日志系统

3.8.1 日志配置

GeoTools 使用 Java Util Logging (JUL),可以桥接到其他日志框架:

log4j2.xml 配置

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
        </Console>
        <File name="File" fileName="logs/geotools.log">
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} %-5level %logger{36} - %msg%n"/>
        </File>
    </Appenders>
    
    <Loggers>
        <!-- GeoTools 日志级别 -->
        <Logger name="org.geotools" level="INFO"/>
        <Logger name="org.geotools.data" level="DEBUG"/>
        <Logger name="org.geotools.jdbc" level="DEBUG"/>
        <Logger name="org.geotools.referencing" level="WARN"/>
        
        <!-- JTS 日志 -->
        <Logger name="org.locationtech.jts" level="WARN"/>
        
        <Root level="INFO">
            <AppenderRef ref="Console"/>
            <AppenderRef ref="File"/>
        </Root>
    </Loggers>
</Configuration>

3.8.2 代码中使用日志

import java.util.logging.Level;
import java.util.logging.Logger;
import org.geotools.util.logging.Logging;

public class LoggingExample {
    
    // GeoTools 推荐方式
    private static final Logger LOGGER = Logging.getLogger(LoggingExample.class);
    
    public void process() {
        LOGGER.info("开始处理");
        
        try {
            // 业务逻辑
            LOGGER.fine("详细调试信息");
            
        } catch (Exception e) {
            LOGGER.log(Level.SEVERE, "处理失败", e);
        }
        
        LOGGER.info("处理完成");
    }
}

3.9 本章小结

本章详细介绍了 GeoTools 的核心架构和模块设计:

  1. 分层架构

    • 模型层、数据层、表现层、服务层、应用层
    • 清晰的职责划分
  2. 核心接口

    • DataStore API:统一数据访问
    • Filter API:查询过滤
    • Style API:地图样式
  3. 工厂模式

    • CommonFactoryFinder:工厂定位器
    • DataStoreFinder:数据源查找
    • JTSFactoryFinder:几何工厂
  4. SPI 机制

    • 插件化扩展
    • 服务发现
  5. 事务和资源管理

    • Transaction 接口
    • 资源释放原则
  6. 线程安全和日志

    • 线程安全级别
    • 日志配置

关键要点

  • GeoTools 采用接口导向的设计
  • 使用工厂模式创建对象
  • SPI 机制支持插件扩展
  • 注意资源释放和线程安全

下一步

在下一章中,我们将深入学习 JTS 几何对象的使用。


← 上一章:环境搭建与快速开始 | 返回目录 | 下一章:几何对象与 JTS 集成 →

posted @ 2025-12-29 10:47  我才是银古  阅读(1)  评论(0)    收藏  举报