基于Java和高德开放平台的WebAPI集成实践 - 以搜索POI 2.0为例

基于Java和高德开放平台的WebAPI集成实践 - 以搜索POI 2.0为例

一、前期准备

1.1 高德开放平台注册与密钥获取

  1. 访问高德开放平台注册开发者账号

  2. 进入「控制台」→「应用管理」→「创建新应用」

  3. 添加「Web服务」类型的Key

  4. 记录生成的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 分页查询实现

java

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 自定义异常类

java

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);
  }
}

九、最佳实践建议

  1. 密钥安全

    • 不要将API Key硬编码在代码中

    • 使用环境变量或配置中心管理敏感信息

    • 设置IP白名单限制调用来源

  2. 请求优化

    • 合理设置分页大小(建议20-50条/页)

    • 对高频查询结果实施缓存

    • 合并邻近区域的查询请求

  3. 合规使用

    • 遵守高德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,如路径规划、地理编码、天气查询等,构建更丰富的位置服务应用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值