最近做的《食盐行业信用管理与公共服务系统》项目,需要做一个网站文章页面的访问量功能。自己的解决方案,可能很简陋,但是解决了问题,而且我也给出了详细的过程。请大家多多支持,参与谈论。博客写这么长不容易啊。嘿嘿.
需求及规则如下:
1.同一个ip地址,两次刷新页面的时间,大于10秒,则认为是2次访问。
2.每天的凌晨0点到1点之间,执行插入或更新数据库操作。因此,网站页面展示的访问量,都是昨天的访问量。
思路及设计说明:
1.过滤器:过滤要计算访问量的某个页面或某些页面,当访问过滤页面时,组装访问量数据。
2.监听器:在项目启动时,启动定时器。
3.定时器:每隔一小时执行一次,如果时间处于0点-1点之间,则把访问量数据插入或更新进数据库,并清空ServletContext里面存放的访问量数据。
4.ServletContext:存放每天的访问量数据。因为ServletContext的生命周期是tomcat启动后,整个项目的生命周期。
5.本项目中采用的是springMVC,因此,附录的PagevisitRemark.java类添加了注解,请大家根据需要,自己写,不一定用我写的。
问题及解决:
1.开发中,遇到了在监听类中,无法调用到service层的方法,试了很多种办法都不行,可能是框架有问题,也可能是别的问题。
鉴于时间关系,没有解决,只能等以后再说了。反正是每天才执行一次插入或更新操作,最后就用了一个笨办法,直接用jdbc连接数据库,写sql语句了。
2.代码里面的MMap类,是我项目中经常用到的一个类。就是个封装数据的类。不用也没关系。
代码中所有的类都贴出来,用不到的自己删除。本文主要是提供一个解决思路和一个详细解决方案(虽然很笨)。
以后如果发现更好的办法。我会再次更新。欢迎积极留言讨论,请关注我的其它博客文章。
目录视图链接地址:http://blog.csdn.net/ludongshun2016?viewmode=contents
表结构
表约束
web.xml添加代码
<!-- 网站页面访问量,测试用,lds...begin -->
<filter>
<filter-name>PageVisitFilter</filter-name>
<filter-class>com.app.archive.web.filter.PageVisitFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>PageVisitFilter</filter-name>
<url-pattern>/website/xiangxi_article.jsp</url-pattern>
</filter-mapping>
<listener>
<listener-class>com.app.archive.util.online.PageVisitListener</listener-class>
</listener>
<!-- 网站页面访问量,测试用,lds...end -->
监听器类(含定时器):PageVisitListener.java
package com.app.archive.util.online;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import com.app.archive.model.salt.MMap;
import com.app.archive.model.salt.PagevisitRemark;
/**
* <p>Title:PageVisitListener </p>
* <p>Description: 监听器,在程序启动时执行定时器,每隔一小时执行一次定时器,如果时间
* 在每天的0点-1点之间,则执行插入或更新数据库操作</p>
* @author 鲁东顺
* @date 2016-10-24 下午1:42:59
*/
public class PageVisitListener implements ServletContextListener{
private ServletContext context;
@Override
public void contextDestroyed(ServletContextEvent arg0) {
}
@Override
public void contextInitialized(ServletContextEvent arg0) {
this.context = arg0.getServletContext();
timerRun();
}
/**
* 定时器,每隔一小时执行一次,如果在0-1点之间,则执行插入数据库操作
* lds
*/
public void timerRun() {
//得到时间类
Calendar date = Calendar.getInstance();
//设置时间为 xx-xx-xx 00:00:00
date.set(date.get(Calendar.YEAR), date.get(Calendar.MONTH), date.get(Calendar.DATE), 0, 0, 0);
//一天的毫秒数
long daySpan = 24 * 60 * 60 * 1000;
//long daySpan = 60*1000*60;//1小时
//long daySpan = 30*1000;//30秒 ..测试时用
//得到定时器实例
Timer t = new Timer();
//使用匿名内方式进行方法覆盖
t.schedule(new TimerTask() {
public void run() {//run中填写定时器主要执行的代码块
Map<String, MMap> ipMap = (Map<String, MMap>) context.getAttribute("ipMap");
Date d=new Date();
int hour=d.getHours();
//每天的凌晨0-1点之间执行
if(hour>=0 && hour < 1){
try {
Thread.sleep(30000);//睡眠30秒,等待项目启动
System.out.println("定时器执行..");
if(null == ipMap){
ipMap = new HashMap<String, MMap>();
}
insertOrUpdate(ipMap);
ipMap = new HashMap<String, MMap>();
context.setAttribute("ipMap", ipMap);
System.out.println("定时器结束..");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}, date.getTime(), daySpan); //daySpan是一天的毫秒数,也是执行间隔
};
/**
* 更新或插入操作
* 鲁东顺
*/
public void insertOrUpdate(Map<String, MMap> ipMap){
List<PagevisitRemark> sprList = new ArrayList<PagevisitRemark>();
List<PagevisitRemark> uprList = new ArrayList<PagevisitRemark>();
List<PagevisitRemark> prList = new ArrayList<PagevisitRemark>();
if(null != ipMap && ipMap.size() > 0){
prList = this.getAll();
for (Object o : ipMap.keySet()) {
MMap mmap = (MMap)ipMap.get(o);
PagevisitRemark pr2 = new PagevisitRemark();
if(null != prList && prList.size() > 0){
for(int i = 0; i < prList.size(); i++){
PagevisitRemark pr = prList.get(i);
if(pr.getIp().equals(mmap.getObj()+"") && (pr.getArticleid()+"").equals(mmap.getObj1()+"")){
pr2 = pr;
pr2.setCount(Integer.parseInt(mmap.getObj2()+"")+pr2.getCount());
break;
}
}
}
if(null == pr2.getId()){
pr2.setArticleid(Integer.parseInt(mmap.getObj1()+""));
pr2.setCount(Integer.parseInt(mmap.getObj2()+""));
pr2.setIp(mmap.getObj()+"");
sprList.add(pr2);
}else{
uprList.add(pr2);
}
}
}
if(null != uprList && uprList.size() > 0){
this.updateAll(uprList);
}
if(null != sprList && sprList.size() > 0){
this.insertAll(sprList);
}
}
/**
* 新增所有
* 鲁东顺
*/
public static void insertAll(List<PagevisitRemark> prList) {
Connection con = null;// 创建一个数据库连接
PreparedStatement pre = null;// 创建预编译语句对象,一般都是用这个而不用Statement
ResultSet result = null;// 创建一个结果集对象
try{
Class.forName("oracle.jdbc.driver.OracleDriver");// 加载Oracle驱动程序
System.out.println("开始尝试连接数据库!");
String url = "jdbc:oracle:" + "thin:@211.88.1.111:1522:orcl";// 127.0.0.1是本机地址,XE是精简版Oracle的默认数据库名
String user = "salt";// 用户名,系统默认的账户名
String password = "salt";// 你安装时选设置的密码
con = DriverManager.getConnection(url, user, password);// 获取连接
System.out.println("连接成功!");
for(int i = 0; i < prList.size(); i++){
String sql = "insert into SALT_PAGEVISIT_REMARK (id,ip,articleid,count) values (SEQ_SALT_PAGEVISIT_REMARK.NEXTVAL,?,?,?)";// 预编译语句,“?”代表参数
pre = con.prepareStatement(sql);// 实例化预编译语句
pre.setString(1, prList.get(i).getIp());// 设置参数,前面的1表示参数的索引,而不是表中列名的索引
pre.setInt(2, prList.get(i).getArticleid());// 设置参数,前面的1表示参数的索引,而不是表中列名的索引
pre.setInt(3, prList.get(i).getCount());// 设置参数,前面的1表示参数的索引,而不是表中列名的索引
result = pre.executeQuery();// 执行查询,注意括号中不需要再加参数
}
System.out.println("新增数据:"+prList.size()+"条。");
}catch (Exception e){
e.printStackTrace();
}finally{
try{
// 逐一将上面的几个对象关闭,因为不关闭的话会影响性能、并且占用资源
// 注意关闭的顺序,最后使用的最先关闭
if (result != null)
result.close();
if (pre != null)
pre.close();
if (con != null)
con.close();
System.out.println("数据库连接已关闭!");
}catch (Exception e){
e.printStackTrace();
}
}
}
/**
* 更新所有
* lds
*/
public static void updateAll(List<PagevisitRemark> prList) {
Connection con = null;// 创建一个数据库连接
PreparedStatement pre = null;// 创建预编译语句对象,一般都是用这个而不用Statement
ResultSet result = null;// 创建一个结果集对象
try{
Class.forName("oracle.jdbc.driver.OracleDriver");// 加载Oracle驱动程序
System.out.println("开始尝试连接数据库!");
String url = "jdbc:oracle:" + "thin:@211.88.1.111:1522:orcl";// 127.0.0.1是本机地址,XE是精简版Oracle的默认数据库名
String user = "salt";// 用户名,系统默认的账户名
String password = "salt";// 你安装时选设置的密码
con = DriverManager.getConnection(url, user, password);// 获取连接
System.out.println("连接成功!");
for(int i = 0; i < prList.size(); i++){
String sql = "update SALT_PAGEVISIT_REMARK set count = ? where id = ?";// 预编译语句,“?”代表参数
pre = con.prepareStatement(sql);// 实例化预编译语句
pre.setInt(1, prList.get(i).getCount());// 设置参数,前面的1表示参数的索引,而不是表中列名的索引
pre.setInt(2, prList.get(i).getId());// 设置参数,前面的1表示参数的索引,而不是表中列名的索引
result = pre.executeQuery();// 执行查询,注意括号中不需要再加参数
}
System.out.println("更新数据:"+prList.size()+"条。");
}catch (Exception e){
e.printStackTrace();
}finally{
try{
// 逐一将上面的几个对象关闭,因为不关闭的话会影响性能、并且占用资源
// 注意关闭的顺序,最后使用的最先关闭
if (result != null)
result.close();
if (pre != null)
pre.close();
if (con != null)
con.close();
System.out.println("数据库连接已关闭!");
}catch (Exception e){
e.printStackTrace();
}
}
}
/**
* 查询所有
* lds
*/
public static List<PagevisitRemark> getAll() {
Connection con = null;// 创建一个数据库连接
PreparedStatement pre = null;// 创建预编译语句对象,一般都是用这个而不用Statement
ResultSet result = null;// 创建一个结果集对象
List<PagevisitRemark> list = new ArrayList<PagevisitRemark>();
try{
Class.forName("oracle.jdbc.driver.OracleDriver");// 加载Oracle驱动程序
System.out.println("开始尝试连接数据库!");
String url = "jdbc:oracle:" + "thin:@211.88.1.111:1522:orcl";// 127.0.0.1是本机地址,XE是精简版Oracle的默认数据库名
String user = "salt";// 用户名,系统默认的账户名
String password = "salt";// 你安装时选设置的密码
con = DriverManager.getConnection(url, user, password);// 获取连接
System.out.println("连接成功!");
String sql = "select id,ip,articleid,count from SALT_PAGEVISIT_REMARK";// 预编译语句,“?”代表参数
pre = con.prepareStatement(sql);// 实例化预编译语句
result = pre.executeQuery();// 执行查询,注意括号中不需要再加参数
while(result.next()){// 当结果集不为空时
PagevisitRemark pr = new PagevisitRemark();
pr.setId(Integer.parseInt(result.getString("id")));//id
pr.setIp(result.getString("ip"));//ip
pr.setArticleid(Integer.parseInt(result.getString("articleid")));//articleid
pr.setCount(Integer.parseInt(result.getString("count")));//数量
list.add(pr);
}
System.out.println("查询出数据:"+list.size()+"条。");
}catch (Exception e){
e.printStackTrace();
}finally{
try{
// 逐一将上面的几个对象关闭,因为不关闭的话会影响性能、并且占用资源
// 注意关闭的顺序,最后使用的最先关闭
if (result != null)
result.close();
if (pre != null)
pre.close();
if (con != null)
con.close();
System.out.println("数据库连接已关闭!");
}catch (Exception e){
e.printStackTrace();
}
}
return list;
}
}
过滤器类PageVisitFilter.java
package com.app.archive.web.filter;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import com.app.archive.model.salt.MMap;
/**
* <p>Title:PageVisitFilter </p>
* <p>Description: 过滤器,用于统计文章页面访问量</p>
* @author 鲁东顺
* @date 2016-10-24 下午1:42:59
*/
public class PageVisitFilter implements Filter{
private FilterConfig filterConfig;
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,FilterChain chain) throws IOException, ServletException {
String ip = request.getRemoteAddr();
HttpServletRequest request1 = (HttpServletRequest) request;
String url = request1.getRequestURI();
if(null != url && !"".equals(url) && url.contains("/")){
int aa = url.lastIndexOf("/");
url = url.substring(aa+1,url.length());
}
String articleid = request1.getParameter("id");
ServletContext context = filterConfig.getServletContext();
Map<String, MMap> ipMap = (Map<String, MMap>) context.getAttribute("ipMap");
if(null == ipMap){
ipMap = new HashMap<String, MMap>();
}
ipMap = getIpMap(ipMap,ip,articleid);
context.setAttribute("ipMap", ipMap);
chain.doFilter(request, response);
}
/**
* 计数操作
* 鲁东顺
*/
private Map<String, MMap> getIpMap(Map<String, MMap> ipMap,String ip, String articleid){
String key = "";
if(null != articleid && !"".equals(articleid)){
key = ip+"_"+articleid;
if(null != ipMap && ipMap.size() > 0 && ipMap.containsKey(key)){//ipMap中包含当前key(登录ip+文章id)
MMap mmap = ipMap.get(key);
long d1 = System.currentTimeMillis()/1000;
long d2 = Long.parseLong(mmap.getObj3()+"");
long d3=d1-d2;
if(d3 > 10){//相隔时间大约10秒,才算有效
System.out.println("两次刷新间隔:"+d3+"秒。");
mmap.setObj2(Integer.parseInt(mmap.getObj2()+"")+1);
}
mmap.setObj3(System.currentTimeMillis()/1000);//访问时间
}else{//ipMap中不包含当前key
MMap mmap = new MMap();
mmap.setObj(ip);//登录ip
mmap.setObj1(articleid);//文章id
mmap.setObj2(1);//访问次数
mmap.setObj3(System.currentTimeMillis()/1000);//访问时间
ipMap.put(ip+"_"+articleid, mmap);
}
return ipMap;
}
return new HashMap<String, MMap>();
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
}
}
**附:统计类PagevisitRemark.java**
package com.app.archive.model.salt;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import javax.persistence.Lob;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import java.util.Date;
/**
* @author SinoCredit
* @since 2016-10-21 13:19:01
* @version 1.0
* Company 北京**管理有限公司
* Descripion 测试用数据库表
*/
@Entity
@Table(name=”SALT_PAGEVISIT_REMARK”)
public class PagevisitRemark{
@Id
@SequenceGenerator(name = "SEQ_SALT_PAGEVISIT_REMARK", sequenceName = "SEQ_SALT_PAGEVISIT_REMARK", allocationSize = 1)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEQ_SALT_PAGEVISIT_REMARK")
/** */
@Column(name="ID")
private Integer id;
/** 访问者ip */
@Column(name="IP")
private String ip;
/** 访问次数 */
@Column(name="COUNT")
private Integer count;
/** 访问文章的id */
@Column(name="ARTICLEID")
private Integer articleid;
/**
* @return
*/
public Integer getId() {
return this.id;
}
/**
* @param _id
*/
public void setId(Integer _id) {
this.id = _id;
}
/**
* @return 访问者ip
*/
public String getIp() {
return this.ip;
}
/**
* @param _ip 访问者ip
*/
public void setIp(String _ip) {
this.ip = _ip;
}
/**
* @return 访问次数
*/
public Integer getCount() {
return this.count;
}
/**
* @param _count 访问次数
*/
public void setCount(Integer _count) {
this.count = _count;
}
/**
* @return 访问文章的id
*/
public Integer getArticleid() {
return this.articleid;
}
/**
* @param _articleid 访问文章的id
*/
public void setArticleid(Integer _articleid) {
this.articleid = _articleid;
}
}
附:MMap.java类(这个类只是我项目中常用的一个类,这里的功能不用非用这个类)
package com.app.archive.model.salt;
import java.util.List;
public class MMap {
private String key;
private Object obj;
private Object obj1;
private Object obj2;
private Object obj3;
private Object obj4;
private Object obj5;
private Object obj6;
private Object obj7;
private Object obj8;
private Object obj9;
private Object obj10;
private List list;
public MMap() {
super();
}
public MMap(String key, Object obj, Object obj1, Object obj2, Object obj3) {
super();
this.key = key;
this.obj = obj;
this.obj1 = obj1;
this.obj2 = obj2;
this.obj3 = obj3;
}
public MMap(String key, Object obj, Object obj1, Object obj2, Object obj3,
Object obj4) {
super();
this.key = key;
this.obj = obj;
this.obj1 = obj1;
this.obj2 = obj2;
this.obj3 = obj3;
this.obj4 = obj4;
}
public MMap(String key, Object obj, Object obj1, Object obj2, Object obj3, Object obj4, Object obj5,Object obj6) {
super();
this.key = key;
this.obj = obj;
this.obj1 = obj1;
this.obj2 = obj2;
this.obj3 = obj3;
this.obj4 = obj4;
this.obj5 = obj5;
this.obj6 = obj6;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public Object getObj() {
return obj;
}
public void setObj(Object obj) {
this.obj = obj;
}
public Object getObj1() {
return obj1;
}
public void setObj1(Object obj1) {
this.obj1 = obj1;
}
public Object getObj2() {
return obj2;
}
public void setObj2(Object obj2) {
this.obj2 = obj2;
}
public Object getObj3() {
return obj3;
}
public void setObj3(Object obj3) {
this.obj3 = obj3;
}
public List getList() {
return list;
}
public void setList(List list) {
this.list = list;
}
public Object getObj4() {
return obj4;
}
public void setObj4(Object obj4) {
this.obj4 = obj4;
}
public Object getObj5() {
return obj5;
}
public void setObj5(Object obj5) {
this.obj5 = obj5;
}
public Object getObj6() {
return obj6;
}
public void setObj6(Object obj6) {
this.obj6 = obj6;
}
public Object getObj7() {
return obj7;
}
public void setObj7(Object obj7) {
this.obj7 = obj7;
}
public Object getObj8() {
return obj8;
}
public void setObj8(Object obj8) {
this.obj8 = obj8;
}
public Object getObj9() {
return obj9;
}
public void setObj9(Object obj9) {
this.obj9 = obj9;
}
public Object getObj10() {
return obj10;
}
public void setObj10(Object obj10) {
this.obj10 = obj10;
}
}