基于Java和高德开放平台的WebAPI集成实践 - 以搜索POI 2.0为例
一、前期准备
1.1 高德开放平台注册与密钥获取
-
访问高德开放平台注册开发者账号
-
进入「控制台」→「应用管理」→「创建新应用」
-
添加「Web服务」类型的Key
-
记录生成的Key(如:
d2e8c1a3f5b7d9f2e4c6b8a9d3f5e7c2
)
java
// 建议将密钥存储在配置文件中 public class AMapConfig { public static final String API_KEY = "您的Web服务Key"; public static final String POI_SEARCH_URL = "https://restapi.amap.com/v5/place/text"; }
1.2 开发环境搭建
Maven依赖:
xml
<dependencies> <!-- HTTP客户端 --> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.13</version> </dependency> <!-- JSON处理 --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.13.3</version> </dependency> <!-- 单元测试 --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> <scope>test</scope> </dependency> </dependencies>
二、POI搜索API核心实现
2.1 基础请求封装
java
import org.apache.http.HttpEntity; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; public class AMapHttpUtil { public static String doGet(String url) throws Exception { CloseableHttpClient httpClient = HttpClients.createDefault(); HttpGet httpGet = new HttpGet(url); try (CloseableHttpResponse response = httpClient.execute(httpGet)) { HttpEntity entity = response.getEntity(); return EntityUtils.toString(entity, "UTF-8"); } } }
2.2 POI搜索请求构建
java
import java.net.URLEncoder; import java.util.LinkedHashMap; import java.util.Map; public class POISearchBuilder { private final Map<String, String> params = new LinkedHashMap<>(); public POISearchBuilder(String key) { params.put("key", key); } public POISearchBuilder keywords(String keywords) { params.put("keywords", keywords); return this; } public POISearchBuilder city(String city) { params.put("city", city); return this; } public POISearchBuilder pageSize(int size) { params.put("page_size", String.valueOf(size)); return this; } public String build() throws Exception { StringBuilder url = new StringBuilder(AMapConfig.POI_SEARCH_URL); url.append("?"); for (Map.Entry<String, String> entry : params.entrySet()) { url.append(entry.getKey()) .append("=") .append(URLEncoder.encode(entry.getValue(), "UTF-8")) .append("&"); } return url.substring(0, url.length() - 1); } }
三、响应数据处理
3.1 定义POI数据结构
java
import com.fasterxml.jackson.annotation.JsonProperty; public class POI { private String id; private String name; @JsonProperty("address") private String address; @JsonProperty("location") private String location; // 格式:"经度,纬度" @JsonProperty("pname") private String province; @JsonProperty("cityname") private String city; @JsonProperty("adname") private String district; // getters & setters // ... }
3.2 响应解析工具
java
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.JsonNode; public class POIResponseParser { private static final ObjectMapper mapper = new ObjectMapper(); public static List<POI> parsePOIList(String jsonResponse) throws Exception { JsonNode root = mapper.readTree(jsonResponse); JsonNode pois = root.path("pois"); if (pois.isMissingNode()) { throw new RuntimeException("无效的POI响应数据"); } List<POI> result = new ArrayList<>(); for (JsonNode poiNode : pois) { POI poi = mapper.treeToValue(poiNode, POI.class); result.add(poi); } return result; } }
四、完整功能实现
4.1 服务层封装
java
import java.util.List; public class POISearchService { public List<POI> search(String keywords, String city, int pageSize) throws Exception { // 构建请求URL String url = new POISearchBuilder(AMapConfig.API_KEY) .keywords(keywords) .city(city) .pageSize(pageSize) .build(); // 发送HTTP请求 String response = AMapHttpUtil.doGet(url); // 解析响应数据 return POIResponseParser.parsePOIList(response); } }
4.2 单元测试示例
java
import org.junit.Test; import java.util.List; public class POISearchServiceTest { @Test public void testSearch() throws Exception { POISearchService service = new POISearchService(); // 搜索北京市的"星巴克" List<POI> pois = service.search("星巴克", "北京", 10); System.out.println("搜索结果:"); pois.forEach(poi -> { System.out.printf("%s - %s (%s)\n", poi.getName(), poi.getAddress(), poi.getLocation()); }); assertFalse(pois.isEmpty()); } }
五、进阶功能扩展
5.1 分页查询实现
public class POISearchBuilder { // 在原有基础上添加 public POISearchBuilder pageNum(int page) { params.put("page_num", String.valueOf(page)); return this; } // 使用示例 public List<POI> searchByPage(String keywords, String city, int pageSize, int pageNum) throws Exception { String url = new POISearchBuilder(AMapConfig.API_KEY) .keywords(keywords) .city(city) .pageSize(pageSize) .pageNum(pageNum) .build(); String response = AMapHttpUtil.doGet(url); return POIResponseParser.parsePOIList(response); } }
5.2 分类过滤查询
java
public class POISearchBuilder { // 添加分类过滤 public POISearchBuilder types(String types) { params.put("types", types); return this; } // 使用示例(搜索餐饮类) List<POI> restaurants = service.search("", "上海", 20) .types("050000"); // 餐饮分类代码 }
六、性能优化实践
6.1 HTTP连接池配置
java
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; public class AMapHttpUtil { private static final PoolingHttpClientConnectionManager connManager; static { connManager = new PoolingHttpClientConnectionManager(); connManager.setMaxTotal(200); // 最大连接数 connManager.setDefaultMaxPerRoute(50); // 每个路由最大连接数 } public static CloseableHttpClient createHttpClient() { return HttpClients.custom() .setConnectionManager(connManager) .build(); } }
6.2 响应缓存策略
java
import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine; public class POISearchService { private final Cache<String, List<POI>> cache; public POISearchService() { this.cache = Caffeine.newBuilder() .maximumSize(1000) .expireAfterWrite(30, TimeUnit.MINUTES) .build(); } public List<POI> searchWithCache(String keywords, String city) throws Exception { String cacheKey = keywords + "|" + city; return cache.get(cacheKey, k -> { try { return search(keywords, city, 20); } catch (Exception e) { throw new RuntimeException(e); } }); } }
七、异常处理与日志
7.1 自定义异常类
public class AMapAPIException extends Exception { private final int errorCode; public AMapAPIException(int errorCode, String message) { super(message); this.errorCode = errorCode; } public int getErrorCode() { return errorCode; } }
7.2 增强的错误处理
java
public class POIResponseParser { public static List<POI> parsePOIList(String jsonResponse) throws AMapAPIException { try { JsonNode root = mapper.readTree(jsonResponse); // 检查API返回的错误码 if (root.has("infocode")) { int code = root.get("infocode").asInt(); if (code != 10000) { String info = root.get("info").asText(); throw new AMapAPIException(code, info); } } // 原有解析逻辑... } catch (IOException e) { throw new AMapAPIException(-1, "JSON解析失败"); } } }
八、实际应用案例
8.1 Spring Boot集成示例
Controller层:
java
@RestController @RequestMapping("/api/poi") public class POIController { @Autowired private POISearchService poiService; @GetMapping("/search") public ResponseEntity<List<POI>> search( @RequestParam String keywords, @RequestParam(required = false) String city, @RequestParam(defaultValue = "10") int size) { try { List<POI> pois = poiService.search(keywords, city, size); return ResponseEntity.ok(pois); } catch (AMapAPIException e) { return ResponseEntity.status(500) .header("X-Error-Code", String.valueOf(e.getErrorCode())) .build(); } } }
8.2 前端调用示例
javascript
// Vue.js示例 async function searchPOI() { try { const response = await axios.get('/api/poi/search', { params: { keywords: '医院', city: '广州', size: 5 } }); this.pois = response.data; } catch (error) { console.error('搜索失败:', error); } }
九、最佳实践建议
-
密钥安全:
-
不要将API Key硬编码在代码中
-
使用环境变量或配置中心管理敏感信息
-
设置IP白名单限制调用来源
-
-
请求优化:
-
合理设置分页大小(建议20-50条/页)
-
对高频查询结果实施缓存
-
合并邻近区域的查询请求
-
-
合规使用:
-
遵守高德API调用频率限制(QPS限制)
-
及时处理接口返回的配额不足提示
-
商用场景购买相应服务套餐
-
十、常见问题解答
Q1: 如何获取POI的详细图片信息?
A: 使用「高德POI详情API」获取特定POI的详细信息,包括图片URL:
java
String detailUrl = "https://restapi.amap.com/v3/place/detail?id=" + poiId + "&key=" + key;
Q2: 返回的坐标是什么坐标系?
A: 高德默认使用GCJ-02坐标系,如需WGS-84坐标需调用坐标转换接口
Q3: 如何实现周边搜索?
A: 使用「周边搜索API」并传入中心点坐标:
java
String aroundUrl = "https://restapi.amap.com/v3/place/around" + "?key=" + key + "&location=116.473168,39.993015" + "&radius=1000" + // 搜索半径(米) "&keywords=餐饮";
通过本教程,您已经掌握了高德POI搜索API的核心集成方法。建议进一步探索高德的其他Web服务API,如路径规划、地理编码、天气查询等,构建更丰富的位置服务应用。