
本文共 11894 字,大约阅读时间需要 39 分钟。
HttpClient、JSONP
HttpClient介绍
HTTP 协议可能是现在 Internet 上使用得最多、最重要的协议了,越来越多的 Java 应用程序需要直接通过 HTTP 协议来访问网络资源。虽然在 JDK 的 java net包中已经提供了访问 HTTP 协议的基本功能,但是对于大部分应用程序来说,JDK 库本身提供的功能还不够丰富和灵活。HttpClient 是 Apache Jakarta Common 下的子项目,用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议。HttpClient 已经应用在很多的项目中,比如 Apache Jakarta 上很著名的另外两个开源项目 Cactus 和 HTMLUnit 都使用了 HttpClient。
HttpClient入门
- 导入jar包
<!--添加httpClient jar包 --> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> </dependency>
- 单元测试
/** * 1.测试HttpClient中的get请求 * 步骤: * 1.实例化httpClient对象 * 2.确定访问url地址 * 3.封装请求的类型 get/post/put/delete * 4.发起http请求,获取响应对象 response. * 5.校验返回对象的状态码信息. * 200表示成功 * 400请求的参数异常 * 404请求路径不匹配 * 406请求的返回值类型异常 * 500后台服务器运行异常 * 504请求超时. 后台服务器宕机/遇忙 * 6.如果状态码信息为200,则动态的获取响应数据. * 7.获取返回值数据,之后进行业务调用. * @throws IOException * @throws ClientProtocolException * */ @Test public void testGet() throws ClientProtocolException, IOException { HttpClient httpClient = HttpClients.createDefault(); String url = "https://www.baidu.com"; HttpGet httpGet = new HttpGet(url); HttpPost httpPost = new HttpPost(url); HttpResponse httpResponse = httpClient.execute(httpPost); if(httpResponse.getStatusLine().getStatusCode()==200) { //表示用户请求成功!! HttpEntity entity = httpResponse.getEntity(); //返回值数据的实体对象 String json = EntityUtils.toString(entity,"utf-8"); System.out.println(json); }else { //一定请求有误. System.out.println("服务器正忙,请稍后!!!"); } }
SpringBoot整合HttpClient
- 编辑properties配置文件
#最大连接数http.maxTotal=1000#并发数http.defaultMaxPerRoute=20#创建连接的最长时间http.connectTimeout=5000#从连接池中获取到连接的最长时间http.connectionRequestTimeout=500#数据传输的最长时间http.socketTimeout=5000#提交请求前测试连接是否可用http.staleConnectionCheckEnabled=true
- 编辑HttpClient的配置类
@Configuration@PropertySource(value="classpath:/properties/httpClient.properties")public class HttpClientConfig { @Value("${http.maxTotal}") private Integer maxTotal; //最大连接数 @Value("${http.defaultMaxPerRoute}") private Integer defaultMaxPerRoute; //最大并发链接数 @Value("${http.connectTimeout}") private Integer connectTimeout; //创建链接的最大时间 @Value("${http.connectionRequestTimeout}") private Integer connectionRequestTimeout; //链接获取超时时间 @Value("${http.socketTimeout}") private Integer socketTimeout; //数据传输最长时间 @Value("${http.staleConnectionCheckEnabled}") private boolean staleConnectionCheckEnabled; //提交时检查链接是否可用 //定义httpClient链接池 @Bean(name="httpClientConnectionManager") public PoolingHttpClientConnectionManager getPoolingHttpClientConnectionManager() { PoolingHttpClientConnectionManager manager = new PoolingHttpClientConnectionManager(); manager.setMaxTotal(maxTotal); //设定最大链接数 manager.setDefaultMaxPerRoute(defaultMaxPerRoute); //设定并发链接数 return manager; } //定义HttpClient /** * 实例化连接池,设置连接池管理器。 * 这里需要以参数形式注入上面实例化的连接池管理器 @Qualifier 指定bean标签进行注入 */ @Bean(name = "httpClientBuilder") public HttpClientBuilder getHttpClientBuilder(@Qualifier("httpClientConnectionManager")PoolingHttpClientConnectionManager httpClientConnectionManager){ //HttpClientBuilder中的构造方法被protected修饰,所以这里不能直接使用new来实例化一个HttpClientBuilder,可以使用HttpClientBuilder提供的静态方法create()来获取HttpClientBuilder对象 HttpClientBuilder httpClientBuilder = HttpClientBuilder.create(); httpClientBuilder.setConnectionManager(httpClientConnectionManager); return httpClientBuilder; } /** * 注入连接池,用于获取httpClient * @param httpClientBuilder * @return */ @Bean public CloseableHttpClient getCloseableHttpClient(@Qualifier("httpClientBuilder") HttpClientBuilder httpClientBuilder){ return httpClientBuilder.build(); } /** * Builder是RequestConfig的一个内部类 * 通过RequestConfig的custom方法来获取到一个Builder对象 * 设置builder的连接信息 * @return */ @Bean(name = "builder") public RequestConfig.Builder getBuilder(){ RequestConfig.Builder builder = RequestConfig.custom(); return builder.setConnectTimeout(connectTimeout) .setConnectionRequestTimeout(connectionRequestTimeout) .setSocketTimeout(socketTimeout) .setStaleConnectionCheckEnabled(staleConnectionCheckEnabled); } /** * 使用builder构建一个RequestConfig对象 * @param builder * @return */ @Bean public RequestConfig getRequestConfig(@Qualifier("builder") RequestConfig.Builder builder){ return builder.build(); }}
- 定义关闭链接操作
@Component //交给spring容器管理,定时将超时的链接关闭public class HttpClientClose extends Thread{ @Autowired private PoolingHttpClientConnectionManager manage; private volatile boolean shutdown; //开关 volatitle表示多线程可变数据,一个线程修改,其他线程立即修改 public HttpClientClose() { ///System.out.println("执行构造方法,实例化对象"); //线程开启启动 this.start(); } @Override public void run() { try { //如果服务没有关闭,执行线程 while(!shutdown) { synchronized (this) { wait(5000); //等待5秒 //System.out.println("线程开始执行,关闭超时链接"); //关闭超时的链接 PoolStats stats = manage.getTotalStats(); int av = stats.getAvailable(); //获取可用的线程数量 int pend = stats.getPending(); //获取阻塞的线程数量 int lea = stats.getLeased(); //获取当前正在使用的链接数量 int max = stats.getMax(); //System.out.println("max/"+max+": av/"+av+": pend/"+pend+": lea/"+lea); manage.closeExpiredConnections(); } } } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(); } super.run(); } //关闭清理无效连接的线程 @PreDestroy //容器关闭时执行该方法. public void shutdown() { shutdown = true; synchronized (this) { //System.out.println("关闭全部链接!!"); notifyAll(); //全部从等待中唤醒.执行关闭操作; } }}
- 编辑HttpClient工具API
@Servicepublic class HttpClientService { @Autowired private CloseableHttpClient httpClient; @Autowired private RequestConfig requestConfig; /** * 编辑一个HttpClientAPI,简化程序远程调用的过程 * @param url * @param params * @param charset * @return */ public String doGet(String url,Map<String,String> params,String charset) { //1.判断字符集编码是否为空 if(StringUtils.isEmpty(charset)) { charset="UTF-8"; } //2.校验Map中是否有参数 Map<entry<k,v>> // 有参数: http://www.jt.com/findItem?id=1&name=brrby if(params!=null) { //需要进行参数的拼接 将Map集合遍历,动态获取key=value url+="?"; for (Map.Entry<String, String> i : params.entrySet()) { String key=i.getKey(); String value=i.getValue(); //拼接参数 url+=key+"="+value+"&"; } //http://www.jt.com/findItem?id=1&name=brrby& url=url.substring(0,url.length()-1); } //3.定义用户的请求类型 HttpGet httpGet=new HttpGet(url); //为get请求添加超时时间 httpGet.setConfig(requestConfig); //4.发起http请求 String result=null; try { HttpResponse httpResponse = httpClient.execute(httpGet); //判断状态码是否正确 if(httpResponse.getStatusLine().getStatusCode()==200) { HttpEntity entity=httpResponse.getEntity(); result=EntityUtils.toString(entity,charset); } }catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); throw new RuntimeException(); } return result; } //为不同的用户提供不同的方法支持 public String doGet(String url) { return doGet(url,null,null); } public String doGet(String url,Map<String,String> params) { return doGet(url,params,null); } //实现httpClient POST提交 public String doPost(String url,Map<String,String> params,String charset){ String result = null; //1.定义请求类型 HttpPost post = new HttpPost(url); post.setConfig(requestConfig); //定义超时时间 //2.判断字符集是否为null if(StringUtils.isEmpty(charset)){ charset = "UTF-8"; } //3.判断用户是否传递参数 if(params !=null){ //3.2准备List集合信息 List<NameValuePair> parameters = new ArrayList<>(); //3.3将数据封装到List集合中 for (Map.Entry<String,String> entry : params.entrySet()) { parameters.add(new BasicNameValuePair(entry.getKey(), entry.getValue())); } //3.1模拟表单提交 try { UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(parameters,charset); //采用u8编码 //3.4将实体对象封装到请求对象中 post.setEntity(formEntity); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } //4.发送请求 try { CloseableHttpResponse response = httpClient.execute(post); //4.1判断返回值状态 if(response.getStatusLine().getStatusCode() == 200) { //4.2表示请求成功 result = EntityUtils.toString(response.getEntity(),charset); }else{ System.out.println("获取状态码信息:"+response.getStatusLine().getStatusCode()); throw new RuntimeException(); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } return result; } public String doPost(String url){ return doPost(url, null, null); } public String doPost(String url,Map<String,String> params){ return doPost(url, params, null); } public String doPost(String url,String charset){ return doPost(url, null, charset); }}
- 编辑后台controller
@RestController@RequestMapping("/web/item")public class WebItemController { @Autowired private ItemService itemService; /** * 根据前台url动态获取商品数据 * http://manage.jt.com/web/item/findItemById?itmeId=562379 */ @RequestMapping("/findItemById") public Item findItemById(Long itemId) { return itemService.findItemById(itemId); }}
- 编辑前台测试类
/** * 测试后台动态获取数据,是否成功!!! * http://manage.jt.com/web/item/findItemById?itemId=562379 */ //远程获取服务器数据,并且转化为自己的对象. @Test public void testFindItem() { String url = "http://manage.jt.com/web/item/findItemById"; Map<String,String> params = new HashMap<>(); params.put("itemId", "562379"); String result = httpClientService.doGet(url, params); //将JSON转化为对象 Item item = ObjectMapperUtil.toObj(result, Item.class); System.out.println(item); }
HttpClient执行过程
JSONP介绍
JSONP(JSON with Padding)是JSON的一种“使用模式”,可用于解决主流浏览器的跨域数据访问的问题。由于同源策略,一般来说位于 server1.example.com 的网页无法与不是 server1.example.com的服务器沟通,而 HTML 的< script > 元素是一个例外。利用 < script > 元素的这个开放策略,网页可以得到从其他来源动态产生的 JSON 资料,而这种使用模式就是所谓的 JSONP。用 JSONP 抓到的资料并不是 JSON,而是任意的JavaScript,用 JavaScript 直译器执行而不是用 JSON 解析器解析。
JSONP跨域访问原理
- 利用javaScript中的src属性实现跨域访问, src属性的开放策略。
- 自定义回调函数 。callback
- 将返回值的结果经过特殊的格式封装 。callback(json)
jQuery实现JSONP调用
编辑a.html在a项目
<!DOCTYPE html><html><head><meta charset="UTF-8"><title>JSONP测试</title><script type="text/javascript" src="http://manage.jt.com/js/jquery-easyui-1.4.1/jquery.min.js"></script><script type="text/javascript"> $(function(){ alert("测试访问开始!!!!!") $.ajax({ url:"http://manage.jt.com/web/testJSONP", type:"get", //jsonp只能支持get请求 dataType:"jsonp", //dataType表示返回值类型 //jsonp: "callback", //指定参数名称 //jsonpCallback: "hello", //指定回调函数名称 success:function (data){ //data经过jQuery封装返回就是json串 alert(data.itemId); alert(data.itemDesc); //转化为字符串使用 //var obj = eval("("+data+")"); //alert(obj.name); } }); })</script></head><body> <h1>JSON跨域请求测试</h1></body></html>
在后台manage项目里编辑一个专门接收a项目发来的请求的RestController
@RestControllerpublic class JSONPController { //http://manage.jt.com/web/testJSONP /** * JSONPObject是跨域访问的API * function:回调函数的名称 * value: 需要返回的对象 */ @RequestMapping("/web/testJSONP") public JSONPObject jsonp(String callback) { ItemDesc itemDesc=new ItemDesc(); itemDesc.setItemId(5050L).setItemDesc("我是!"); return new JSONPObject(callback, itemDesc); } }
HttpClient和JSONP对比
- HttpClient调用的层级较多,代码结构繁琐
- JSONP调用层级比HttpClient调用较少
- HttpClient和JSONP获取远程服务器数据
- HttpClient可以进行post提交,JSONP只能进行get提交
- httpClinet调用安全性更好,JSONP的全部调用被浏览器进行监控
- 如果提交重要的数据,一般采用httpClient。JSONP一般只负责查询数据
SOA思想
面向服务的架构(SOA)是一个组件模型,它将应用程序的不同功能单元(称为服务)进行拆分,并通过这些服务之间定义良好的接口和协议联系起来。接口是采用中立的方式进行定义的,它应该独立于实现服务的硬件平台、操作系统和编程语言。这使得构件在各种各样的系统中的服务可以以一种统一和通用的方式进行交互。
RPC
RPC是远程过程调用(Remote Procedure Call)的缩写形式。也就是说两台服务器A、B,一个应用部署在A服务器上,想要调用B服务器上应用提供的函数或方法。由于不在一个内存空间,不能直接调用,需要通过网络来表达调用的语义和传达调用的数据。SAP系统RPC调用的原理其实很简单,有一些类似于三层构架的C/S系统,第三方的客户程序通过接口调用SAP内部的标准或自定义函数,获得函数返回的数据进行处理后显示或打印。
- 特点:
- 1.用户无需了解底层实现的协议规范,类似于调用本地服务一样调用远程服务
- 2.RPC调用实质就是一个代理
- 3.httpClient也属于RPC(RPC只是一种思想,是说两个项目之间的调用)
RPC 和 SOA 一样也不是规范,不是协议,是一种技术思想,一种理念。 比如耳熟能祥的 HTTP 和 WebService 就是 RPC 思想的一种很好的体现方式。
发表评论
最新留言
关于作者
