48.大数据之旅——java分布式项目09-前台系统搭建

前台系统搭建

搭建步骤:
1.创建jt-web Maven web工程,并更改三项环境参数
2.让jt-web 继承 jt-parent 工程,并依赖jt-common
3.修改jt-web pom.xml文件,添加tomcat7插件,并设置端口号
4.引入前台的静态文件,js,css,jpg等资源
5.引入Spring 及SpringMVC等配置文件,因为前台系统不直接和数据库做交互,所以不需要配置Mybatis的配置文案
6.配置web.xml
7.配置debug configuration
8.配置nginx反向代理
配置示例:

server {
listen       80;
server_name  www.jt.com;
#charset koi8-r;
#access_log  logs/host.access.log  main;
 
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host; #代理头信息,tomcat中就可以获取访问的域名
 
location / {
       proxy_pass http://127.0.0.1:8082;
       proxy_connect_timeout 600;
       proxy_read_timeout 600;
}
}

9.配置hosts文件(可以通过SwitchHosts工具类配置)

10.启动前台服务器,输入地址进行访问

www.jt.com/index.html

web.xml配置代码:

<servlet-mapping>
<servlet-name>springmvc-web</servlet-name>
<!-- 为什么配置*.html?因为前台的页面都是静态页面(也可能是伪静态页面),静态页面一般以htm,html为扩展名。
静态网页内容一经发布到网站服务器上,无论是否有用户访问,每个静态网页的内容都是保存在网站服务器上的,也就是说,
静态网页是实实在在保存在服务器上的文件,每个网页都是一个独立的文件。
           静态网页比较稳定,所以容易被搜索引擎检索(爬虫)。
           
           爬虫一般来说它只爬html静态页面,它会检查url,并仅对html,htm结尾的文件发送资源请求,如果是静态网页,爬虫会下载网页,
           并对页面进行处理,处理完之后,爬虫把下载到的网页资源放到大型数据里存储,并建立索引。比如Google索搜引擎,至今存储了30亿个web页面
          
           所以,我们在用搜索引擎搜索某一个网页时,实际上先到搜索引擎的数据库里,根据用户输入的关键字,在配合数据库的索引找到符合条件的页面,
            然后搜索引擎会根据排名算法,向用户展示搜索结果,当用户点击某一个页面时,才真正去访问某个具体的服务网站,比如淘宝,京东。
           这样能够避免爬到哪些数据频繁变动且不真正存在的动态页面文件。
    
           此外,网站本身也会做一些搜索优化,SEO,常见的优化措施就是设置网站标题和关键字
    
           爬虫个人也可以开发,但是想象一下,如果有1000万个个人爬虫同时去爬京东网站,爬虫在爬目标网站的时候,肯定是要发送资源请求的,
           相当于造成10000万的高并发,这个威力比用户并发访问的压力还有大,所以会导致网络或者服务器阻塞,所以网站都会设置相关机制来屏蔽个人爬虫。
   -->
<url-pattern>*.html</url-pattern>
</servlet-mapping>
 
<servlet-mapping>
<servlet-name>springmvc-web</servlet-name>
<url-pattern>/service/*</url-pattern>
</servlet-mapping>

为什么配置以*.html为后缀的拦截路径呢?
前台页面一般都是以静态页面来展示的(html,htm)为后缀的页面
静态网页有什么特点:
1.静态网页一经发布到web服务器上,无论是否有用户访问,每个静态页面页面都保存在网站服务器上,也就是静态网页是真正存在的。
2.静态网页比较稳定,有利于被搜索引擎收录。

爬虫,爬虫是搜索引擎的重要组成部分,它是一个自动获取网页内容的程序。爬虫,只爬静态静态,只怕以htm,html为扩展名的网页
动态网页 ,jsp,.net,php等都不爬,还有 网页地址里带?的都不爬。

爬虫会先先查url是否合法,并仅对静态文件发送请求,如果是静态文件,爬虫会下载网页,对网页进行处理后,把网页资源存在大型数据库里存储,并建立
索引。比如Google,至今存储了30多亿的web页面。

当用户通过搜索引擎,输入关键字查找网页时,搜索引擎会调用全文检索技术以及网页排名算法(比如Google的pageRank,百度的pageByPrice)把网页展示给用户。

web.xml的配置说明


<servlet-mapping>
<servlet-name>springmvc-web</servlet-name>
<!-- 以html为后缀(扩展名)的文件,是一个静态页面
静态页面的特点:
1.静态页面是真正存在服务上的文件,动态页面不真正存在
2.静态页面,html,htm.
3.静态页面会被搜索引擎收录。
 
搜索引擎,只收录静态页面。搜索引擎有一个重要的组成部分,叫爬虫。
爬虫是一个抓取网页的程序,爬虫会判断url的合法性,然后对以html,htm为扩展名的url发送请求
发现是静态页面,就把这个静态页面下载下来,然后做一些处理,存在大型数据库里,比如Google
存储了将近30亿个web页面。
 
用户通过搜索引擎来搜索页面,用户通过关键字,搜索引擎根据关键字,做全文检索,配合非常博大的算法,
再结合排名算法(比如Google的pageRank,比如百度的pageByprice)展示给用户。
 
现在京淘,配置的url-pattren 是*.html
前台页面的首页位置WEB-INF/views/index.jsp
需要通过后台Controller转发过来,转发的前提示,Controller收到一个url请求
当 在url地址里输入:http://localhost:8082/index.html,后台controller才能收到
 
这么配置的目的,是为了把我们的页面伪装成一个html静态页面,便于搜索引擎的收录。
不配置 / 的原因是为了 做一个更严格的集中控制,即前天所有的页面,都以html为结尾,都变成
静态页面,让搜索引擎收录到。
 
体现了一个思想,就是想办法和搜索引擎的关系变得更亲密,即让搜索引擎能够抓到更多网站上的内容。
这个SEO优化。
 
SEO优化手段措施之一,就是页面的静态化。
SEO优化手段措施之二,为网站设置标题和关键字。
 -->
<url-pattern>*.html</url-pattern>
</servlet-mapping>

SEO与伪静态


首先要明确一点,就是之所以要静态(伪静态)页面,目的就是为了SEO,搜索引擎优化。

什么是URL伪静态?

最开始,没有动态语言的时候,比如jsp,php,asp。做静态页面是都手工做的,比如做好之后,给这个静态页面 编个名,比如 12324.html,然后发布到服务器上,用户就可以访问到了。这种方式,效率相当低,比如更换图片,文字信息等,还得重新得编辑静态页面,替换,发布。特别的麻烦

后来出现了动态语言asp/jsp,和数据库一结合,交互性很强的网站就出现了,访问地址的形式就变成了带有?,向后台传参数,然后数据库根据参数来查询信息。这就是我们所说的动态页面。

后来动态页面技术产生了,这样就可以根据数据库的信息,动态的生成页面了,解放了前台人员的双手,但是搜索引擎不会收录动态页面,还不会收录带?url地址。爬虫去爬这样的地址,很容易陷入死循环,爬不出来,一般我们把这个叫做 “机器人爬虫陷阱”,如果进去爬不出来,只能重启爬虫服务器。所以搜索引擎不会爬动态页面。

有的网站为了避免恶意爬虫,会添加信赖地址,比如google 和百度允许,其他的不允许。
爬虫会设置一个爬页面的频率,这个频率不能太高,也不能太低。太高会提高网站的访问压力。

因为搜索引擎不会收录动态页面,所以早期,人们都是把生成的动态页面,再转换成以html结尾的静态页面,存在服务器上,这些页面是真的静态页面。但是有很严重的问题,随着数据量越来越大,服务器上存储的静态页面越来越多,对网站的访问速度会造成严重的影响,于是,伪静态技术就出现了,伪静态技术解决了三个问题:一是把动态页面转换成html结尾的静态页面,二是可以实时更新数据,三是避免了大量真静态页面的产生

即伪静态技术带来以下几点好处:
真正的静态页面空间存储量大,进行删除或者更新html文件会造成大量文件碎片,动态页面虽然可以实时更新,但对搜索引擎不友好。

伪静态实现原理:
一般用404错误处理机制来实现
比如用户或蜘蛛发送一个静态地址的请求,比如:
http://网址XXX/12345.html时(12345为文章在数据库的ID). 这个页面不存在,所以触发了404错误。但是这个404错误不会给用户和蜘蛛看到,而是会触发伪静态代码
如果是用asp动态技术来实现的话:
404错误出现后,转向index.asp,在index.asp里添加下面的代码:
CurrDomain=Request.ServerVariables(“HTTP_HOST”) '当前访问域名
CurrURL=Replace(Request.ServerVariables(“QUERY_STRING”),“404;http://”&CurrDomain&":80") '当前访问URL
此时的CurrURL应该是:12345.html
如果用java后台程序来做的话:
就是用RESTFUL的接参方式来接,也能拿到12345这个id号

有了文章或商品的ID号(12345)就好办了,从数据库里抓数据,把商品的各项信息,比如商品图片,参数信息,价格填进页面模板里,然后输出到页面里,就OK了。
这样。用户或蜘蛛看到的URL还是他访问的URL,而我们对内容的处理上也用到了动态技术。这就是我们想要的结果。说得简单了一些。但是基本思路就是这样了。

伪静态的好处
1.伪静态生成的页面是以静态页面来展示的,便于SEO优化,搜索引擎能够收录
2.伪静态生成的页面,是不存在的,这样能够避免过多的真静态页面生成,便于维护管理,降低服务器空间的占用率。

跨域问题及解决办法

实验一:在manage.jt.com域下,建立两个文件 test.htm和test.json(放在webapp目录下即可):
test.html代码:

<!doctype html>
<html>
 <head>
 </head>
 <body>
<script type="text/javascript" src="http://manage.jt.com/js/jquery-easyui-1.4.1/jquery.min.js"></script>
<script type="text/javascript">
//这段js代码的含义就是,当页面被请求时,会利用js去请求http://manage.jt.com/test.json这个文件
//然后利用js函数,把test.json里的json数据拿到,并解析key对应的value值
$(function(){
$.get("http://manage.jt.com/test.json",function(data){
alert(data.key);
});
});
 
</script>
 
 </body>
</html>
test.json代码(是一个json串):
{
"key":123
}

当访问 http://manage.jt.com/test.htm 时,页面会弹出 “123”;证明请求的数据已拿到。注意test.htm请求数据的方式,是通过javaScript访问了 http://manage.jt.com/test.json (jquery是对javaScript的封装和扩展,本质上也是javaScript)

实验二:在jt.com域下,把这两个文件拷贝过来,测试看是否能拿到数据
结果:拿不到数据,打开F12开发人员工具,发现出现如下提示:
48.大数据之旅——java分布式项目09-前台系统搭建
JavaScript出于安全方面的考虑,禁止了跨域访问。
48.大数据之旅——java分布式项目09-前台系统搭建
48.大数据之旅——java分布式项目09-前台系统搭建

JSONP


Jsonp是一种使用模式,用于解决浏览器跨域数据访问的问题。利用

举例说明:
假设客户期望返回JSON数据:{“id”:“1”,“name”:"}
而这个数据由服务端返回,服务端的地址所在域是:http://www.jt.manage.com
那客户就可以发送一个跨域调用:http://www.yiwuku.com/callback?

那么服务端收到这个请求后,发现有callback参数,一般来讲,callback就代表是一个跨域调用,这是约定俗称的一个习惯。
知道是跨域调用后,我们就要组织相应的json格式数据,并用 aaa(callback参数值)将要返回的json格式数据包裹起来(这样就形成了一个js函数),即如下形式:
aaa({“id”:“1”,“name”:"})

页面分析:
48.大数据之旅——java分布式项目09-前台系统搭建

前台页面商品类目展示


48.大数据之旅——java分布式项目09-前台系统搭建

商场首页分类实现


48.大数据之旅——java分布式项目09-前台系统搭建
WebItemCatController类代码:

/**
 * 这个类是用于响应前台页面的商品类目展示
 * @author ysq
 *
 */
@Controller
public class WebItemCatController {
 
@Autowired
private ItemCatService itemCatService;
 
private static final ObjectMapper MAPPER=new ObjectMapper();
 
@RequestMapping("/web/itemcat/all")
public void queryItemCatJsonp(String callback,HttpServletResponse resp){
ItemCatResult result=itemCatService.queryItemJsonp();
try {
String dataJson=MAPPER.writeValueAsString(result);
String jsonData=callback+"("+dataJson+")";
 
resp.setContentType("text/html;charset=UTF-8");
resp.getWriter().write(jsonData);
} catch (Exception e) {
e.printStackTrace();
}
 
 
}
}

所在工程:jt-manage-service
48.大数据之旅——java分布式项目09-前台系统搭建
ItemCatService代码:

@Override
public ItemCatResult queryItemJsonp() {
 
//查询所有商品分类
List<ItemCat> LIST_ALL=itemCatMapper.select(null);
//用来封装一级商品分类的集合
List<ItemCatData> LEVEL_1=new ArrayList<>(); 
 
for(ItemCat L1:LIST_ALL){
//先筛出一级商品分类
if(L1.getParentId()==0){
 
ItemCatData ICD_1=new ItemCatData();
ICD_1.setUrl("/products/"+L1.getId()+".html");
ICD_1.setName("<a href='/products/"+L1.getId()+".html'>"+L1.getName()+"</a>");
//这个集合,用来封装二级分类的ItemCatData对象的集合
List<ItemCatData> LEVEL_2=new ArrayList<>();
ICD_1.setItems(LEVEL_2);
 
LEVEL_1.add(ICD_1);
 
//根据当前一级商品的id,得到其二级商品分类的集合
List<ItemCat> L2_LIST=itemCatMapper.findItemCatByParentId(L1.getId());
for(ItemCat L2:L2_LIST){
ItemCatData ICD_2=new ItemCatData();
ICD_2.setUrl("/products/"+L2.getId()+".html");
ICD_2.setName(L2.getName());
//这个集合,用来封装三级分类商品josn数据集合
List<String> LEVEL_3=new ArrayList<>();
ICD_2.setItems(LEVEL_3);
 
LEVEL_2.add(ICD_2);
 
//根据当前二级商品分类的id去查对应的三级商品分类
List<ItemCat> SUB_OF_L2=itemCatMapper.findItemCatByParentId(L2.getId());
for(ItemCat L3:SUB_OF_L2){
//三级商品分类的json数据比较特殊:
//["/products/4.html|网络原创","/products/5.html|数字杂志"]
//没有{},所以,它要求的是:List<String>这种list集合(不是List<Itemcat>这种)
LEVEL_3.add("/products/"+L3.getId()
+".html|"+L3.getName());
}
}
 
}
 
}
ItemCatResult itemCatResult=new ItemCatResult();
itemCatResult.setItemCats(LEVEL_1);
 
return itemCatResult;
 
}

@Responsebody


后台Controller:

@Controller
public class ItemCatWebController {
 
@Autowired
private ItemCatService itemCatService;
 
/*
 * 针对当前的返回类型,ItemCatResult,如果用@ResoponseBody注解来实现的话
 * 返回的只是一个普通的json串,但是不能应对跨域请求要求的格式,比如说:
 * test(json数据)
 * 1.写一个类,实现或者间接实现HttpMessageConverter,在这个类里,做相应的格式转换
 * 2.在SpringMVC的核心配置文件里,具体是在<mvc:annotation> <>配置一个<mvc:message-converts>
 * 然后把这个类的bean在里面配置一下就可以了
 */
@RequestMapping("/web/itemcat/all")
@ResponseBody
public ItemCatResult queryItemCatJsonp(){
return itemCatService.queryItemCatJsonp();
}
CallBackMappingJackson2HttpMessageConverter 代码:
public class CallBackMappingJackson2HttpMessageConverter extends MappingJackson2HttpMessageConverter {
 
 
 @Override
    protected void writeInternal(Object object, HttpOutputMessage outputMessage) throws IOException,
            HttpMessageNotWritableException {
      //1.object 对应的就是返回的对象,比如本例中的ItemCatResult对象
//2.outputMessage 可以理解为HttpServletResponse
        // 从threadLocal中获取当前的Request对象,目的是通过request,得到callback对应的方法名
            
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder
                .currentRequestAttributes()).getRequest();
        //得到callback对应的方法名
        String callbackParam = request.getParameter("callback");
        if (StringUtils.isEmpty(callbackParam)) {
            // 没有找到callback参数,直接返回json数据,不做任何的处理
            super.writeInternal(object, outputMessage);
        } else {
                //得到响应头里的字符集信息
            JsonEncoding encoding = getJsonEncoding(outputMessage.getHeaders().getContentType());
            try {
                    //将对象转换为json串
                String result = callbackParam + "(" + super.getObjectMapper().writeValueAsString(object)
                        + ");";
                //调用commons.io包里的IOUtils的方法,把组织好的跨域要求的数据返回去了
                IOUtils.write(result, outputMessage.getBody(), encoding.getJavaName());
            } catch (JsonProcessingException ex) {
                throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getMessage(), ex);
            }
        }
    }
 
 
 
 
}

mvc:message-converters


当我们使用@Responsbody注解时,它可以非常方便的帮助开发人员把返回的对象转换成json串,但有些特殊场景下,我们需要返回一些特殊的数据格式,而@Responsbody注解默认转换的json格式并不能达到 我们想要的效果。我们需要人为地改变@Responsbody生成的json格式。
所以,这时,我们可以在<mvc:annotion-driven>注解里,配置一个可选参数:
<mvc:annotation-driven>它的作用是:
允许注册实现了HttpMessageConverter接口的bean,来对requestbody 或responsebody中的数据进行解析和处理。

配置代码如下:

<mvc:annotation-driven>
<!-- 采用自定义方案 -->
<mvc:message-converters>
<!-- 定义文本转化器,定义字符集,避免出现乱码 -->
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<constructor-arg index="0" value="UTF-8" />
</bean>
 
<!-- 定义json转化器,支持json跨域
注册自定义的bean,这个bean间接实现了HttpMessageConverter接口,
我们在这个bean里对@Responsbody生成的json格式进行人为地改变->
 
<bean class="com.jt.common.spring.exetend.jackson.CallbackMappingJackson2HttpMessageConverter"></bean>
</mvc:message-converters>
</mvc:annotation-driven>

自定义json格式bean的简写版:

/**
 * 注意,实现了MappingJackson2HttpMessageConverter这个类后,只有当请求方式是传统get请求时,此bean对于@ResponseBody的json格式的更改才会生效
 * 如果请求时RESTFUL风格,则用的还是@ResponseBody默认的json格式转换
 * 由于本例中,发送的请求是:http://manage.jt.com/web/itemcat/all?callback=category.getDataService,是传统的get请求方式,所以可以生效
 * 
 */
public class CallbackMappingJackson2HttpMessageConverter extends MappingJackson2HttpMessageConverter {
 
    @Override
    protected void writeInternal(Object object, HttpOutputMessage outputMessage) throws IOException,
            HttpMessageNotWritableException {
            
            String dataJson=super.getObjectMapper().writeValueAsString(object);
            String callbackJson="category.getDataService"+"("+dataJson+");";
            IOUtils.write(callbackJson, outputMessage.getBody());
自定义json格式bean的完整版:
 
public class CallBackMappingJackson2HttpMessageConverter extends MappingJackson2HttpMessageConverter {
 
    @Override
    protected void writeInternal(Object object, HttpOutputMessage outputMessage) throws IOException,
            HttpMessageNotWritableException {
  
        // 从threadLocal中获取当前的Request对象
            
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder
                .currentRequestAttributes()).getRequest();
        String callbackParam = request.getParameter("callback");
        if (StringUtils.isEmpty(callbackParam)) {
            // 没有找到callback参数,直接返回json数据
            super.writeInternal(object, outputMessage);
        } else {
            JsonEncoding encoding = getJsonEncoding(outputMessage.getHeaders().getContentType());
            try {
                    //将对象转换为json串
                String result = callbackParam + "(" + super.getObjectMapper().writeValueAsString(object)
                        + ");";
                IOUtils.write(result, outputMessage.getBody(), encoding.getJavaName());
            } catch (JsonProcessingException ex) {
                throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getMessage(), ex);
            }
        }
    }
 
 
 
}

HttpClient介绍


HttpClient介绍
HttpClient是Apache Jakarta Common下的子项目,用来提供高效的、最新的、功能丰富的支持HTTP协议的客户端编程工具包,并且它支持HTTP协议最新的版本和建议。HttpClient已经应用在很多的项目中,比如Apache Jakarta上很著名的另外两个开源项目Cactus和HTMLUnit都使用了HttpClient。

HttpClient特点

  1. 基于标准、纯净的Java语言。实现了Http1.0和Http1.1
  2. 以可扩展的面向对象的结构实现了Http全部的方法(GET, POST, PUT, DELETE, HEAD, OPTIONS, and TRACE)。
  3. 支持HTTPS协议。
  4. 通过Http代理建立透明的连接。
  5. 利用CONNECT方法通过Http代理建立隧道的https连接。
  6. Basic, Digest, NTLMv1, NTLMv2, NTLM2 Session, SNPNEGO/Kerberos认证方案。
  7. 插件式的自定义认证方案。
  8. 便携可靠的套接字工厂使它更容易的使用第三方解决方案。
  9. 连接管理器支持多线程应用。支持设置最大连接数,同时支持设置每个主机的最大连接数,发现并关闭过期的连接。
  10. 自动处理Set-Cookie中的Cookie。
  11. 插件式的自定义Cookie策略。
  12. Request的输出流可以避免流中内容直接缓冲到socket服务器。
  13. Response的输入流可以有效的从socket服务器直接读取相应内容。
  14. 在http1.0和http1.1中利用KeepAlive保持持久连接。
  15. 直接获取服务器发送的response code和 headers。
  16. 设置连接超时的能力。
  17. 实验性的支持http1.1 response caching。
  18. 源代码基于Apache License 可免费获取。

通过HttpClient模拟GET提交
使用 HttpClient 需要以下 6 个步骤:

  1. 创建 HttpClient 的实例
  2. 创建某种连接方法的实例,在这里是GetMethod。在 GetMethod 的构造函数中传入待连接的地址
  3. 调用第一步中创建好的实例的 execute 方法来执行第二步中创建好的 method 实例
  4. 读 response
  5. 释放连接。无论执行方法是否成功,都必须释放连接
  6. 对得到后的内容进行处理

代码示例:

public class TestHttpClient {
@Test
public void testConnent(){
CloseableHttpClient httpClient=HttpClients.createDefault();
HttpGet httpGet=new HttpGet("http://item.jd.com/3243686.html");
try {
CloseableHttpResponse response= httpClient.execute(httpGet);
if(response.getStatusLine().getStatusCode()==200){
String content=EntityUtils.toString(response.getEntity());
System.out.println(content);
}
} catch (Exception e) {
// TODO: handle exception
}finally{
httpClient=null;
}
 
 
 
}
 

通过HttpClient模拟Post提交

@Test
public void testPost(){
CloseableHttpClient httpClient=HttpClients.createDefault();
HttpPost  httpPost=new HttpPost("https://passport.****.net/");
List formparams=new ArrayList<>();
formparams.add(new BasicNameValuePair("username", "tom"));
formparams.add(new BasicNameValuePair("password", "1234"));
 
try {
httpPost.setEntity(new UrlEncodedFormEntity(formparams,"UTF-8"));
CloseableHttpResponse response=httpClient.execute(httpPost);
if(response.getStatusLine().getStatusCode()==200){
String content=EntityUtils.toString(response.getEntity());
System.out.println(content);
}
} catch (Exception e) {
 
e.printStackTrace();
}finally{
httpClient=null;
}
}

HttpClient 和Spring 整合


整个步骤:
1.引入Httpclient的依赖jar包
pom.xml坐标:

<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>${httpclient.version}</version>
</dependency>
 
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpmime</artifactId>
<version>4.3.1</version>
</dependency>

2.引入httpclient和spring的整合文件
配置代码:

<!-- 定义httpclient连接池 -->
<bean id="httpClientConnectionManager" class="org.apache.http.impl.conn.PoolingHttpClientConnectionManager" destroy-method="close">
<!-- 设置连接总数 -->
<property name="maxTotal" value="${http.pool.maxTotal}"></property>
<!-- 设置每个地址的并发数 -->
<property name="defaultMaxPerRoute" value="${http.pool.defaultMaxPerRoute}"></property>
</bean>
 
<!-- 定义 HttpClient工厂,这里使用HttpClientBuilder构建-->
<bean id="httpClientBuilder" class="org.apache.http.impl.client.HttpClientBuilder" factory-method="create">
<property name="connectionManager" ref="httpClientConnectionManager"></property>
</bean>
 
<!-- 得到httpClient的实例 -->
<bean id="httpClient" factory-bean="httpClientBuilder" factory-method="build"/>
 
<!-- 定期清理无效的连接 -->
<bean class="com.jt.common.util.IdleConnectionEvictor" destroy-method="shutdown">
<constructor-arg index="0" ref="httpClientConnectionManager" />
<!-- 间隔一分钟清理一次 -->
<constructor-arg index="1" value="60000" />
</bean>
 
<!-- 定义requestConfig的工厂 -->
<bean id="requestConfigBuilder" class="org.apache.http.client.config.RequestConfig.Builder">
<!-- 从连接池中获取到连接的最长时间 -->
<property name="connectionRequestTimeout" value="${http.request.connectionRequestTimeout}"/>
<!-- 创建连接的最长时间 -->
<property name="connectTimeout" value="${http.request.connectTimeout}"/>
<!-- 数据传输的最长时间 -->
<property name="socketTimeout" value="${http.request.socketTimeout}"/>
<!-- 提交请求前测试连接是否可用 -->
<property name="staleConnectionCheckEnabled" value="${http.request.staleConnectionCheckEnabled}"/>
</bean>        
 
<!-- 得到requestConfig实例 -->
<bean id="requestConfig" factory-bean="requestConfigBuilder" factory-method="build" />
 

3.配置Spring核心文件,使其加载Httpclient的属性文件
4.在jt-web工程里,调用HttpClient的API
前台·Contorller代码:

@Controller
public class PageController {
 
@Autowired
private HttpClient httpclient;
 
@RequestMapping("/index")
public String goIndex(){
HttpGet httpGet=new HttpGet("http://manage.jt.com/page/index");
 
try {
HttpResponse response=httpclient.execute(httpGet);
String s=EntityUtils.toString(response.getEntity(),"UTF-8");
System.err.println(s);
} catch (ClientProtocolException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//WEB-INF/views/index.jsp
return "index";
}
}

第二种办法:用封装好的httpservice
48.大数据之旅——java分布式项目09-前台系统搭建

商品详情页展现


前台对应的jsp文件:WEB-INFO-Views-error-item.jsp
这个jsp文件相当于是一个页面模板,当后台传值过来后,会解析商品的各项数据,比如商品标题、价格等。所以我们需要利用Model对象,把Item对象传给前台
48.大数据之旅——java分布式项目09-前台系统搭建
此外,前台会展示商品图片,这个要解析的是Item的images属性,这个属性本质上是将商品的image信息按 , 切分,形成一个图片地址的字符串数组。
48.大数据之旅——java分布式项目09-前台系统搭建
48.大数据之旅——java分布式项目09-前台系统搭建
jt-web Controller代码:

@Controller
public class ItemController {
@Autowired 
private ItemService itemService;
 
//访问商品详情页        http://www.jt.com/items/562379.html
@RequestMapping("items/{itemId}")
public String item(@PathVariable String itemId, Model model) throws Exception{
 
Item item = itemService.getItemByItemId(itemId);
model.addAttribute("item", item);
 
return "item";
}
}

jt-web Service代码:

@Service
public class ItemService {
@Autowired
private HttpClientService httpClientService;
private static final ObjectMapper MAPPER = new ObjectMapper();        //jackson提供工具类
 
public Item getItemByItemId(String itemId) throws Exception {
String url = "http://manage.jt.com/item/"+itemId;
String jsonData = httpClientService.doGet(url);
 
 
Item item = MAPPER.readValue(jsonData, Item.class);
return item;
}
 
}

jt-web Pojo代码:

public class Item extends BasePojo{
private Long id;
private String title;
private String sellPoint;
private Long price;
private Integer num;
private String barcode;
private String image;                //最多5张图片
private Long cid;
private Integer status;                //'默认值为1,可选值:1正常,2下架,3删除        
 
private String[] images;        //利用这个字段拆分图片
 
public String[] getImages() {
return this.image.split(",");        //将图片字段拆分
}
 

jt-manage-web后台
jt-manage-web Controller代码:

@Controller
public class ItemWebController {
 
@Autowired
private ItemService itemSerivce;
 
@RequestMapping("/item/{itemId}")
@ResponseBody
public Item queryItem(@PathVariable Long itemId){
return itemSerivce.findItemById(itemId);
}
}
jt-manage-service代码:
@Override
public Item findItemById(Long itemId) {
 
return itemMapper.selectByPrimaryKey(itemId);
}

商品详情引入Redis缓存


所在工程:jt-manage-service

ItemService类代码:

 
@Autowired
private RedisService rs;
 
private static final ObjectMapper MAPPER=new ObjectMapper();
 
@Override
public Item findItemById(Long itemId) {
//引入缓存
String ITEM_KEY="ITEM_"+itemId;
String resultJson=rs.get(ITEM_KEY);
 
try {
if(StringUtils.isNullOrEmpty(resultJson)){
Item item=itemMapper.selectByPrimaryKey(itemId);
rs.set(ITEM_KEY,MAPPER.writeValueAsString(item));
 
return item;
}else{
return MAPPER.readValue(resultJson, Item.class);
}
 
 
} catch (Exception e) {
return itemMapper.selectByPrimaryKey(itemId);
}
 
 
}
 

上一篇 47.大数据之旅——java分布式项目08–Docker(tomcat,Nginx,redis镜像部署)