0%

Listener用于监听某些事件的发生,在指定事件发生时,触发相应的方法。 Servlet监听器有三种:ServletContext监听器,HttpSession监听器,ServletRequest监听器。 ServletContext监听接口:ServletContextAttributeListener(监听属性改变),ServletContextListener(监听ServletContext本身的改变) HttpSession监听接口:HttpSessionAttributeListener(监听属性改变),HttpSessionListener(监听HttpSession本身的改变),HttpSessionActivationListener(监听HttpSession对象的状态,激活或钝化),HttpSessionBindingListener(监听对象的绑定状态) ServletRequest监听接口:ServletRequestAttributeListener(监听属性改变),ServletRequestListener(监听对象本身) 下面实现HttpSessionListener接口,写个在线人数统计。原理:因为HttpSessionListener可以监听Session的创建与销毁,所以我们只需要在创建时计数加一,销毁时计数减一就可以。Session会在服务端和客户端都保存,所以,并不是客户端关闭网页,关闭浏览器,服务端上的Session就销毁了,服务端有个超时时间,当超过一定时间,用户没有活动时,session才会过期。基于上面的原因,我们的统计不可能是实时准确的。 还有,如果测试使用Chrome,有点小问题,因为,重启浏览器后,还是同一个Session,就不会触发监听器,所以重启浏览器后,会发现在线人数并没有增加,使用IE和Firefox没有这个问题。 OnlineCounter.java:

package pocketdigi;

import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

public class OnlineCounter implements HttpSessionListener {

    
    private int count;
    public OnlineCounter()
    {
        count=0;
    }

    @Override
    public void sessionCreated(HttpSessionEvent arg0) {
        // TODO Auto-generated method stub
        //session创建时触发,即此时有新用户访问
        count++;
        arg0.getSession().getServletContext().setAttribute("Count", new Integer(count));
        //写入全局属性,arg0.getSession().getServletContext()得到的是javax.servlet.ServletContext对象,即JSP内置对象application
    }

    @Override
    public void sessionDestroyed(HttpSessionEvent arg0) {
        // TODO Auto-generated method stub
        //session过期销毁时触发
        count--;
        arg0.getSession().getServletContext().setAttribute("Count", new Integer(count));
        
    }

}

web.xml注册Listener:

    <listener>
        <listener-class>pocketdigi.OnlineCounter</listener-class>
    </listener>

JSP页面读取:

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<html>
<head>
<title>My JSP '1.jsp' starting page</title>
</head>
    <body>
    <%=(Integer)application.getAttribute("Count")%>
    </body>
</html>

JSP中引入自定义标签,主要是为了把JSP代码与HTML代码分离,两者混在一起,维护成本更高。 自定义标签有三步骤,1、写标签类 2、写定义标签的tld文件 3、JSP中使用 标签类Tablib.java:

package pocketdigi;

import java.io.IOException;

import javax.servlet.jsp.JspContext;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.SimpleTagSupport;

public class Taglib extends SimpleTagSupport {
        private String name;
        private int age;
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public int getAge() {
            return age;
        }
        public void setAge(int age) {
            this.age = age;
        }
        @Override
        public void doTag() throws JspException, IOException {
            // TODO Auto-generated method stub
            //自动调用doTag()方法
            JspContext context=getJspContext();
            JspWriter out=context.getOut();
            out.println("My Name is "+name+",I'm "+age+" years old");
            super.doTag();
        }
        
}

在WEB-INF目录下新建Taglib.tld文件,MyEclipse有模板,很方便:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
                        "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">
<taglib>
    <tlib-version>1.0</tlib-version>
    <jsp-version>2.0</jsp-version>
    <short-name>myTag</short-name>
    <!-- 短名好像没什么用 -->
    <uri>http://www.pocketdigi.com</uri>
    <!-- uri好像唯一就可以 -->
    <tag>
        <name>introduce</name>
        <!-- 标签名,jsp中引用时用 -->
        <tag-class>pocketdigi.Taglib</tag-class>
        <!-- 类名 -->
        <body-content>empty</body-content>
        <!-- 标签体内容,保持empty,暂时不了解其他值的用法 -->
        <attribute>
            <name>name</name>
            <required>true</required>
        </attribute>
        <attribute>
            <name>age</name>
            <required>true</required>
        </attribute>
        <!-- 两个属性,required是指是否必须设置 -->
    </tag>
</taglib>

在JSP中使用:

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib uri="myTagLib" prefix="myTag" %>
<html>
<head>
<title>My JSP '1.jsp' starting page</title>
</head>
    <body>
        <myTag:introduce age="15" name="Jim"/>
    </body>
</html>

执行结果:My Name is Jim,I’m 15 years old

使用empty函数时,报错,提示Can’t use method return value in write context。这是因为empty只能检查变量,不能检查语句。所以解决方法很简单,用一个中间变量储存语句执行的结果,再用empty.

本文接,在前文的基础上添加删除用户功能。 效果:访问http://localhost/cakephp/users/del/ID时,会从users和user\_infos两个表删除指定ID的信息 修改users_controller.php,添加del方法:

    function del()
    {
        $delId=(int)($this->params['pass'][0]);
        if($this->User->delete($delId,true))
        {//官方book里是del,api里是delete,其实在1.3版的cakephp里,应该是delete,没有del方法的,这里被困了好久
        //该方法会同时删除关联的其他表的数据
            $this->flash ( '删除成功', '/users/' );
        }else{
            $this->flash ( '删除失败', '/users/' );
        }
        
    }

修改视图模板index.php,添加删除链接:

<table>
<?php 
$arrayHeader=array_keys(array_merge($allUsers[0]['User'],$allUsers[0]['UserInfo']));
$arrayHeader['del']='删除';
echo $html->tableHeaders($arrayHeader);
//生成表头,参数是一个数组,array_merge方法是把两个数组合并
foreach ($allUsers as $thisuser)
{
    $arrayCell=array_merge($thisuser['User'],$thisuser['UserInfo']);
    $delLink=$html->link("删除","/users/del/".$thisuser['User']['id']);
    $arrayCell['delLink']=$delLink;
    echo $html->tableCells($arrayCell);
    //生成行,参数是数组
}
?>
</table>

两个表,一个是users,三个字段id,user_name,user_password,另一个是user_infos,三个字段,id,email,user_id.其中,user_id与表users中的id关联。 我的目标是在往表users插入数据后,取到新记录的id,作为user_infos的user_id字段内容,插入一行到user_infos表. 项目名为cakephp,访问路径为http://localhost/cakephp/,添加用户的URL为http://localhost/cakephp/users/add,查看用户的URL为http://localhost/cakephp/users/ models/user.php:

<?php
class User extends AppModel {
    var $name = 'User';
    var $uses = array (
            'User',
            'UserInfo' 
    );
    //需要用到的模型
    var $hasOne = array (
            "UserInfo" => array (
                    'className' => 'UserInfo',
                    'conditions' => '',
                    'order' => '',
                    'dependent' => true,
                    'foreignKey' => 'user_id' 
            ) 
    );
    //与表user_infos关联
    var $validate = array (
            'user_name' => array (
                    'rule' => 'notEmpty',
                    'isUnique'=>true
            )
    );
    //验证user_name不为空,且唯一
}
?>

models/user_info.php:

<?php
class UserInfo extends AppModel {
    var $name = 'UserInfo';
    var $validate = array (
            'email' => array (
                    'rule' => array (
                            'email',
                            true 
                    ) 
            ) 
    );
    //<?php
class UserInfo extends AppModel {
    var $name = 'UserInfo';
    var $validate = array (
            'email' => array (
                    'rule' => array (
                            'email',
                            true 
                    ) 
            ) 
    );
    //验证Email格式,好像会联网查域名是否已注册
}
?>
}
?>

controllers/users_controller.php:

<?php
class UsersController extends AppController {
    var $helpers = array (
            'Html',
            'Form' 
    );
    function index() {
        // 读取,访问http://localhost/cakephp/users/时执行
        $allUsers = $this->User->find ( 'all', array (
                'fields' => array (
                        'User.id',
                        'User.user_name',
                        'User.user_password',
                        'UserInfo.email' 
                ),
                'order' => 'id DESC' 
        ) );
        //
        $this->set ( 'allUsers', $allUsers );
    }
    
    function add() {
        // 添加,访问http://localhost/cakephp/users/add时执行
        if (! empty ( $this->data )) {
            // 如果post数据不为空
            if ($this->User->saveAll ( $this->data, array (
                    'validate' => 'first' 
            ) )) {
                // saveAll方法会同时在两个表添加数据,validate=>first 验证所有模型
                
                $this->flash ( '注册成功', '/users/' );
            } else {
                $this->flash ( '注册失败', '/users/add' );
            }
        
        }
    }
}
?>

下面是View,我把模板扩展名改成了php,这样可以高亮显示 views/add.php:

<p>注册帐号.</p>
<?php echo $form->create('User', array('action' => 'add')) ;?>

<?php
    echo $form->input('User.user_name');
    echo $form->input('User.user_password');
    echo $form->input('UserInfo.email');
?>
<?php echo $form->end('注册');?>

views/index.php:

<table>
<?php 
echo $html->tableHeaders(array_keys(array_merge($allUsers[0]['User'],$allUsers[0]['UserInfo'])));
//生成表头,参数是一个数组,array_merge方法是把两个数组合并
foreach ($allUsers as $thisuser)
{
    echo $html->tableCells(array_merge($thisuser['User'],$thisuser['UserInfo']));
    //生成行,参数是数组
}
?>
</table>

前面笔记讲的Servlet,都是继承自javax.servlet.http.HttpServlet,是javax.servlet.GenericServlet的子类,主要用于处理http协议。一般的Servlet只需要继承javax.servlet.GenericServlet.

package pocketdigi;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class Servlet extends GenericServlet {

    @Override
    public void service(ServletRequest req, ServletResponse resp)
            throws ServletException, IOException {
        // TODO Auto-generated method stub
        resp.setCharacterEncoding("UTF-8");
        //默认是iso-8859-1,中文会乱码
        PrintWriter out=resp.getWriter();
        out.println("");
        out.println("");
        out.println("");
        out.println("Hello,Servlet!我");
        out.println("");
        out.close();
    }

}

注册Servlet,在web.xml中添加:

    <servlet>
        <servlet-name>Servlet</servlet-name>
        <servlet-class>pocketdigi.Servlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>Servlet</servlet-name>
        <url-pattern>/Servlet</url-pattern>
    </servlet-mapping>

访问http://localhost:8080/HelloUser/Servlet 即可看到结果(假设项目名为HelloUser). Sevlet的生命周期: 1、init() 初始化 2、service() 处理客户端请求,返回结果 3、destory() 在Servlet容器卸载Servlet前调用,释放资源 初始化可能在以下情况下完成: 1、服务器启动时 2、浏览器第一次请求时 3、根据管理员配置 所以,并不是被用户请求,才会初始化Servlet,Servlet初始化到卸载Servlet期间没有处理任何请求也是可能的。 如果需要Servlet在服务器启动时初始化,可以在Web.xml中,servlet内增加load-on-startup,如

    <servlet>
        <servlet-name>Servlet</servlet-name>
        <servlet-class>pocketdigi.Servlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

load-on-startup的值必须是正整数,加载优先级从小到大。如果两个Servlet数值相同,由服务器自动选择。 卸载Servlet: 由于系统资源不足或其他原因,Servlet容器会卸载Servlet,这个操作由Servlet容器定义和实现,Servlet容器会在该Servlet处理完所有的请求后马上或指定时间后卸载Servlet,当再次被请求时,Servlet容器重新创建一个新的Servlet实例。

之前没学过任何框架,搞这个cakephp把我难住了,几乎花了一天时间,才有点头绪,简直比新学一门语言还要难。cakephp虽然有官方中文文档,但有些部分好像是机器翻译的,也许是我理解能力有点差,反正看不太懂。 这个校验规则官方文档也有,但那网站偶尔抽风,另外也让自己更熟悉一点,所以抄一下。官方文档地址:http://book.cakephp.org/1.3/cn/view/1152/%E6%A0%B8%E5%BF%83%E6%A0%A1%E9%AA%8C%E8%A7%84%E5%88%99 alphaNumeric :数据字段只能包括字母和数字。 between :输入数据的长度必须介于指定的数字范围以内。必须提供最小值与最大值。使用 blank :该规则确定字段为空或者值仅为空白字符。空白字符包括:空格、tab、回车符,以及换行符。 boolean:字符是布尔型,如true,false,0,1,’0’,’1’ cc:该规则用来校验数据是否为一个有效的信用卡号码。有三个参数:‘type’, ‘deep’ and ‘regex’. comparison:Comparison用来比较数字值。它支持“is greater”, “is less”, “greater or equal”, “less or equal”, “is less”, “equal to”, 和 “not equal”。 date:本规则确保数据以有效的日期格式提交。允许传入一个单独的参数(可以为数组)来校验提供日期的格式。 decimal:本规则确保数据为一个有效的小数。可以传入参数指定小数点后的数字位数。如果不传入参数,数据会以科学浮点数进行校验,如果小数点后无数字,则会校验失败。 email:检查数据是否为一个有效的email地址。第二个参数传入布尔值true,规则也会试图校验给定地址的主机地址是否有效。 equalTo:本规则确保数据与给定的值相等,且类型亦相同。 extension:本规则检查文件扩展名是否有效,如.jpg或.png等。允许以数组格式传入多个扩展名。 ip:本规则确保提交的是一个有效的IPv4地址 isUnique:字段值必须唯一,不能用于其它行。 minLength:本规则规定数据的最小长度。 maxLength:本规则规定数据的最大长度。 money:本规则确保值为一个有效的货币值。第二个参数定义货币符号在哪边(左边/右边) Multiple:用来校验多项选择输入。支持”in”, “max”和”min”等参数 inList:本规则确保值在给定的集合中。需要提供一个数组的值。如果值在给定数组中,则该字段有效。 numeric:检查传入的数据是一个有效的数字。 notEmpty:确保字段非空的基本规则。 phone:Phone检验美国的电话号码。如果要校验其它国家的电话号码,需要提供相应的正则表达式作为第二个参数。 postal:Postal用来校验美国(us), 加拿大(ca), 英国(uk), 意大利(it), 德国(de)和比利时(be)的邮政编码格式。对其它国家的邮政编码格式,需要定义相应的正则表达式作为第二个参数。 range:本规则确保值在指定的范围之内。如果未提供范围,规则会检查值是否符合当前平台定义。 ssn:Ssn校验美国(us), 丹麦(dk), 以及荷兰(nl)的社会保险号码。对于其它国家的社会保险号格式,需要提供相应的正则表达式。 url:本规则校验有效的URL格式。支持http(s), ftp(s), file, news以及gopher协议。 使用正则表达式校验规则:

    var $validate = array (
            'login' => array (
                    'rule' => array (
                            'custom',
                            '/^[a-z0-9]{3,}$/i' 
                    ),
                    'message' => 'Only letters and integers, min 3 characters' 
            ) 
    );

单个字段,匹配多个规则:

    var $validate = array (
            'login' => array (
                    'alphanumeric' => array (
                            'rule' => 'alphaNumeric',
                            'message' => 'Only alphabets and numbers allowed',
                            'last' => true 
                    ),
                    'minlength' => array (
                            'rule' => array (
                                    'minLength',
                                    '8' 
                            ),
                            'message' => 'Minimum length of 8 characters' 
                    ) 
            ) 
    );

mysqli可以使用面向对象的方法操作数据库,支持MySQL 4.1 或更高版本所提供的功能(mysql好像不支持). 下面是一个查询数据库的例子:

header("Content-type: text/html; charset=utf-8");
$host="localhost";
$db_user="root";
$db_pwd="";
$db_name="levideo";
$mysqli=new mysqli($host,$db_user,$db_pwd,$db_name);
$mysqli->query("SET NAMES utf8");
//处理编码
$result=$mysqli->query("select * from tv limit 0,30;");
if($result&&$result->num_rows>0)
{
    echo "返回".$result->num_rows."行<br />";
    while ($row=$result->fetch_array())
    {
        echo $row[2].'<br />';	
    }
}
$mysqli->close();
//关闭

$smarty=new Smarty(); $smarty->caching=true; //开启缓存 $smarty->cache_lifetime=3600; //缓存时间3600秒 if(!$smarty->isCached("a.html")) { //未缓存时执行 } $smarty->clearAllCache(); //清除所有缓存 $smarty->clearCache("a.html"); //只清除模板a.html的缓存

Smarty可以开启缓存,下次请求的时候速度就会快很多,但在页面的某些地方,我们可能不需要缓存,比如说显示当前时间,如果用缓存,每次刷新都是第一次请求的时间,不会变。insert功能在于,每次请求时都会重新执行指定的函数,而不会去读取缓存。 模板: {insert name="currentTime"} 上面的代码,会使php在每次请求时执行insert_currentTime()方法,注意,在php中要加前缀insert_ php源代码:

<?php
require_once 'libs/Smarty.class.php';
$smarty=new Smarty();
$smarty->caching=true;
//开启缓存
$smarty->display("a.html");
function insert_currentTime()
{
    return date("Y-m-d H:m:s");
}
?>