SpringBoot + 百度地图SDK,打造企业级位置服务中台

大家好,我是小悟。

一、百度地图平台详解

1.1 百度地图简介

百度地图是百度公司提供的智能位置服务平台,是中国领先的数字化地图解决方案。它集成了海量地理数据、先进的位置技术和丰富的API接口,为开发者和企业提供全面的地图服务能力。

1.2 核心功能特性

基础地图服务:

  • 2D/3D地图展示:支持多种地图样式(普通地图、卫星地图、交通地图)
  • 地图交互操作:缩放、平移、旋转、倾斜等交互功能
  • 自定义地图样式:支持个性化地图风格定制

定位与导航:

  • 高精度定位:支持GPS、基站、Wi-Fi混合定位
  • 路线规划:驾车、步行、骑行、公交等多种出行方式的路线规划
  • 实时导航:语音导航、路口放大图、电子眼提醒等功能

搜索与查询:

  • POI搜索:支持周边搜索、区域搜索、城市内搜索
  • 逆地理编码:将坐标转换为详细地址信息
  • 地理编码:将地址描述转换为经纬度坐标

数据可视化:

  • 海量点展示:支持百万级点数据的渲染
  • 热力图:展示数据分布密度
  • 轨迹回放:实时或历史轨迹展示

高级功能:

  • 行政区划边界:获取省市县各级行政区划边界
  • 实时路况:获取道路实时交通状况
  • 天气查询:获取指定位置的天气信息

1.3 应用场景

  • 出行服务:网约车、共享单车、物流配送
  • 生活服务:外卖订餐、本地生活、酒店预订
  • 商业智能:选址分析、客流分析、商圈洞察
  • 政务管理:城市规划、应急管理、环境监测
  • 社交娱乐:位置共享、签到打卡、旅游推荐

二、SpringBoot集成百度地图SDK详细步骤

2.1 环境准备

前置条件:

  • JDK 8+
  • Maven 3.6+
  • Spring Boot 2.x
  • 百度地图开发者账号

2.2 获取百度地图AK密钥

  1. 访问百度地图开放平台
  2. 注册/登录百度账号
  3. 进入控制台,点击"应用管理" → "我的应用"
  4. 创建应用:
    • 应用名称:自定义(如"MySpringBootApp")
    • 应用类型:选择"服务端"
    • 启用服务:根据需要勾选(一般选择"地理编码"、"逆地理编码"等)
  5. 获取AK(Access Key)

2.3 创建SpringBoot项目

使用Spring Initializr创建项目:

  • Group: com.example
  • Artifact: baidu-map-demo
  • Dependencies: Spring Web, Lombok

2.4 配置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>
    
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.0</version>
    </parent>
    
    <groupId>com.example</groupId>
    <artifactId>baidu-map-demo</artifactId>
    <version>1.0.0</version>
    
    <properties>
        <java.version>1.8</java.version>
    </properties>
    
    <dependencies>
        <!-- Spring Boot Web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        
        <!-- Lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        
        <!-- HttpClient -->
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.13</version>
        </dependency>
        
        <!-- FastJson -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.83</version>
        </dependency>
        
        <!-- Configuration Processor -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>
</project>

2.5 配置文件

application.yml

复制代码
server:
  port: 8080

spring:
  application:
    name: baidu-map-demo

# 百度地图配置
baidu:
  map:
    ak: your_ak_here  # 替换为你的AK
    geocoding-url: https://api.map.baidu.com/geocoding/v3/
    reverse-geocoding-url: https://api.map.baidu.com/reverse_geocoding/v3/
    place-search-url: https://api.map.baidu.com/place/v2/search
    direction-url: https://api.map.baidu.com/direction/v2
    weather-url: https://api.map.baidu.com/weather/v1/
    
# 日志配置
logging:
  level:
    com.example: DEBUG

2.6 配置类创建

BaiduMapProperties.java

复制代码
package com.example.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Data
@Component
@ConfigurationProperties(prefix = "baidu.map")
public class BaiduMapProperties {
    private String ak;
    private String geocodingUrl;
    private String reverseGeocodingUrl;
    private String placeSearchUrl;
    private String directionUrl;
    private String weatherUrl;
}

BaiduMapConfig.java

复制代码
package com.example.config;

import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class BaiduMapConfig {
    
    @Bean
    public CloseableHttpClient httpClient() {
        PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
        connectionManager.setMaxTotal(200);
        connectionManager.setDefaultMaxPerRoute(20);
        
        return HttpClients.custom()
                .setConnectionManager(connectionManager)
                .setConnectionManagerShared(true)
                .build();
    }
}

2.7 实体类设计

BaseResponse.java

复制代码
package com.example.dto;

import lombok.Data;

@Data
public class BaseResponse<T> {
    private Integer status;
    private String message;
    private T data;
    
    public static <T> BaseResponse<T> success(T data) {
        BaseResponse<T> response = new BaseResponse<>();
        response.setStatus(0);
        response.setMessage("success");
        response.setData(data);
        return response;
    }
    
    public static <T> BaseResponse<T> error(Integer status, String message) {
        BaseResponse<T> response = new BaseResponse<>();
        response.setStatus(status);
        response.setMessage(message);
        return response;
    }
}

GeocodingRequest.java

复制代码
package com.example.dto.request;

import lombok.Data;

@Data
public class GeocodingRequest {
    private String address;      // 地址
    private String city;         // 城市(可选)
    private Integer retCoordtype = 1;  // 坐标类型:1为百度坐标,2为国测局坐标,3为wgs84
}

GeocodingResult.java

复制代码
package com.example.dto.response;

import lombok.Data;

@Data
public class GeocodingResult {
    private Location location;
    private Integer precise;      // 1为精确查找,0为模糊查找
    private Integer confidence;   // 可信度
    private String comprehension; // 理解程度
    private String level;         // 地址类型
    
    @Data
    public static class Location {
        private Double lng;  // 经度
        private Double lat;  // 纬度
    }
}

ReverseGeocodingRequest.java

复制代码
package com.example.dto.request;

import lombok.Data;

@Data
public class ReverseGeocodingRequest {
    private Double latitude;      // 纬度
    private Double longitude;     // 经度
    private Integer retCoordtype = 1;  // 坐标类型
    private Integer radius = 100;      // 搜索半径(米)
}

ReverseGeocodingResult.java

复制代码
package com.example.dto.response;

import lombok.Data;
import java.util.List;

@Data
public class ReverseGeocodingResult {
    private AddressComponent addressComponent;
    private String formattedAddress;
    private List<Poi> pois;
    private Location location;
    
    @Data
    public static class AddressComponent {
        private String country;      // 国家
        private String province;     // 省份
        private String city;         // 城市
        private String district;     // 区县
        private String street;       // 街道
        private String streetNumber; // 门牌号
    }
    
    @Data
    public static class Poi {
        private String name;         // POI名称
        private String addr;         // POI地址
        private Location location;   // POI坐标
    }
    
    @Data
    public static class Location {
        private Double lng;
        private Double lat;
    }
}

2.8 服务层实现

BaiduMapService.java

复制代码
package com.example.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.example.config.BaiduMapProperties;
import com.example.dto.request.GeocodingRequest;
import com.example.dto.request.ReverseGeocodingRequest;
import com.example.dto.response.GeocodingResult;
import com.example.dto.response.ReverseGeocodingResult;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;

@Slf4j
@Service
public class BaiduMapService {
    
    @Autowired
    private CloseableHttpClient httpClient;
    
    @Autowired
    private BaiduMapProperties baiduMapProperties;
    
    /**
     * 地理编码(地址转坐标)
     */
    public GeocodingResult geocoding(GeocodingRequest request) {
        try {
            URIBuilder uriBuilder = new URIBuilder(baiduMapProperties.getGeocodingUrl());
            uriBuilder.setParameter("address", request.getAddress())
                    .setParameter("output", "json")
                    .setParameter("ak", baiduMapProperties.getAk())
                    .setParameter("ret_coordtype", String.valueOf(request.getRetCoordtype()));
            
            if (StringUtils.hasText(request.getCity())) {
                uriBuilder.setParameter("city", request.getCity());
            }
            
            URI uri = uriBuilder.build();
            String response = executeGet(uri);
            
            JSONObject jsonObject = JSON.parseObject(response);
            if (jsonObject.getInteger("status") == 0) {
                JSONObject result = jsonObject.getJSONObject("result");
                return JSON.parseObject(result.toJSONString(), GeocodingResult.class);
            } else {
                log.error("地理编码失败: {}", jsonObject.getString("message"));
                return null;
            }
        } catch (URISyntaxException e) {
            log.error("URI构建异常", e);
            return null;
        }
    }
    
    /**
     * 逆地理编码(坐标转地址)
     */
    public ReverseGeocodingResult reverseGeocoding(ReverseGeocodingRequest request) {
        try {
            URIBuilder uriBuilder = new URIBuilder(baiduMapProperties.getReverseGeocodingUrl());
            String location = request.getLatitude() + "," + request.getLongitude();
            
            uriBuilder.setParameter("location", location)
                    .setParameter("output", "json")
                    .setParameter("ak", baiduMapProperties.getAk())
                    .setParameter("ret_coordtype", String.valueOf(request.getRetCoordtype()))
                    .setParameter("radius", String.valueOf(request.getRadius()))
                    .setParameter("extensions_poi", "1");  // 返回周边POI
            
            URI uri = uriBuilder.build();
            String response = executeGet(uri);
            
            JSONObject jsonObject = JSON.parseObject(response);
            if (jsonObject.getInteger("status") == 0) {
                JSONObject result = jsonObject.getJSONObject("result");
                return JSON.parseObject(result.toJSONString(), ReverseGeocodingResult.class);
            } else {
                log.error("逆地理编码失败: {}", jsonObject.getString("message"));
                return null;
            }
        } catch (URISyntaxException e) {
            log.error("URI构建异常", e);
            return null;
        }
    }
    
    /**
     * 执行HTTP GET请求
     */
    private String executeGet(URI uri) {
        HttpGet httpGet = new HttpGet(uri);
        httpGet.setHeader("Content-Type", "application/json;charset=utf-8");
        
        try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
            return EntityUtils.toString(response.getEntity(), "UTF-8");
        } catch (IOException e) {
            log.error("HTTP请求异常", e);
            return null;
        }
    }
}

2.9 控制器层实现

BaiduMapController.java

复制代码
package com.example.controller;

import com.example.dto.BaseResponse;
import com.example.dto.request.GeocodingRequest;
import com.example.dto.request.ReverseGeocodingRequest;
import com.example.dto.response.GeocodingResult;
import com.example.dto.response.ReverseGeocodingResult;
import com.example.service.BaiduMapService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;

@Slf4j
@RestController
@RequestMapping("/api/map")
public class BaiduMapController {
    
    @Autowired
    private BaiduMapService baiduMapService;
    
    /**
     * 地理编码接口
     */
    @PostMapping("/geocoding")
    public BaseResponse<GeocodingResult> geocoding(@RequestBody @Valid GeocodingRequest request) {
        log.info("地理编码请求: {}", request);
        GeocodingResult result = baiduMapService.geocoding(request);
        if (result != null) {
            return BaseResponse.success(result);
        } else {
            return BaseResponse.error(500, "地理编码失败");
        }
    }
    
    /**
     * 逆地理编码接口
     */
    @PostMapping("/reverse-geocoding")
    public BaseResponse<ReverseGeocodingResult> reverseGeocoding(@RequestBody @Valid ReverseGeocodingRequest request) {
        log.info("逆地理编码请求: {}", request);
        ReverseGeocodingResult result = baiduMapService.reverseGeocoding(request);
        if (result != null) {
            return BaseResponse.success(result);
        } else {
            return BaseResponse.error(500, "逆地理编码失败");
        }
    }
    
    /**
     * 健康检查接口
     */
    @GetMapping("/health")
    public BaseResponse<String> health() {
        return BaseResponse.success("百度地图服务正常运行");
    }
}

2.10 启动类

BaiduMapApplication.java

复制代码
package com.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;

@SpringBootApplication
@EnableConfigurationProperties
public class BaiduMapApplication {
    public static void main(String[] args) {
        SpringApplication.run(BaiduMapApplication.class, args);
    }
}

三、接口测试

3.1 使用curl测试地理编码接口

复制代码
curl -X POST http://localhost:8080/api/map/geocoding \
  -H "Content-Type: application/json" \
  -d '{
    "address": "北京市朝阳区百度大厦",
    "city": "北京市",
    "retCoordtype": 1
  }'

3.2 使用curl测试逆地理编码接口

复制代码
curl -X POST http://localhost:8080/api/map/reverse-geocoding \
  -H "Content-Type: application/json" \
  -d '{
    "latitude": 40.056878,
    "longitude": 116.30815,
    "radius": 200
  }'

3.3 测试健康检查接口

复制代码
curl http://localhost:8080/api/map/health

四、扩展功能实现

4.1 POI搜索功能

BaiduMapService中添加:

复制代码
/**
 * POI搜索
 */
public JSONObject placeSearch(String query, String region, String location) {
    try {
        URIBuilder uriBuilder = new URIBuilder(baiduMapProperties.getPlaceSearchUrl());
        uriBuilder.setParameter("query", query)
                .setParameter("region", region)
                .setParameter("output", "json")
                .setParameter("ak", baiduMapProperties.getAk());
        
        if (StringUtils.hasText(location)) {
            uriBuilder.setParameter("location", location);
        }
        
        URI uri = uriBuilder.build();
        String response = executeGet(uri);
        return JSON.parseObject(response);
    } catch (URISyntaxException e) {
        log.error("URI构建异常", e);
        return null;
    }
}

4.2 路线规划功能

复制代码
/**
 * 驾车路线规划
 */
public JSONObject drivingDirection(String origin, String destination) {
    try {
        URIBuilder uriBuilder = new URIBuilder(baiduMapProperties.getDirectionUrl() + "/driving");
        uriBuilder.setParameter("origin", origin)
                .setParameter("destination", destination)
                .setParameter("ak", baiduMapProperties.getAk());
        
        URI uri = uriBuilder.build();
        String response = executeGet(uri);
        return JSON.parseObject(response);
    } catch (URISyntaxException e) {
        log.error("URI构建异常", e);
        return null;
    }
}

五、最佳实践

5.1 性能优化

  1. 连接池管理:合理配置HTTP连接池参数
  2. 缓存策略:对频繁请求的结果进行缓存
  3. 异步处理:使用异步方式处理耗时请求
  4. 限流控制:防止QPS超过百度地图API限制

5.2 异常处理

复制代码
@ControllerAdvice
public class GlobalExceptionHandler {
    
    @ExceptionHandler(Exception.class)
    public BaseResponse<?> handleException(Exception e) {
        log.error("系统异常", e);
        return BaseResponse.error(500, "系统内部错误");
    }
}

5.3 日志记录

使用AOP记录API调用日志:

复制代码
@Aspect
@Component
@Slf4j
public class BaiduMapLogAspect {
    
    @Around("@annotation(org.springframework.web.bind.annotation.PostMapping)")
    public Object logApiCall(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        Object result = joinPoint.proceed();
        long duration = System.currentTimeMillis() - start;
        
        log.info("API调用: {},耗时: {}ms", joinPoint.getSignature(), duration);
        return result;
    }
}

六、总结

6.1 技术要点回顾

  1. 百度地图能力:提供了丰富的地图服务能力,包括定位、搜索、导航、可视化等核心功能
  2. SpringBoot集成:通过配置类和属性绑定,实现百度地图SDK与SpringBoot框架的无缝集成
  3. HTTP客户端封装:使用Apache HttpClient封装HTTP请求,处理API调用
  4. 响应式设计:统一API响应格式,方便前端调用和处理

6.2 项目架构特点

  • 模块化设计:将配置、服务、控制层分离,便于维护和扩展
  • 配置集中管理:所有百度地图相关配置集中管理,便于修改和部署
  • 异常处理完善:统一的异常处理机制,提供友好的错误提示
  • 日志记录全面:记录关键操作日志,便于问题追踪和性能优化

6.3 应用价值

  1. 地理位置服务:为业务系统提供标准化的地理位置服务能力
  2. 功能扩展性:可根据业务需求快速扩展新的地图功能
  3. 开发效率提升:封装了复杂的API调用细节,简化开发流程
  4. 系统可靠性:完善的异常处理和日志记录,保障系统稳定运行

通过以上完整的实现,成功地将百度地图SDK集成到SpringBoot项目中,建立了一个功能完善、易于扩展的位置服务平台。

谢谢你看我的文章,既然看到这里了,如果觉得不错,随手点个赞、转发、在看三连吧,感谢感谢。那我们,下次再见。

您的一键三连,是我更新的最大动力,谢谢

山水有相逢,来日皆可期,谢谢阅读,我们再会

我手中的金箍棒,上能通天,下能探海

相关推荐
weixin199701080162 小时前
网易考拉商品详情页前端性能优化实战
java·前端·python·性能优化
Natalia_Portman2 小时前
springboot整合DolphinDB
java·数据库·spring boot·后端·db
hua872222 小时前
【Springboot3+vue3】从零到一搭建Springboot3+vue3前后端分离项目之后端环境搭建
java
不光头强2 小时前
LinkedList知识点
java
跳跳鱼2 小时前
ThreadLocal 核心源码解析:属性、内部类与重要接口深度剖析
java
JamesYoung79712 小时前
第七部分 — 存储 数据建模与迁移提示
java·开发语言·数据结构
大志学java2 小时前
idea中切换分支后,项目目录不显示的问题
java·elasticsearch·intellij-idea
程序员敲代码吗2 小时前
进程与线程:操作系统中的核心组件
java·开发语言
Java面试题总结2 小时前
java面试题及答案(基础题122道)
java·开发语言·jvm·spring·spring cloud·golang·java-ee