多方式调用三方接口练习(二)(代码练习)

公司里最近有了一些需要在自己系统业务逻辑中调用三方系统业务逻辑的需求;因为有了这些需求,我这里也对三方接口调用的过程进行了一些梳理,还有多种方式调用的练习、接收结果的json格式化、数据入库等操作:特此记录,以提示自己,希望也能帮到大家,大家一起进步!

自己在网上找了一些可以进行三方调用练习的网址接口,并开展如下的练习

 无颜色标注完整版代码

package com.atguigu.gulimall.coupon.learn.controller;

import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.atguigu.gulimall.coupon.entity.ThirdRequestLog;
import com.atguigu.gulimall.coupon.entity.WeatherEntity;
import com.atguigu.gulimall.coupon.learn.annotation.ParamValided;
import com.atguigu.gulimall.coupon.learn.entity.CommonConstant;
import com.atguigu.gulimall.coupon.learn.entity.vo.WeatherReqVo;
import com.atguigu.gulimall.coupon.learn.myexception.BusiException;
import com.atguigu.gulimall.coupon.learn.util.SpringBeanUtil;
import com.atguigu.gulimall.coupon.service.ThirdLearnService;
import com.atguigu.gulimall.coupon.service.ThirdRequestLogService;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**httpThirdCallController
 * 调用三方接口 学习
 *
 * @author: jd
 * @create: 2024-05-27
 */
@RestController
@Slf4j
@RequestMapping("/coupon/thirdLearn")
public class HttpThirdCallController {

    @Autowired
    private Environment environment;

    @Autowired
    CommonConstant commonConstant;

    @Autowired
    ThirdLearnService thirdLearnService;

    @Autowired
    public ThirdRequestLogService thirdRequestLogService;

    //
    private ExecutorService service = Executors.newCachedThreadPool();

    //0.模拟数据库中的数据
    private static Map<Integer, WeatherReqVo> weatherReqVoMap = null;

    static {
        weatherReqVoMap = new HashMap<Integer, WeatherReqVo>();//创建一个天气表,存储着现有的,可以查询天气的地市
        //初始化表数据
        weatherReqVoMap.put(10001, new WeatherReqVo(10001L, "石家庄"));
        weatherReqVoMap.put(10002, new WeatherReqVo(10002L, "北京"));
        weatherReqVoMap.put(10003, new WeatherReqVo(10003L, "武汉"));
        weatherReqVoMap.put(10004, new WeatherReqVo(10004L, "邯郸"));
        weatherReqVoMap.put(10005, new WeatherReqVo(10005L, "天津"));
        weatherReqVoMap.put(10006, new WeatherReqVo(10006L, "郑州"));
    }
    // 数据来源: 原文链接:https://blog.csdn.net/QQQZSJ/article/details/109116120



    @ApiOperation("第三方调用练习")
    @GetMapping("/thirdCall")
    @ParamValided
    public String getWeather(WeatherReqVo weatherReqVo) {
        //校验所传地址在数据库中是否存在
        if (!weatherReqVoMap.containsValue(new WeatherReqVo(weatherReqVo.getId(), weatherReqVo.getCityName()))) {
            //这里抛出的是自定义的异常,而且这个自定义的异常是被自定义的异常捕捉器给捕捉
            throw new BusiException("所查询地市不在数据库可查询地市列表中", CommonConstant.ErrorCode.NO_VALUE_ERROR);
        }
        //调用三方接口,获取天气

//        ()AopContext.currentProxy()
        //由于当@Transactional(rollbackFor = Exception.class) 加在同类下面的自有方法的时候,直接调用的话,会导致事务失效,
        // 这种情况下要对在会被外界调用的方法上加上这个注解,因为被外界调用的方法会被代理对象所调用,此时事务会生效。如果有异常发生,则会回滚。如果非要将@Transactional 注解加到下面内部调用的方法里面的话
        //并且想要这个注解生效的话,则需要 使用Spring的IOC容器中去取得bean代理对象,通过这个代理对象去调用内部的方法。此时事务才会生效。



//     调用方式getWeatherMethod()方式一:(也是最常用调用内部方法的方式)   getWeatherMethod(weatherReqVo); //直接调用的话,会导致事务失效, 


       调用方式二:(因为这里是方法A调用同类的内部方法B,而B是有事务注解的,这种情况下相当于this调用,不会通过代理对象调用,所以此时的调用,B方法上的事务其实不会生效,所以这里可以通过代理对象来调用) 
        HttpThirdCallController httpThirdCallController = (HttpThirdCallController)SpringBeanUtil.getBean("httpThirdCallController");
        httpThirdCallController.getWeatherMethod(weatherReqVo);
        return "查询成功";

    }

    /**
     * 调用三方天气查询接口
     *
     * @param weatherReqVo
     */
    @Transactional(rollbackFor = Exception.class)
    public void getWeatherMethod(WeatherReqVo weatherReqVo) {
        //组合参数
        HashMap<String, Object> params = new HashMap<>();
        params.put("city", weatherReqVo.getCityName());
        //获取API_KEY
        params.put("key", environment.getProperty("API_KEY"));  // 另外一种获取方式  CommonConstant.API_KEY
        //拼接参数(包含参数编码)
        String queryParams = urlencode(params);
        log.info("===========>{}", queryParams);

        //原始Connection get请求方式,进行三方接口调用
//        String response = doGet(CommonConstant.API_URL, queryParams);
        //原始Connection post请求方式  进行三方接口调
   
        //String response = httpRequestPost(CommonConstant.API_URL, weatherReqVo.getCityName(), environment.getProperty("API_KEY"));

        //使用Spring的三方接口调用工具进行调用 springRestTemplate 请求第三方接口 post方式
        String response = springRestTemplate(CommonConstant.API_URL, params, queryParams);

        log.info("===========>JSONUtil.parseObj(response)\n{}", JSONUtil.parseObj(response));

        JSONObject jsonObject = JSONUtil.parseObj(response);
        int error_code = jsonObject.getInt("error_code");
        if (error_code == 0) {
            System.out.println("调用接口成功");
            JSONObject result1 = JSONUtil.parseObj(jsonObject.getStr("result"));
            JSONObject result = jsonObject.getJSONObject("result");
            log.info("========>JSONUtil.parseObj(jsonObject.getStr(\"result\"))\n  {}", result1);
            System.out.println("======间隔======");
            log.info("========>jsonObject.getJSONObject(\"result\")\n {}", result);
            JSONObject realtime = result.getJSONObject("realtime");
            System.out.println("=============今日天气===============");
            //查询地市
            System.out.printf("城市:%s%n", result.getStr("city"));
            //今日天气
            System.out.printf("天气:%s%n", realtime.getStr("info"));
            System.out.printf("温度:%s%n", realtime.getStr("temperature"));
            System.out.printf("湿度:%s%n", realtime.getStr("humidity"));
            System.out.printf("风向:%s%n", realtime.getStr("direct"));
            System.out.printf("风力:%s%n", realtime.getStr("power"));
            System.out.printf("空气质量:%s%n", realtime.getStr("aqi"));
            //保存今日天气
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
            WeatherEntity weatherEntity = new WeatherEntity();
            weatherEntity.setCity(result.getStr("city"));
            weatherEntity.setDate(simpleDateFormat.format(new Date()));
            weatherEntity.setTemperature(realtime.getStr("temperature"));
            weatherEntity.setHumidity(realtime.getStr("humidity"));
            weatherEntity.setInfo(realtime.getStr("info"));
            weatherEntity.setDirect(realtime.getStr("direct"));
            weatherEntity.setPower(realtime.getStr("power"));
            weatherEntity.setAqi(realtime.getStr("aqi"));
            LocalDateTime now = LocalDateTime.now();
            weatherEntity.setCreateTime(now);
            thirdLearnService.save(weatherEntity);
            log.info("===============>今日天气保存成功");

            //批量保存使用
            ArrayList<WeatherEntity> weatherEntityArrayList= new ArrayList<>();
            //未来五天天气
            System.out.println("=============未来五天天气===============");
            JSONArray future = result.getJSONArray("future");
            for (int i = 0; i < future.size(); i++) {
                //未来日期
                String date = future.getJSONObject(i).getStr("date");
                //未来某天温度
                String temperature = future.getJSONObject(i).getStr("temperature");
                //未来某天天气
                String weather = future.getJSONObject(i).getStr("weather");
                //未来某天风向
                String direct = future.getJSONObject(i).getStr("direct");
                System.out.printf("%s %s 天气%n", date, result.getStr("city"));
                System.out.printf("温度:%s%n", temperature);
                System.out.printf("天气:%s%n", weather);
                System.out.printf("风向:%s%n", direct);
                System.out.println();

                //保存未来天气
                WeatherEntity futureWeather = new WeatherEntity();
                futureWeather.setCity(result.getStr("city"));
                futureWeather.setDate(date);
                futureWeather.setTemperature(temperature);
                futureWeather.setInfo(weather);
                futureWeather.setDirect(direct);
                LocalDateTime nowTime = LocalDateTime.now();
                futureWeather.setCreateTime(nowTime);
                weatherEntityArrayList.add(futureWeather);
            }
//            System.out.printf("", 1 / 0);
            //批量保存
            thirdLearnService.saveBatch(weatherEntityArrayList);
            log.info("===============>未来天气保存成功");

            //异步存入日志表-查询成功
//            service.submit(new dealTask(weatherReqVo.getCityName(), CommonConstant.REQUEST_SUCCESS));
            //同步插入日志 -查询成功,这个是为了验证事务的有效性
            thirdRequestLogService.insertRequestLog(weatherReqVo.getCityName(), CommonConstant.REQUEST_FAIL);
        } else {
            System.out.println("接口方法错误getWeatherMethod()调用接口失败,失败原因" + jsonObject.getStr("reason"));
            //异步存入轨迹表-查询成功
//            service.submit(new dealTask(weatherReqVo.getCityName(), CommonConstant.REQUEST_FAIL));

            throw new BusiException("查询失败!请重新进行查询。", CommonConstant.ErrorCode.BUSI_ERROR);  //因为这种异常 我做了全局的异常捕获,所以报错的时候,会报出具体的原因,这种的肯定更好
//            throw new RuntimeException("查询失败!");  // 因为对这种异常没有做全局的捕获,所以请求完报错后直接提示错误信息 500,没有提示具体的原因 "status": 500, "error": "Internal Server Error",



        }

    }

//============================================
// get方式的http请求 (方式一,使用原始HttpURLConnection进行请求 )

//===========================================

    /**
     * get方式的http请求 (方式一,使用原始HttpURLConnection进行请求 )
     *
     * @param apiUrl      请求入参
     * @param queryParams 入参参数(查询天气的参数)
     * @return
     */
    private String doGet(String apiUrl, String queryParams) {
        HttpURLConnection httpURLConnection = null;
        InputStream inputStream = null;
        BufferedReader bufferedReader = null;
        String result = null;  //字符串结果
        try {
            //将格式化好的后半部分(URL的参数部分)和url前半部分拼接起来 形成 http://apis.juhe.cn/simpleWeather/query?cityName=石家庄&XXX=YYY
            String berUrl = new StringBuffer(apiUrl).append("?").append(queryParams).toString();
            log.info("========> 完整Url {}", berUrl);
            //通过拼接好的URL创建爱你URL对象
            URL url = new URL(berUrl);
            //通过完整URL打开Connection连接;
            httpURLConnection = (HttpURLConnection) url.openConnection();
            //设置连接方式:get
            httpURLConnection.setRequestMethod("GET");
            // 设置连接主机服务器的超时时间:15000毫秒
            httpURLConnection.setConnectTimeout(5000);
            // 设置读取远程返回的数据时间:6000毫秒
            httpURLConnection.setReadTimeout(6000);
            // 设置请求头
            httpURLConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
            // 发送请求
            httpURLConnection.connect();
            // 通过connection连接,获取输入流 ,也就是获取响应参数
            if (200 == httpURLConnection.getResponseCode()) {
                //创建输入流
                inputStream = httpURLConnection.getInputStream();
                // 封装输入流,并指定字符集
                bufferedReader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
                // 创建StringBuilder,存放数据
                StringBuilder sbf = new StringBuilder();
                String temp;
                while ((temp = bufferedReader.readLine()) != null) {
                    sbf.append(temp);
                    sbf.append(System.getProperty("line.separator"));
                }
                result = sbf.toString();
            } else {
                result = "返回响应码不是200!";
            }


        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //关闭资源
            //按顺序关闭,1.先关闭输入流读取流
            if (null != bufferedReader) {
                try {
                    bufferedReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            //2.关闭输入流
            if (null != inputStream) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }
            if (httpURLConnection != null) {
                httpURLConnection.disconnect();// 关闭远程连接
            }

        }
        return result;
    }




//======================================
//post方式的http请求 (方式一,使用原始HttpURLConnection进行请求 )

//=======================================
    /**
     * post方式的http请求 (方式一,使用原始HttpURLConnection进行请求 )
     *
     * @param apiUrl      请求url
     * @param queryParams 查询参数
     * @return
     */
    private String doPost(String apiUrl, String queryParams) {
        HttpURLConnection httpURLConnection = null;
        OutputStream outputStream = null;
        InputStream inputStream = null;
        BufferedReader bufferedReader = null;
        String result = null;
        try {
            //通过url创建 url
            URL url = new URL(apiUrl);
            httpURLConnection = (HttpURLConnection) url.openConnection();
            //设置请求方式
            httpURLConnection.setRequestMethod("POST");
            // 设置连接主机服务器超时时间:15000毫秒
            httpURLConnection.setConnectTimeout(5000);
            // 设置读取主机服务器返回数据超时时间:60000毫秒
            httpURLConnection.setReadTimeout(6000);
            // 默认值为:false,当向远程服务器传送数据/写数据时,需要设置为true
            httpURLConnection.setDoOutput(true);
            // 设置请求参数类型
            httpURLConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
            // 通过连接对象获取一个输出流
            outputStream = httpURLConnection.getOutputStream();
            // 通过输出流对象将参数写出去/传输出去,它是通过字节数组写出的 ,这里就是实际调用第三方接口的位置
            //特别注意 post这里的参数也是,应该是 ?A=a&B=b的形式来传 ,如 param1=value1&param2=value2,这里和get请求是一样的
            // 上面说法的具体原因,课件博客 https://www.cnblogs.com/isme-zjh/p/18216094
            outputStream.write(queryParams.getBytes());
            // 通过连接对象获取一个输入流,向远程读取
            if (httpURLConnection.getResponseCode() == 200) {
                //通过连接创建输入流,其实就是调用三方接口的响应结果
                inputStream = httpURLConnection.getInputStream();
                //对响应结果使用包装流进行包装
                bufferedReader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
                StringBuilder stringBuilder = new StringBuilder();
                String temp = null;
                //包装后的数据流进行读取
                while ((temp = bufferedReader.readLine()) != null) {
                    stringBuilder.append(temp);
                    stringBuilder.append(System.getProperty("line.separator"));
                }
                //调用toString方法
                result = stringBuilder.toString();

            }

        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //按照顺序关闭资源,按照从里到外的顺序关闭流
            if (null != bufferedReader) {
                try {
                    bufferedReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            if (null != inputStream) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }

            if (null != outputStream) {
                try {
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }

            if (httpURLConnection != null) {
                httpURLConnection.disconnect();
            }
        }

        return result;

    }


//===================================
// hutool工具:httpRequest.post()访问第三方接口
//===================================


    /**
     * hutool工具:httpRequest.post()访问第三方接口
     *
     * @param apiUrl 请求url
     * @param city   城市
     * @param key    API_KEY
     * @return
     */
    public String httpRequestPost(String apiUrl, String city, String key) {
        log.info("调用url{} ", apiUrl);
        // 创建HttpRequest对象,指定要访问的URL POST请求,请求头格式 是典型的表单提交格式:application/x-www-form-urlencoded,这种post请求的传参格式如下
        HttpRequest request = HttpRequest.post(apiUrl);
        request.header("Content-Type", "application/x-www-form-urlencoded");
        // application/x-www-form-urlencoded  post 方式 且Content-Type 是表单格式时,使用form()方法添加表单参数
        request.form("city", city)
                .form("key", environment.getProperty("API_KEY"));

/*
      //post请求
        这个的请求头是application/json类型的,入参传参是设置到request.body(jsonBody) 中,具体见注解这一部分
        // 添加请求头
        request.header("Content-Type", "application/json");
        // 拼接请求对象
        OutBoundNotice outBoundNotice = assemblyParamObject(token, outboundNoticeOrderResult.getData()); //此方法拼接初始化了一个对象
        String jsonBody = JSONUtil.toJsonStr(outBoundNotice);  //zjh 或者通过outBoundNotice.toString()方法也是可以的 转换成String
        log.info("发送出库通知单接口入参对象:{}", jsonBody);
        request.body(jsonBody);
        HttpResponse response = request.execute();


        特别注意!!:上面的JSONObject jsonObject = new JSONObject();
        jsonObject中的值的样式:类似下面: 其实也是键值对,所以可以用JSONUtil.toJsonStr(outBoundNotice); 转换成String的时候,也是可以通过outBoundNotice.toString() 来转换成可以作为入参的String的
        jsonObject.put("applyId", intApplyInfo.getApplyId());
        jsonObject.put("departId", intApplyInfo.getDepartmentId());
        jsonObject.put("departName", intApplyInfo.getDepartName());
        jsonObject.put("userId", intApplyInfo.getOwnerId());
        jsonObject.put("userName", intApplyInfo.getUserName());
        jsonObject.put("busiId", intApplyInfo.getBusiId());
        jsonObject.put("busiType", intApplyInfo.getBusiType());
        jsonObject.put("sts", sts);
        jsonObject.put("procInstId", requestId);

*/

        // 发送请求并获取响应
        HttpResponse response = request.execute();
        log.info("响应结果JSONUtil.toJsonStr(response.body()) \n{} ", JSONUtil.toJsonStr(response.body()));
        log.info("响应结果JSONUtil.toJsonStr(response)\n {} ", JSONUtil.toJsonStr(response));
        JSONObject jsonObject = JSONUtil.parseObj(response);

        String responseCode = jsonObject.getStr("error_code");
        if (responseCode == CommonConstant.SUCCESS_CODE) {
            String result = jsonObject.getStr("result");
            JSONObject resultObj = JSONUtil.parseObj(result);
            String cityObj = resultObj.getStr("city");
            String realtime = resultObj.getStr("realtime");
            JSONObject realtimeJson = JSONUtil.parseObj(realtime);
            //温度
            String temperature = realtimeJson.getStr("temperature");
            //湿度
            String humidity = realtimeJson.getStr("humidity");
            //天气
            String info = realtimeJson.getStr("info");
            //风向
            String direct = realtimeJson.getStr("direct");
            //风力
            String power = realtimeJson.getStr("power");
            //空气质量指数
            String aqi = realtimeJson.getStr("aqi");
            System.out.printf("===httpRequestPost==%s温度%s%n===", cityObj, temperature);

        }
        return JSONUtil.toJsonStr(response.body());

    }


//===================================
//RestTemplate 进行第三方接口调用
//===================================


    /**
     * RestTemplate 进行第三方接口调用
     * get请求:三种方式 :
     *             1、restTemplate.getForObject(allUrl, String.class, params)  url中的参数用占位符,并且将参数通过map的方式放到getForObject()方法参数中 这种调用成功
     *             2、restTemplate.getForObject(allUrl, String.class) 直接将参数拼接到url里面,不在   getForObject 中传参。这种调用失败
     *             3、restTemplate.exchange(allUrl,
     *                 HttpMethod.GET,
     *                 request,
     *                 String.class,
     *                 params)  这种请求是带请求头的,url传参和第一种的是一样的,也是url是有占位符,最后一个参数是“参数Map”
     *
     * @param apiUrl
     * @param queryParams
     * @return
     */
    public String springRestTemplate(String apiUrl, Map params, String queryParams) {


        Map<String, Object> map;
        RestTemplate restTemplate = new RestTemplate();
        //第一种调用通方式-start,调用restTemplate.getForObject   https://www.cnblogs.com/windyWu/p/16872871.html 见博客  url中的参数用占位符,并且将参数通过map的方式放到getForObject()方法参数中,
       //这种是可以调用通过,并返回正确参数
        String allUrl = apiUrl + "?city={city}&key={key}";
        log.info("========> 完整allUrl {}", allUrl);
        String resultObject = restTemplate.getForObject(allUrl, String.class, params);
        //第一种:-end


//        //第二种:-start调用restTemplate.getForObject ,直接将参数拼接到url里面,不在   getForObject 中传参。
          这种调用失败,响应: {"result":null,"reason":"暂不支持该城市","error_code":207301},所以这种方法目前是还未调通的;初步判应该是传参格式有问题,导致请求没有被接受。
//        String allUrl = new StringBuffer(apiUrl).append("?").append(queryParams).toString();
//        log.info("========> 完整allUrl {}", allUrl);
//        String resultObject = restTemplate.getForObject(allUrl, String.class);
        //第二种:-end

     //前两种调用方法是公共解析代码
        //第一种和第二种请求方式 都是调用的 restTemplate.getForObject 响应结果是用下面这两行解析
        map = JSONUtil.parseObj(resultObject);  //需要知道的是 为什么这里既能用Map<String, Object> map来接受,也能用JSONObject jsonObject来接受,
        // 因为JSONObject其实就是相当于前面的map结构,里面存储的也是键值对,可以看上面的方法  httpRequestPost() 中的注释部分put方法。也可以很明显的看出JSONObject结构类似于map
        JSONObject jsonObject = JSONUtil.parseObj(resultObject);



/*
        //第三种调用方式-start,带请求头的,
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        // build the request
        HttpEntity request = new HttpEntity(headers);
        // make an HTTP GET request with headers
        ResponseEntity<String> resultObject1 = restTemplate.exchange(
                allUrl,
                HttpMethod.GET,
                request,
                String.class,
                params
        );
       // 第三种方式 调用的 restTemplate.exchange 响应结果是用下面这种方式进行解析代码,需要拿到响应结果中的getBody,才是用前两种方式得到的响应体
        map = JSONUtil.parseObj(resultObject1.getBody());
        HttpStatus statusCode = resultObject1.getStatusCode();
        int statusCodeValue = resultObject1.getStatusCodeValue();
        HttpHeaders headers1 = resultObject1.getHeaders();
        //第三种-end  */





        //下面代码是三种请求方式公用的;
        int code = Integer.parseInt(map.get("error_code").toString());
        if (code == 0) {
            System.out.println("响应成功");
            log.info("=================>jsonObject \n {}", JSONUtil.toJsonStr(jsonObject));  //将JSONObject 对象转换为 String
            log.info("=================>map \n {}", map);//这行的输出和上面一行的输出是完全一样的(已经验证过了)
            //取值练习
            JSONObject result = jsonObject.getJSONObject("result");
            //获取地市
            String city = result.getStr("city");  //特别注意,如果get之后是一个对象,则用jsonObject.getJSONObject,如果是得到一个基本类型,则是使用JSONObject对象的getStr()、getInt()等方法
            //获取今天温度
            JSONObject realtime = result.getJSONObject("realtime");
            String temperature = realtime.getStr("temperature");
            System.out.printf("===springRestTemplate==%s温度%s%n===", city, temperature);
        }

        return JSONUtil.toJsonStr(resultObject);
    }


    //异步任务
    public class dealTask implements Runnable{
        //(String cityName,String code)
        private String  cityName;
        private String  code;

        public dealTask(String cityName, String code) {
            this.cityName = cityName;
            this.code = code;
        }

        //业务处理
        @Override
        public void run() {
            log.info("===异步执行开始===");
            try {
                //10s后存储日志表
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            insertRequestLog(cityName,code);
            log.info("========异步执行成功=====");

        }
    }

    /**
     * 记录请求日志
     * @param cityName
     * @param code
     */
    public void insertRequestLog(String cityName, String code) {
        log.info("=========>insertRequestLog()方法");
        //报错模拟 ,会导致事务失效!!
        double a =1/0;
        ThirdRequestLog thirdRequestLog = new ThirdRequestLog();
        thirdRequestLog.setCity(cityName);
        thirdRequestLog.setRequestCode(code);
        LocalDateTime now = LocalDateTime.now();
        thirdRequestLog.setCreateTime(now);
        thirdRequestLogService.save(thirdRequestLog);
        log.info("=============>insertRequestLog()日志记录成功");
    }

    /**
     * URL入参参数 编码
     *
     * @param params
     * @return
     */
    private String urlencode(Map<String, ?> params) {
        StringBuilder stringBuilder = new StringBuilder();
        for (Map.Entry<String, ?> stringEntry : params.entrySet()) {
            try {
                stringBuilder.append(stringEntry.getKey()).append("=").append(URLEncoder.encode(stringEntry.getValue() + "", "UTF-8")).append("&");
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        }
        String result = stringBuilder.toString();
        //截取 从第一个字符到最后一个&,之间的字符,用于后面拼接到url后面当入参
        result = result.substring(0, result.lastIndexOf("&"));
        return result;
    }


}

有颜色标注完整版代码

 ,突出重点

package com.atguigu.gulimall.coupon.learn.controller;

import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.atguigu.gulimall.coupon.entity.ThirdRequestLog;
import com.atguigu.gulimall.coupon.entity.WeatherEntity;
import com.atguigu.gulimall.coupon.learn.annotation.ParamValided;
import com.atguigu.gulimall.coupon.learn.entity.CommonConstant;
import com.atguigu.gulimall.coupon.learn.entity.vo.WeatherReqVo;
import com.atguigu.gulimall.coupon.learn.myexception.BusiException;
import com.atguigu.gulimall.coupon.learn.util.SpringBeanUtil;
import com.atguigu.gulimall.coupon.service.ThirdLearnService;
import com.atguigu.gulimall.coupon.service.ThirdRequestLogService;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**httpThirdCallController
 * 调用三方接口 学习
 *
 * @author: jd
 * @create: 2024-05-27
 */
@RestController
@Slf4j
@RequestMapping("/coupon/thirdLearn")
public class HttpThirdCallController {

    @Autowired
    private Environment environment;

    @Autowired
    CommonConstant commonConstant;

    @Autowired
    ThirdLearnService thirdLearnService;

    @Autowired
    public ThirdRequestLogService thirdRequestLogService;

    //
    private ExecutorService service = Executors.newCachedThreadPool();

    //0.模拟数据库中的数据
    private static Map<Integer, WeatherReqVo> weatherReqVoMap = null;

    static {
        weatherReqVoMap = new HashMap<Integer, WeatherReqVo>();//创建一个天气表,存储着现有的,可以查询天气的地市
        //初始化表数据
        weatherReqVoMap.put(10001, new WeatherReqVo(10001L, "石家庄"));
        weatherReqVoMap.put(10002, new WeatherReqVo(10002L, "北京"));
        weatherReqVoMap.put(10003, new WeatherReqVo(10003L, "武汉"));
        weatherReqVoMap.put(10004, new WeatherReqVo(10004L, "邯郸"));
        weatherReqVoMap.put(10005, new WeatherReqVo(10005L, "天津"));
        weatherReqVoMap.put(10006, new WeatherReqVo(10006L, "郑州"));
    }
    // 数据来源: 原文链接:https://blog.csdn.net/QQQZSJ/article/details/109116120

    @ApiOperation("第三方调用练习")
    @GetMapping("/thirdCall")
    @ParamValided
    public String getWeather(WeatherReqVo weatherReqVo) {
        //校验所传地址在数据库中是否存在
        if (!weatherReqVoMap.containsValue(new WeatherReqVo(weatherReqVo.getId(), weatherReqVo.getCityName()))) {
            //这里抛出的是自定义的异常,而且这个自定义的异常是被自定义的异常捕捉器给捕捉
            throw new BusiException("所查询地市不在数据库可查询地市列表中", CommonConstant.ErrorCode.NO_VALUE_ERROR);
        }
        //调用三方接口,获取天气

//        ()AopContext.currentProxy()
        //由于当@Transactional(rollbackFor = Exception.class) 加在同类下面的自有方法的时候,直接调用的话,会导致事务失效,
        // 这种情况下要对在会被外界调用的方法上加上这个注解,因为被外界调用的方法会被代理对象所调用,此时事务会生效。如果有异常发生,则会回滚。如果非要将@Transactional 注解加到下面内部调用的方法里面的话
        //并且想要这个注解生效的话,则需要 使用Spring的IOC容器中去取得bean代理对象,通过这个代理对象去调用内部的方法。此时事务才会生效。
//        getWeatherMethod(weatherReqVo); //直接调用的话,会导致事务失效, 此时可以通过


        HttpThirdCallController httpThirdCallController = (HttpThirdCallController)SpringBeanUtil.getBean("httpThirdCallController");
        httpThirdCallController.getWeatherMethod(weatherReqVo);
        return "查询成功";

    }

    /**
     * 调用三方天气查询接口
     *
     * @param weatherReqVo
     */
    @Transactional(rollbackFor = Exception.class)
    public void getWeatherMethod(WeatherReqVo weatherReqVo) {
        //组合参数
        HashMap<String, Object> params = new HashMap<>();
        params.put("city", weatherReqVo.getCityName());
        //获取API_KEY
        params.put("key", environment.getProperty("API_KEY"));  // 另外一种获取方式  CommonConstant.API_KEY
        //拼接参数(包含参数编码)
        String queryParams = urlencode(params);
        log.info("===========>{}", queryParams);

        //原始Connection get请求方式
//        String response = doGet(CommonConstant.API_URL, queryParams);
        //原始Connection post请求方式
//        String response = doPost(CommonConstant.API_URL, queryParams);

        //hutool 工具 HttpRequest.post() post请求方式
//        String response = httpRequestPost(CommonConstant.API_URL, weatherReqVo.getCityName(), environment.getProperty("API_KEY"));

        //springRestTemplate 请求第三方接口 post方式
        String response = springRestTemplate(CommonConstant.API_URL, params, queryParams);

        log.info("===========>JSONUtil.parseObj(response)\n{}", JSONUtil.parseObj(response));

        JSONObject jsonObject = JSONUtil.parseObj(response);
        int error_code = jsonObject.getInt("error_code");
        if (error_code == 0) {
            System.out.println("调用接口成功");
            JSONObject result1 = JSONUtil.parseObj(jsonObject.getStr("result"));
            JSONObject result = jsonObject.getJSONObject("result");
            log.info("========>JSONUtil.parseObj(jsonObject.getStr(\"result\"))\n  {}", result1);
            System.out.println("======间隔======");
            log.info("========>jsonObject.getJSONObject(\"result\")\n {}", result);
            JSONObject realtime = result.getJSONObject("realtime");
            System.out.println("=============今日天气===============");
            //查询地市
            System.out.printf("城市:%s%n", result.getStr("city"));
            //今日天气
            System.out.printf("天气:%s%n", realtime.getStr("info"));
            System.out.printf("温度:%s%n", realtime.getStr("temperature"));
            System.out.printf("湿度:%s%n", realtime.getStr("humidity"));
            System.out.printf("风向:%s%n", realtime.getStr("direct"));
            System.out.printf("风力:%s%n", realtime.getStr("power"));
            System.out.printf("空气质量:%s%n", realtime.getStr("aqi"));
            //保存今日天气
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
            WeatherEntity weatherEntity = new WeatherEntity();
            weatherEntity.setCity(result.getStr("city"));
            weatherEntity.setDate(simpleDateFormat.format(new Date()));
            weatherEntity.setTemperature(realtime.getStr("temperature"));
            weatherEntity.setHumidity(realtime.getStr("humidity"));
            weatherEntity.setInfo(realtime.getStr("info"));
            weatherEntity.setDirect(realtime.getStr("direct"));
            weatherEntity.setPower(realtime.getStr("power"));
            weatherEntity.setAqi(realtime.getStr("aqi"));
            LocalDateTime now = LocalDateTime.now();
            weatherEntity.setCreateTime(now);
            thirdLearnService.save(weatherEntity);
            log.info("===============>今日天气保存成功");

            //批量保存使用
            ArrayList<WeatherEntity> weatherEntityArrayList= new ArrayList<>();
            //未来五天天气
            System.out.println("=============未来五天天气===============");
            JSONArray future = result.getJSONArray("future");
            for (int i = 0; i < future.size(); i++) {
                //未来日期
                String date = future.getJSONObject(i).getStr("date");
                //未来某天温度
                String temperature = future.getJSONObject(i).getStr("temperature");
                //未来某天天气
                String weather = future.getJSONObject(i).getStr("weather");
                //未来某天风向
                String direct = future.getJSONObject(i).getStr("direct");
                System.out.printf("%s %s 天气%n", date, result.getStr("city"));
                System.out.printf("温度:%s%n", temperature);
                System.out.printf("天气:%s%n", weather);
                System.out.printf("风向:%s%n", direct);
                System.out.println();

                //保存未来天气
                WeatherEntity futureWeather = new WeatherEntity();
                futureWeather.setCity(result.getStr("city"));
                futureWeather.setDate(date);
                futureWeather.setTemperature(temperature);
                futureWeather.setInfo(weather);
                futureWeather.setDirect(direct);
                LocalDateTime nowTime = LocalDateTime.now();
                futureWeather.setCreateTime(nowTime);
                weatherEntityArrayList.add(futureWeather);
            }
//            System.out.printf("", 1 / 0);
            //批量保存
            thirdLearnService.saveBatch(weatherEntityArrayList);
            log.info("===============>未来天气保存成功");

            //异步存入日志表-查询成功
//            service.submit(new dealTask(weatherReqVo.getCityName(), CommonConstant.REQUEST_SUCCESS));
            //同步插入日志 -查询成功,这个是为了验证事务的有效性
            thirdRequestLogService.insertRequestLog(weatherReqVo.getCityName(), CommonConstant.REQUEST_FAIL);
        } else {
            System.out.println("接口方法错误getWeatherMethod()调用接口失败,失败原因" + jsonObject.getStr("reason"));
            //异步存入轨迹表-查询成功
//            service.submit(new dealTask(weatherReqVo.getCityName(), CommonConstant.REQUEST_FAIL));

            throw new BusiException("查询失败!请重新进行查询。", CommonConstant.ErrorCode.BUSI_ERROR);  //因为这种异常 我做了全局的异常捕获,所以报错的时候,会报出具体的原因,这种的肯定更好
//            throw new RuntimeException("查询失败!");  // 因为对这种异常没有做全局的捕获,所以请求完报错后直接提示错误信息 500,没有提示具体的原因 "status": 500, "error": "Internal Server Error",



        }

    }

    /**
     * get方式的http请求 (方式一,使用原始HttpURLConnection进行请求 )
     *
     * @param apiUrl      请求入参
     * @param queryParams 入参参数(查询天气的参数)
     * @return
     */
    private String doGet(String apiUrl, String queryParams) {
        HttpURLConnection httpURLConnection = null;
        InputStream inputStream = null;
        BufferedReader bufferedReader = null;
        String result = null;  //字符串结果
        try {
            //将格式化好的后半部分(URL的参数部分)和url前半部分拼接起来 形成 http://apis.juhe.cn/simpleWeather/query?cityName=石家庄&XXX=YYY
            String berUrl = new StringBuffer(apiUrl).append("?").append(queryParams).toString();
            log.info("========> 完整Url {}", berUrl);
            //通过拼接好的URL创建爱你URL对象
            URL url = new URL(berUrl);
            //通过完整URL打开Connection连接;
            httpURLConnection = (HttpURLConnection) url.openConnection();
            //设置连接方式:get
            httpURLConnection.setRequestMethod("GET");
            // 设置连接主机服务器的超时时间:15000毫秒
            httpURLConnection.setConnectTimeout(5000);
            // 设置读取远程返回的数据时间:6000毫秒
            httpURLConnection.setReadTimeout(6000);
            // 设置请求头
            httpURLConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
            // 发送请求
            httpURLConnection.connect();
            // 通过connection连接,获取输入流 ,也就是获取响应参数
            if (200 == httpURLConnection.getResponseCode()) {
                //创建输入流
                inputStream = httpURLConnection.getInputStream();
                // 封装输入流,并指定字符集
                bufferedReader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
                // 创建StringBuilder,存放数据
                StringBuilder sbf = new StringBuilder();
                String temp;
                while ((temp = bufferedReader.readLine()) != null) {
                    sbf.append(temp);
                    sbf.append(System.getProperty("line.separator"));
                }
                result = sbf.toString();
            } else {
                result = "返回响应码不是200!";
            }


        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //关闭资源
            //按顺序关闭,1.先关闭输入流读取流
            if (null != bufferedReader) {
                try {
                    bufferedReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            //2.关闭输入流
            if (null != inputStream) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }
            if (httpURLConnection != null) {
                httpURLConnection.disconnect();// 关闭远程连接
            }

        }
        return result;
    }

    /**
     * post方式的http请求 (方式一,使用原始HttpURLConnection进行请求 )
     *
     * @param apiUrl      请求url
     * @param queryParams 查询参数
     * @return
     */
    private String doPost(String apiUrl, String queryParams) {
        HttpURLConnection httpURLConnection = null;
        OutputStream outputStream = null;
        InputStream inputStream = null;
        BufferedReader bufferedReader = null;
        String result = null;
        try {
            //通过url创建 url
            URL url = new URL(apiUrl);
            httpURLConnection = (HttpURLConnection) url.openConnection();
            //设置请求方式
            httpURLConnection.setRequestMethod("POST");
            // 设置连接主机服务器超时时间:15000毫秒
            httpURLConnection.setConnectTimeout(5000);
            // 设置读取主机服务器返回数据超时时间:60000毫秒
            httpURLConnection.setReadTimeout(6000);
            // 默认值为:false,当向远程服务器传送数据/写数据时,需要设置为true
            httpURLConnection.setDoOutput(true);
            // 设置请求参数类型
            httpURLConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
            // 通过连接对象获取一个输出流
            outputStream = httpURLConnection.getOutputStream();
            // 通过输出流对象将参数写出去/传输出去,它是通过字节数组写出的 ,这里就是实际调用第三方接口的位置
            //特别注意 post这里的参数也是,应该是 ?A=a&B=b的形式来传 ,如 param1=value1&param2=value2,这里和get请求是一样的
            // 上面说法的具体原因,课件博客 https://www.cnblogs.com/isme-zjh/p/18216094
            outputStream.write(queryParams.getBytes());
            // 通过连接对象获取一个输入流,向远程读取
            if (httpURLConnection.getResponseCode() == 200) {
                //通过连接创建输入流,其实就是调用三方接口的响应结果
                inputStream = httpURLConnection.getInputStream();
                //对响应结果使用包装流进行包装
                bufferedReader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
                StringBuilder stringBuilder = new StringBuilder();
                String temp = null;
                //包装后的数据流进行读取
                while ((temp = bufferedReader.readLine()) != null) {
                    stringBuilder.append(temp);
                    stringBuilder.append(System.getProperty("line.separator"));
                }
                //调用toString方法
                result = stringBuilder.toString();

            }

        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //按照顺序关闭资源,按照从里到外的顺序关闭流
            if (null != bufferedReader) {
                try {
                    bufferedReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            if (null != inputStream) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }

            if (null != outputStream) {
                try {
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }

            if (httpURLConnection != null) {
                httpURLConnection.disconnect();
            }
        }

        return result;

    }


    /**
     * hutool工具:httpRequest.post()访问第三方接口
     *
     * @param apiUrl 请求url
     * @param city   城市
     * @param key    API_KEY
     * @return
     */
    public String httpRequestPost(String apiUrl, String city, String key) {
        log.info("调用url{} ", apiUrl);
        // 创建HttpRequest对象,指定要访问的URL POST请求,请求头格式 是典型的表单提交格式:application/x-www-form-urlencoded,这种post请求的传参格式如下
        HttpRequest request = HttpRequest.post(apiUrl);
        request.header("Content-Type", "application/x-www-form-urlencoded");
        // application/x-www-form-urlencoded  post 方式 且Content-Type 是表单格式时,使用form()方法添加表单参数
        request.form("city", city)
                .form("key", environment.getProperty("API_KEY"));

/*
      //post请求
        这个的请求头是application/json类型的,入参传参是设置到request.body(jsonBody) 中,具体见注解这一部分
        // 添加请求头
        request.header("Content-Type", "application/json");
        // 拼接请求对象
        OutBoundNotice outBoundNotice = assemblyParamObject(token, outboundNoticeOrderResult.getData()); //此方法拼接初始化了一个对象
        String jsonBody = JSONUtil.toJsonStr(outBoundNotice);  //zjh 或者通过outBoundNotice.toString()方法也是可以的 转换成String
        log.info("发送出库通知单接口入参对象:{}", jsonBody);
        request.body(jsonBody);
        HttpResponse response = request.execute();


//上面整块代码也可以这样写
/**
String url="放入自己的url地址";
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("username", "admin");//用户名
        jsonObject.put("password","123456" );//密码
 
 
        String a=HttpRequest.post(url).header("Content-Type","application/json").body(jsonObject.toJSONString()).execute().body();

                        
原文链接:https://blog.csdn.net/m0_50644184/article/details/120351115
**/


        特别注意!!:上面的JSONObject jsonObject = new JSONObject();
        jsonObject中的值的样式:类似下面: 其实也是键值对,所以可以用JSONUtil.toJsonStr(outBoundNotice); 转换成String的时候,也是可以通过outBoundNotice.toString() 来转换成可以作为入参的String的
        jsonObject.put("applyId", intApplyInfo.getApplyId());
        jsonObject.put("departId", intApplyInfo.getDepartmentId());
        jsonObject.put("departName", intApplyInfo.getDepartName());
        jsonObject.put("userId", intApplyInfo.getOwnerId());
        jsonObject.put("userName", intApplyInfo.getUserName());
        jsonObject.put("busiId", intApplyInfo.getBusiId());
        jsonObject.put("busiType", intApplyInfo.getBusiType());
        jsonObject.put("sts", sts);
        jsonObject.put("procInstId", requestId);

*/

        // 发送请求并获取响应
        HttpResponse response = request.execute();
        log.info("响应结果JSONUtil.toJsonStr(response.body()) \n{} ", JSONUtil.toJsonStr(response.body()));
        log.info("响应结果JSONUtil.toJsonStr(response)\n {} ", JSONUtil.toJsonStr(response));
        JSONObject jsonObject = JSONUtil.parseObj(response);

        String responseCode = jsonObject.getStr("error_code");
        if (responseCode == CommonConstant.SUCCESS_CODE) {
            String result = jsonObject.getStr("result");
            JSONObject resultObj = JSONUtil.parseObj(result);
            String cityObj = resultObj.getStr("city");
            String realtime = resultObj.getStr("realtime");
            JSONObject realtimeJson = JSONUtil.parseObj(realtime);
            //温度
            String temperature = realtimeJson.getStr("temperature");
            //湿度
            String humidity = realtimeJson.getStr("humidity");
            //天气
            String info = realtimeJson.getStr("info");
            //风向
            String direct = realtimeJson.getStr("direct");
            //风力
            String power = realtimeJson.getStr("power");
            //空气质量指数
            String aqi = realtimeJson.getStr("aqi");
            System.out.printf("===httpRequestPost==%s温度%s%n===", cityObj, temperature);

        }
        return JSONUtil.toJsonStr(response.body());

    }


    /**
     * RestTemplate 进行第三方接口调用
     * get请求:三种方式 :
     *             1、restTemplate.getForObject(allUrl, String.class, params)  url中的参数用占位符,并且将参数通过map的方式放到getForObject()方法参数中 这种调用成功
     *             2、restTemplate.getForObject(allUrl, String.class) 直接将参数拼接到url里面,不在   getForObject 中传参。这种调用失败
     *             3、restTemplate.exchange(allUrl,
     *                 HttpMethod.GET,
     *                 request,
     *                 String.class,
     *                 params)  这种请求是带请求头的,url传参和第一种的是一样的,也是url是有占位符,最后一个参数是“参数Map”
     *
     * @param apiUrl
     * @param queryParams
     * @return
     */
    public String springRestTemplate(String apiUrl, Map params, String queryParams) {


        Map<String, Object> map;
        RestTemplate restTemplate = new RestTemplate();
        //第一种调用通方式-start,调用restTemplate.getForObject   https://www.cnblogs.com/windyWu/p/16872871.html 见博客  url中的参数用占位符,并且将参数通过map的方式放到getForObject()方法参数中,这种是可以调用通过,并返回正确参数
        String allUrl = apiUrl + "?city={city}&key={key}";
        log.info("========> 完整allUrl {}", allUrl);
        String resultObject = restTemplate.getForObject(allUrl, String.class, params);
        //第一种:-end

//        //第二种:-start调用restTemplate.getForObject ,直接将参数拼接到url里面,不在   getForObject 中传参。这种调用失败,响应: {"result":null,"reason":"暂不支持该城市","error_code":207301}
//        String allUrl = new StringBuffer(apiUrl).append("?").append(queryParams).toString();
//        log.info("========> 完整allUrl {}", allUrl);
//        String resultObject = restTemplate.getForObject(allUrl, String.class);
        //第二种:-end

        //第一种和第二种请求方式 都是调用的 restTemplate.getForObject 响应结果是用下面这两行解析
        map = JSONUtil.parseObj(resultObject);  //需要知道的是 为什么这里既能用Map<String, Object> map来接受,也能用JSONObject jsonObject来接受,
        // 因为JSONObject其实就是相当于前面的map结构,里面存储的也是键值对,可以看上面的方法  httpRequestPost() 中的注释部分put方法。也可以很明显的看出JSONObject结构类似于map
        JSONObject jsonObject = JSONUtil.parseObj(resultObject);

/*
        //第三种调用方式-start,带请求头的,
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        // build the request
        HttpEntity request = new HttpEntity(headers);
        // make an HTTP GET request with headers
        ResponseEntity<String> resultObject1 = restTemplate.exchange(
                allUrl,
                HttpMethod.GET,
                request,
                String.class,
                params
        );
       // 第三种方式 调用的 restTemplate.exchange 响应结果是用下面这种方式进行解析,需要拿到响应结果中的getBody,才是用前两种方式得到的响应体
        map = JSONUtil.parseObj(resultObject1.getBody());
        HttpStatus statusCode = resultObject1.getStatusCode();
        int statusCodeValue = resultObject1.getStatusCodeValue();
        HttpHeaders headers1 = resultObject1.getHeaders();
        //第三种-end  */



        //下面代码是三种请求方式公用的;
        int code = Integer.parseInt(map.get("error_code").toString());
        if (code == 0) {
            System.out.println("响应成功");
            log.info("=================>jsonObject \n {}", JSONUtil.toJsonStr(jsonObject));  //将JSONObject 对象转换为 String
            log.info("=================>map \n {}", map);//这行的输出和上面一行的输出是完全一样的(已经验证过了)
            //取值练习
            JSONObject result = jsonObject.getJSONObject("result");
            //获取地市
            String city = result.getStr("city");  //特别注意,如果get之后是一个对象,则用jsonObject.getJSONObject,如果是得到一个基本类型,则是使用JSONObject对象的getStr()、getInt()等方法
            //获取今天温度
            JSONObject realtime = result.getJSONObject("realtime");
            String temperature = realtime.getStr("temperature");
            System.out.printf("===springRestTemplate==%s温度%s%n===", city, temperature);
        }

        return JSONUtil.toJsonStr(resultObject);
    }


    //异步任务
    public class dealTask implements Runnable{
        //(String cityName,String code)
        private String  cityName;
        private String  code;

        public dealTask(String cityName, String code) {
            this.cityName = cityName;
            this.code = code;
        }

        //业务处理
        @Override
        public void run() {
            log.info("===异步执行开始===");
            try {
                //10s后存储日志表
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            insertRequestLog(cityName,code);
            log.info("========异步执行成功=====");

        }
    }

    /**
     * 记录请求日志
     * @param cityName
     * @param code
     */
    public void insertRequestLog(String cityName, String code) {
        log.info("=========>insertRequestLog()方法");
        //报错模拟 ,会导致事务失效!!
        double a =1/0;
        ThirdRequestLog thirdRequestLog = new ThirdRequestLog();
        thirdRequestLog.setCity(cityName);
        thirdRequestLog.setRequestCode(code);
        LocalDateTime now = LocalDateTime.now();
        thirdRequestLog.setCreateTime(now);
        thirdRequestLogService.save(thirdRequestLog);
        log.info("=============>insertRequestLog()日志记录成功");
    }

    /**
     * URL入参参数 编码
     *
     * @param params
     * @return
     */
    private String urlencode(Map<String, ?> params) {
        StringBuilder stringBuilder = new StringBuilder();
        for (Map.Entry<String, ?> stringEntry : params.entrySet()) {
            try {
                stringBuilder.append(stringEntry.getKey()).append("=").append(URLEncoder.encode(stringEntry.getValue() + "", "UTF-8")).append("&");
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        }
        String result = stringBuilder.toString();
        //截取 从第一个字符到最后一个&,之间的字符,用于后面拼接到url后面当入参
        result = result.substring(0, result.lastIndexOf("&"));
        return result;
    }


}

在写代码的过程中遇见了一些问题:记录如下:

1、get请求方法中的参数为什么要进行urlencode() 编码呢 ?

2、异步任务会导致事务失效

3、RestTemplate 进行第三方接口调用 ,get请求的三种传参方式,包含不带header的,带header的;

4、  几种调用方式的区别:

  • get方式的http请求 (方式一,使用原始HttpURLConnection进行请求 )
  • post方式的http请求 (方式一,使用原始HttpURLConnection进行请求 )
  • RestTemplate 进行第三方接口调用
  • hutool工具:httpRequest.post()访问第三方接口

5、同一个类中,非事务方法内部调用事务方法导致的事务失效的原因及解决办法:

//由于当@Transactional(rollbackFor = Exception.class) 加在同类下面的自有方法的时候,直接调用的话,会导致事务失效,
        // 这种情况下要对在会被外界调用的方法上加上这个注解,因为被外界调用的方法会被代理对象所调用,此时事务会生效。如果有异常发生,则会回滚。如果非要将@Transactional 注解加到下面内部调用的方法里面的话
        //并且想要这个注解生效的话,则需要 使用Spring的IOC容器中去取得bean代理对象,通过这个代理对象去调用内部的方法。此时事务才会生效。



//     调用方式getWeatherMethod()方式一:      (也是最常用调用内部方法的方式)         getWeatherMethod(weatherReqVo); //这里就相当于使用this.方法名直接调用,不是通过代理进行的调用,直接调用的话,会导致事务失效, 


       调用方式二:(因为这里是方法A调用同类的内部方法B,而B是有事务注解的,这种情况下相当于this调用,不会通过代理对象调用,所以此时的调用,B方法上的事务其实不会生效,所以这里可以通过代理对象来调用) 
        HttpThirdCallController httpThirdCallController = (HttpThirdCallController)SpringBeanUtil.getBean("httpThirdCallController");
        httpThirdCallController.getWeatherMethod(weatherReqVo);

其中的

SpringBeanUtil工具类是公共写法:注意通过工具类获取bean的时候,bean默认的名称的首字符是小写。
package com.atguigu.gulimall.coupon.learn.util;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;


@Component
public class SpringBeanUtil implements ApplicationContextAware {

    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext)
            throws BeansException {
        SpringBeanUtil.applicationContext = applicationContext;
    }

    /**
     * 通过名称在spring容器中获取对象
     *
     * @param beanName
     * @return
     */
    public static Object getBean(String beanName) {
        System.out.println(applicationContext);
        return applicationContext.getBean(beanName);
    }
    

}

OK  到这里就结束啦~,代码一点点的敲粗来的,大家一起进步! go  go  go  O(∩_∩)O哈哈~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

执键行天涯、

码你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值