统计网站页面的访问量

最近做的《食盐行业信用管理与公共服务系统》项目,需要做一个网站文章页面的访问量功能。自己的解决方案,可能很简陋,但是解决了问题,而且我也给出了详细的过程。请大家多多支持,参与谈论。博客写这么长不容易啊。嘿嘿.

需求及规则如下:
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;
    }


}

版权声明:本文为ludongshun2016原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。